def _getObject( self, t, key, request, response, depth ):

		queue = [ ( t, depth, 0 ) ]
		
		while len(queue) > 0:

			t, depth, ignore = queue.pop(0)
			reduce_depth_by = 1

			if ignore == 0:
				if 'get' in t.handlers:
					reduce_depth_by = t.handlers['get'].depth + 1
					t.handlers['get']( self, key, response, request, reduce_depth_by if depth is None else min( depth, reduce_depth_by ) )
				elif schema.is_a( t.schema_type, schema.Container ):
					response[t.schema_type][key] = request[t.schema_type][key]
				elif schema.is_a( t.schema_type, schema.Object ):
					raise Error( 405, 'Object at "%s" does not have a get() method associated' % t.schema_type._url.replace( '%s', '*' ) )
			
				if depth is not None:
					depth = depth - reduce_depth_by
				
				ignore = reduce_depth_by
			
			if t.children and depth is None or depth >= 0:
				if schema.is_a( t.schema_type, schema.Container ):
					queue.append( ( t.children, depth, max( 0, ignore-1 ) ) )
				else:
					for child in t.children.itervalues():
						queue.append( ( child, depth, max( 0, ignore-1 ) ) )
    def _getAffected(self, t, key, request, response):
        queue = [(t, 0)]

        while len(queue) > 0:
            t, ignore = queue.pop(0)
            if ignore == 0:
                ignore = 1
                if schema.is_a(t.schema_type, schema.Object):
                    all_of_type = request[t.schema_type]
                    if key not in all_of_type: continue
                    all_of_type = all_of_type[key]
                    if 'get' not in t.handlers:
                        raise Error(
                            405,
                            'Object at "%s" does not have a get() method associated'
                            % t.schema_type._url.replace('%s', '*'))
                    ignore = t.depth + 1
                    for k in all_of_type.iterkeys():
                        t.handlers['get'](self, k, response, request, None)

            if t.children:
                if schema.is_a(t.schema_type, schema.Container):
                    queue.append((t.children, ignore - 1))
                else:
                    for child in t.children.itervalues():
                        queue.append((child, ignore - 1))
    def _getObject(self, t, key, request, response, depth):

        queue = [(t, depth, 0)]

        while len(queue) > 0:

            t, depth, ignore = queue.pop(0)
            reduce_depth_by = 1

            if ignore == 0:
                if 'get' in t.handlers:
                    reduce_depth_by = t.handlers['get'].depth + 1
                    t.handlers['get'](self, key, response, request,
                                      reduce_depth_by if depth is None else
                                      min(depth, reduce_depth_by))
                elif schema.is_a(t.schema_type, schema.Container):
                    response[t.schema_type][key] = request[t.schema_type][key]
                elif schema.is_a(t.schema_type, schema.Object):
                    raise Error(
                        405,
                        'Object at "%s" does not have a get() method associated'
                        % t.schema_type._url.replace('%s', '*'))

                if depth is not None:
                    depth = depth - reduce_depth_by

                ignore = reduce_depth_by

            if t.children and depth is None or depth >= 0:
                if schema.is_a(t.schema_type, schema.Container):
                    queue.append((t.children, depth, max(0, ignore - 1)))
                else:
                    for child in t.children.itervalues():
                        queue.append((child, depth, max(0, ignore - 1)))
def get(schema_type, depth=None, key_depth=0):
    if depth is None:
        depth = 1 if schema.is_a(schema_type, schema.Container) else 0

    def wrapper(method):
        return _SvcGetMethod(method,
                             schema_type,
                             depth=depth,
                             key_depth=key_depth)

    return wrapper
	def _getAffected( self, t, key, request, response ):
		queue = [ ( t, 0 ) ]
		
		while len(queue) > 0:
			t, ignore = queue.pop(0)
			if ignore==0:
				ignore = 1
				if schema.is_a( t.schema_type, schema.Object ):
					all_of_type = request[t.schema_type]
					if key not in all_of_type:	continue
					all_of_type = all_of_type[key]
					if 'get' not in t.handlers:
						raise Error( 405, 'Object at "%s" does not have a get() method associated' % t.schema_type._url.replace( '%s', '*' ) )
					ignore = t.depth + 1
					for k in all_of_type.iterkeys():
						t.handlers['get']( self, k, response, request, None )

			if t.children:					
				if schema.is_a( t.schema_type, schema.Container ):
					queue.append( ( t.children, ignore-1 ) )
				else:
					for child in t.children.itervalues():
						queue.append( ( child, ignore-1 ) )
    def _modifyObject(self, t, key, request, response, add):

        queue = [(t.children, 0, True, True)] if add else [(t, 0, False,
                                                            False)]

        while len(queue) > 0:

            t, ignore, add, is_item = queue.pop(0)
            all_of_type = request[t.schema_type]
            if key not in all_of_type: continue
            all_of_type = all_of_type.slice(key)

            if ignore == 0:
                ignore = 1
                if add:
                    if 'create' in t.handlers:
                        ignore = t.handlers['create'].depth + 1
                        t.handlers['create'](
                            self, ((key + tuple(
                                (response.replaces[kpart]
                                 if schema._is_fake_key(kpart) else kpart
                                 for kpart in (k[:-1] if is_item else k))), v)
                                   for k, v in all_of_type.iteritems()
                                   if (schema._is_fake_key(k[-1])
                                       if is_item else _is_new_key(k))),
                            response, request)
                    elif is_item or schema.is_a(t.schema_type, schema.Object):
                        raise Error(
                            405,
                            '%s at "%s" does not have a create() method associated'
                            % ('Item' if is_item else 'Object',
                               t.schema_type._url.replace('%s', '*')))
                else:
                    if 'update' in t.handlers:
                        ignore = t.handlers['update'].depth + 1
                        t.handlers['update'](self, (
                            v for k, v in all_of_type.iteritems()
                            if not _is_new_key(k) and not _is_deleted_item(v)),
                                             response, request)
                    elif schema.is_a(t.schema_type, schema.Container):
                        all_of_type = request[t.children.schema_type].slice(
                            key)

                        # Process deleted items immediately
                        all_deleted = [(k, _version_of(v))
                                       for k, v in all_of_type.iteritems()
                                       if _is_deleted_item(v)]
                        if len(all_deleted) > 0:
                            if 'delete' in t.children.handlers:
                                t.children.handlers['delete'](self,
                                                              all_deleted,
                                                              response,
                                                              request)
                            else:
                                raise Error(
                                    405,
                                    'Item at "%s" does not have a delete() method associated'
                                    % t.children.schema_type._url.replace(
                                        '%s', '*'))

                        # Add processing of added items to the queue
                        if not packet._is_empty(
                            (k for k in all_of_type.iterkeys()
                             if _is_new_key(k))):
                            queue.append((t.children, 0, True, True))

                    elif schema.is_a(t.schema_type, schema.Object):
                        raise Error(
                            405,
                            'Object at "%s" does not have an update() method associated'
                            % t.schema_type._url.replace('%s', '*'))

            if t.children:
                if schema.is_a(t.schema_type, schema.Container):
                    queue.append((t.children, ignore - 1, add, add))
                else:
                    for child in t.children.itervalues():
                        queue.append((child, ignore - 1, add, False))
    def __call__(self, verb, url, body=None):

        url = urlparse.urlparse(url)
        url_args = dict(
            ((name, val if len(val) > 1 else val[0])
             for name, val in urlparse.parse_qs(url.query).iteritems()))
        url = url.path[1:] if url.path[:1] == '/' else url.path

        request = packet.Packet(self.schema)

        if body is None:
            body = {}
            body[url] = {}

        try:
            request.deserialize(body, url_args=url_args)

            t = self._url_tree
            key = ()
            is_item = False

            for name in url.split('/'):
                if schema.is_a(t.schema_type, schema.Container):
                    key = key + (schema._key_convert(
                        name, t.schema_type._schema['key']), )
                    t = t.children
                    is_item = True
                else:
                    if t.children is None or name not in t.children:
                        return _wrapError(404,
                                          'No handlers associated with ' + url)
                    t = t.children[name]
                    is_item = False

            is_container = schema.is_a(t.schema_type, schema.Container)
        except:
            return _wrap500(sys.exc_info())

        allow_verbs = set(('GET', 'PUT', 'POST', 'DELETE'))
        if not is_item: allow_verbs.remove('DELETE')
        if not is_container: allow_verbs.remove('POST')
        if verb not in allow_verbs:
            return _wrap405(verb + ' is not supported by ' + url, allow_verbs)

        response = packet.Packet(self.schema)
        need_rollback = False

        try:
            if verb == 'GET':
                depth = url_args.get('depth', None)
                self._getObject(t, key, request, response, depth
                                and int(depth))
            elif verb == 'PUT':
                self.begin(verb, url, request, response)
                need_rollback = True
                self._modifyObject(t, key, request, response, False)
                need_rollback = False
                self.commit(verb, url, request, response)
            elif verb == 'DELETE':
                self.begin(verb, url, request, response)
                need_rollback = True
                self._deleteObject(t, key, request, response)
                need_rollback = False
                self.commit(verb, url, request, response)
            elif verb == 'POST':
                self.begin(verb, url, request, response)
                need_rollback = True
                self._modifyObject(t, key, request, response, True)
                need_rollback = False
                self.commit(verb, url, request, response)
            return _wrap200(response)
        except Error as err:
            if need_rollback: self.rollback(verb, url, request, response)
            if err.status == 405:
                allow_verbs.remove(verb)
                return _wrap405(err.message, allow_verbs)
            elif err.status == 409:
                try:
                    response = packet.Packet(self.schema)
                    if verb == 'DELETE':
                        self._getObject(t, key, request, response, None)
                    else:
                        self._getAffected(t, key, request, response)
                    return _wrap409(err.message, response)
                except:
                    return _wrap500(sys.exc_info())
            else:
                return _wrapError(err.status, err.message)
        except:
            if need_rollback: self.rollback(verb, url, request, response)
            return _wrap500(sys.exc_info())
def get( schema_type, depth=None, key_depth=0 ):
	if depth is None:
		depth = 1 if schema.is_a( schema_type, schema.Container ) else 0
	def wrapper( method ):
		return _SvcGetMethod( method, schema_type, depth = depth, key_depth = key_depth )
	return wrapper
	def _modifyObject( self, t, key, request, response, add ):
		
		queue = [ ( t.children, 0, True, True ) ] if add else [ ( t, 0, False, False ) ]
	
		while len(queue) > 0:
			
			t, ignore, add, is_item = queue.pop(0)
			all_of_type = request[t.schema_type]
			if key not in all_of_type:	continue
			all_of_type = all_of_type.slice( key )
			
			if ignore == 0:
				ignore = 1
				if add:
					if 'create' in t.handlers:
						ignore = t.handlers['create'].depth + 1
						t.handlers['create']( self, (
							(
								key + tuple( (
									response.replaces[kpart] if schema._is_fake_key( kpart ) else kpart
									for kpart in ( k[:-1] if is_item else k )
								) ),
								v
							)
							for k,v in all_of_type.iteritems()
							if ( schema._is_fake_key( k[-1] ) if is_item else _is_new_key( k ) )
						), response, request )
					elif is_item or schema.is_a( t.schema_type, schema.Object ):
						raise Error( 405, '%s at "%s" does not have a create() method associated'  % (
							'Item' if is_item else 'Object',
							t.schema_type._url.replace( '%s', '*' )
						) )
				else:
					if 'update' in t.handlers:
						ignore = t.handlers['update'].depth + 1
						t.handlers['update']( self, (
							v for k,v in all_of_type.iteritems() if not _is_new_key( k ) and not _is_deleted_item( v )
						), response, request )
					elif schema.is_a( t.schema_type, schema.Container ):
						all_of_type = request[t.children.schema_type].slice( key )
						
						# Process deleted items immediately
						all_deleted = [ ( k, _version_of( v ) ) for k,v in all_of_type.iteritems() if _is_deleted_item( v ) ]
						if len( all_deleted ) > 0:
							if 'delete' in t.children.handlers:
								t.children.handlers['delete']( self, all_deleted, response, request )
							else:
								raise Error( 405, 'Item at "%s" does not have a delete() method associated'  % t.children.schema_type._url.replace( '%s', '*' ) )
								
						# Add processing of added items to the queue
						if not packet._is_empty( ( k for k in all_of_type.iterkeys() if _is_new_key( k ) ) ):
							queue.append( ( t.children, 0, True, True ) )
							
					elif schema.is_a( t.schema_type, schema.Object ):
						raise Error( 405, 'Object at "%s" does not have an update() method associated'  % t.schema_type._url.replace( '%s', '*' ) )

			if t.children:					
				if schema.is_a( t.schema_type, schema.Container ):
					queue.append( ( t.children, ignore-1, add, add ) )
				else:
					for child in t.children.itervalues():
						queue.append( ( child, ignore-1, add, False ) )
	def __call__( self, verb, url, body = None ):

		url = urlparse.urlparse( url )
		url_args = dict((
				( name, val if len(val)>1 else val[0] )
				for name,val in urlparse.parse_qs( url.query ).iteritems()
			))
		url = url.path[1:] if url.path[:1]=='/' else url.path
		
		request = packet.Packet( self.schema )
		
		if body is None:
			body = {}
			body[ url ] = {}

		try:
			request.deserialize( body, url_args = url_args )
		
			t = self._url_tree
			key = ()
			is_item = False
		
			for name in url.split( '/' ):
				if schema.is_a( t.schema_type, schema.Container ):
					key = key + ( schema._key_convert( name, t.schema_type._schema['key'] ), )
					t = t.children
					is_item = True
				else:
					if t.children is None or name not in t.children:
						return _wrapError( 404, 'No handlers associated with ' + url )
					t = t.children[name]
					is_item = False
			
			is_container = schema.is_a( t.schema_type, schema.Container )
		except:
			return _wrap500( sys.exc_info() )
		
		allow_verbs = set(( 'GET', 'PUT', 'POST', 'DELETE' ))
		if not is_item:			allow_verbs.remove( 'DELETE' )
		if not is_container: 	allow_verbs.remove( 'POST' )
		if verb not in allow_verbs:
			return _wrap405( verb + ' is not supported by ' + url, allow_verbs )
		
		response = packet.Packet( self.schema )
		need_rollback = False
		
		try:
			if verb=='GET':
				depth = url_args.get( 'depth', None )
				self._getObject( t, key, request, response, depth and int(depth) )
			elif verb=='PUT':
				self.begin( verb, url, request, response )
				need_rollback = True
				self._modifyObject( t, key, request, response, False )
				need_rollback = False
				self.commit( verb, url, request, response )
			elif verb=='DELETE':
				self.begin( verb, url, request, response )
				need_rollback = True
				self._deleteObject( t, key, request, response )
				need_rollback = False
				self.commit( verb, url, request, response )
			elif verb=='POST':
				self.begin( verb, url, request, response )
				need_rollback = True
				self._modifyObject( t, key, request, response, True )
				need_rollback = False
				self.commit( verb, url, request, response )
			return _wrap200( response )
		except Error as err:
			if need_rollback:	self.rollback( verb, url, request, response )
			if err.status == 405:
				allow_verbs.remove( verb )
				return _wrap405( err.message, allow_verbs )
			elif err.status == 409:
				try:
					response = packet.Packet( self.schema )
					if verb=='DELETE':	self._getObject( t, key, request, response, None )
					else:				self._getAffected( t, key, request, response )
					return _wrap409( err.message, response )
				except:
					return _wrap500( sys.exc_info() )
			else:
				return _wrapError( err.status, err.message )
		except:
			if need_rollback:	self.rollback( verb, url, request, response )
			return _wrap500( sys.exc_info() )