def url_to_resource(self, url): """ Returns the resource that is addressed by the given URL. :param str url: URL to convert :return: member or collection resource :note: If the query string in the URL has multiple values for a query parameter, the last definition in the query string wins. """ parsed = urlparse.urlparse(url) parsed_path = parsed.path # namedtupble problem pylint: disable=E1101 rc = find_resource(self.__request.root, traversal_path(parsed_path)) if ICollectionResource in provided_by(rc): # In case we found a collection, we have to filter, order, slice. parsed_query = parsed.query # namedtuple problem pylint: disable=E1101 params = dict(parse_qsl(parsed_query)) filter_string = params.get('q') if not filter_string is None: rc.filter = \ UrlPartsConverter.make_filter_specification(filter_string) order_string = params.get('sort') if not order_string is None: rc.order = \ UrlPartsConverter.make_order_specification(order_string) start_string = params.get('start') size_string = params.get('size') if not (start_string is None or size_string is None): rc.slice = \ UrlPartsConverter.make_slice_key(start_string, size_string) elif not IMemberResource in provided_by(rc): raise ValueError('Traversal found non-resource object "%s".' % rc) return rc
def resource_to_url(self, resource, **kw): if ICollectionResource in provided_by(resource): query = {} query.update(kw) if not resource.filter is None: query['q'] = \ UrlPartsConverter.make_filter_string(resource.filter) if not resource.order is None: query['sort'] = \ UrlPartsConverter.make_order_string(resource.order) if not resource.slice is None: query['start'], query['size'] = \ UrlPartsConverter.make_slice_strings(resource.slice) if query != {}: url = model_url(resource, self.__request, query=query) else: url = model_url(resource, self.__request) elif not IMemberResource in provided_by(resource): raise ValueError('Can not convert non-resource object "%s to ' 'URL".' % resource) else: if resource.__parent__ is None: raise ValueError('Can not generate URL for floating member ' '"%s".' % resource) url = model_url(resource, self.__request) return unquote(url)
def __init__(self, data_element): if ILinkedDataElement in provided_by(data_element): raise ValueError('Do not use data element proxies with linked ' 'data elements.') attrs = data_element.mapping.attribute_iterator() self.__attr_map = dict([(attr.repr_name, attr) for attr in attrs]) self.__data_element = data_element
def create_mapping(self, mapped_class, configuration=None): """ Creates a new mapping for the given mapped class and representer configuration. :param configuration: configuration for the new data element class. :type configuration: :class:`RepresenterConfiguration` :returns: newly created instance of :class:`Mapping` """ cfg = self.__configuration.copy() if not configuration is None: cfg.update(configuration) provided_ifcs = provided_by(object.__new__(mapped_class)) if IMemberResource in provided_ifcs: base_data_element_class = self.member_data_element_base_class elif ICollectionResource in provided_ifcs: base_data_element_class = self.collection_data_element_base_class elif IResourceLink in provided_ifcs: base_data_element_class = self.linked_data_element_base_class else: raise ValueError('Mapped class for data element class does not ' 'implement one of the required interfaces.') name = "%s%s" % (mapped_class.__name__, base_data_element_class.__name__) de_cls = type(name, (base_data_element_class, ), {}) mp = self.mapping_class(self, mapped_class, de_cls, cfg) # Set the data element class' mapping. # FIXME: This looks like a hack. de_cls.mapping = mp return mp
def get_nested(self, attr): # We only allow *one* child with the given name. q_tag = self.__get_q_tag(attr) child_it = self.iterchildren(q_tag) try: child = child_it.next() except StopIteration: child = None else: try: child_it.next() except StopIteration: pass else: # This should never happen. raise ValueError('More than one child for member ' 'attribute "%s" found.' % attr) # pragma: no cover # Link handling: look for wrapper tag with *one* link child. if child.countchildren() == 1: grand_child = child.getchildren()[0] if ILinkedDataElement in provided_by(grand_child): # We inject the id attribute from the wrapper element. str_xml = child.get('id') if not str_xml is None: grand_child.set('id', str_xml) child = grand_child return child
def add_resource_representer(self, resource, content_type, options=None, attribute_options=None, _info=u''): if IInterface in provided_by(resource): # If we got an interface, we register representers with the same # configuration for the registered member and collection resources. rcs = [ self.get_registered_utility(resource, name='member-class'), self.get_registered_utility(resource, name='collection-class') ] else: if not issubclass(resource, Resource): raise ValueError('Representers can only be registered for ' 'classes inheriting from the Resource base ' 'class.') rcs = [resource] rpr_reg = self.get_registered_utility(IRepresenterRegistry) mp_reg = rpr_reg.get_mapping_registry(content_type) rpr_config = \ mp_reg.configuration_class(options=options, attribute_options=attribute_options) for rc in rcs: rpr_reg.register(rc, content_type, configuration=rpr_config)
def add_resource_view(self, resource, view=None, name='', renderer=None, request_method=('GET', ), default_content_type=None, default_response_content_type=None, **kw): # FIXME: We should not allow **kw to support setting up standard # views here since some options may have undesired side # effects. if isinstance(request_method, basestring): request_method = (request_method, ) if IInterface in provided_by(resource): if not view is None: raise ValueError('Must pass a resource class, not an ' 'interface, when a custom view is ' 'specified.') rcs = [ self._get_utility(resource, 'collection-class'), self._get_utility(resource, 'member-class') ] else: rcs = [resource] for rc in rcs: self.__add_resource_view(rc, view, name, renderer, request_method, default_content_type, default_response_content_type, kw)
def create_mapping(self, mapped_class, configuration=None): """ Creates a new mapping for the given mapped class and representer configuration. :param configuration: configuration for the new data element class. :type configuration: :class:`RepresenterConfiguration` :returns: newly created instance of :class:`Mapping` """ cfg = self.__configuration.copy() if not configuration is None: cfg.update(configuration) provided_ifcs = provided_by(object.__new__(mapped_class)) if IMemberResource in provided_ifcs: base_data_element_class = self.member_data_element_base_class elif ICollectionResource in provided_ifcs: base_data_element_class = self.collection_data_element_base_class elif IResourceLink in provided_ifcs: base_data_element_class = self.linked_data_element_base_class else: raise ValueError('Mapped class for data element class does not ' 'implement one of the required interfaces.') name = "%s%s" % (mapped_class.__name__, base_data_element_class.__name__) de_cls = type(name, (base_data_element_class,), {}) mp = self.mapping_class(self, mapped_class, de_cls, cfg) # Set the data element class' mapping. # FIXME: This looks like a hack. de_cls.mapping = mp return mp
def provides_collection_resource(obj): """ Checks if the given type or instance provides the :class:`everest.resources.interfaces.ICollectionResource` interface. """ if isinstance(obj, type): obj = object.__new__(obj) return ICollectionResource in provided_by(obj)
def resource_to_url(self, resource, quote=False): """ Returns the URL for the given resource. :param resource: Resource to create a URL for. :param bool quote: If set, the URL returned will be quoted. :raises ValueError: If the given resource is floating (i.e., has the parent attribute set to `None`) """ ifc = provided_by(resource) if not IResource in ifc: raise TypeError('Can not generate URL for non-resource "%s".' % resource) elif resource.__parent__ is None: raise ValueError('Can not generate URL for floating resource ' '"%s".' % resource) if ICollectionResource in ifc: query = {} if not resource.filter is None: query['q'] = \ UrlPartsConverter.make_filter_string(resource.filter) if not resource.order is None: query['sort'] = \ UrlPartsConverter.make_order_string(resource.order) if not resource.slice is None: query['start'], query['size'] = \ UrlPartsConverter.make_slice_strings(resource.slice) if query != {}: options = dict(query=query) else: options = dict() if not resource.is_root_collection: # For nested collections, we check if the referenced root # collection is exposed (i.e., has the service as parent). # If yes, we return an absolute URL, else a nested URL. root_coll = get_root_collection(resource) if not root_coll.has_parent: url = self.__request.resource_url(resource) else: url = self.__request.resource_url(root_coll, **options) else: url = self.__request.resource_url(resource, **options) else: if not resource.is_root_member: # For nested members, we check if the referenced root # collection is exposed (i.e., has the service as parent). # If yes, we return an absolute URL, else a nested URL. root_coll = get_root_collection(resource) if not root_coll.has_parent: url = self.__request.resource_url(resource) else: par_url = self.__request.resource_url(root_coll) url = "%s%s/" % (par_url, resource.__name__) else: url = self.__request.resource_url(resource) if not quote: url = url_unquote(url) return url
def resource_to_url(self, resource, quote=False): """ Returns the URL for the given resource. :param resource: Resource to create a URL for. :param bool quote: If set, the URL returned will be quoted. :raises ValueError: If the given resource is floating (i.e., has the parent attribute set to `None`) """ ifc = provided_by(resource) if not IResource in ifc: raise TypeError('Can not generate URL for non-resource "%s".' % resource) elif resource.__parent__ is None: raise ValueError('Can not generate URL for floating resource ' '"%s".' % resource) if ICollectionResource in ifc: query = {} if not resource.filter is None: query['q'] = \ UrlPartsConverter.make_filter_string(resource.filter) if not resource.order is None: query['sort'] = \ UrlPartsConverter.make_order_string(resource.order) if not resource.slice is None: query['start'], query['size'] = \ UrlPartsConverter.make_slice_strings(resource.slice) if query != {}: options = dict(query=query) else: options = dict() if not resource.is_root_collection: # For nested collections, we check if the referenced root # collection is exposed (i.e., has the service as parent). # If yes, we return an absolute URL, else a nested URL. root_coll = get_root_collection(resource) if not root_coll.has_parent: url = self.__request.resource_url(resource, **options) else: url = self.__request.resource_url(root_coll, **options) else: url = self.__request.resource_url(resource, **options) else: if not resource.is_root_member: # For nested members, we check if the referenced root # collection is exposed (i.e., has the service as parent). # If yes, we return an absolute URL, else a nested URL. root_coll = get_root_collection(resource) if not root_coll.has_parent: par_url = self.__request.resource_url(resource) else: par_url = self.__request.resource_url(root_coll) url = "%s%s/" % (par_url, resource.__name__) else: url = self.__request.resource_url(resource) if not quote: url = url_unquote(url) return url
def _dispatch(self, attr_key, attr, node, parent_data, visitor): ifcs = provided_by(node) if IMemberResource in ifcs: self._traverse_member(attr_key, attr, node, parent_data, visitor) elif ICollectionResource in ifcs: self._traverse_collection(attr_key, attr, node, parent_data, visitor) else: raise ValueError('Data must be a resource.')
def __getattr__(self, name): value = self.__data_element.get_attribute(name) ifcs = provided_by(value) if IMemberDataElement in ifcs: value = DataElementAttributeProxy(value) elif ICollectionDataElement in ifcs: value = [DataElementAttributeProxy(mb_el) for mb_el in value.get_members()] return value
def as_repository(resource): """ Adapts the given registered resource to its configured repository. :return: object implementing :class:`everest.repositories.interfaces.IRepository`. """ reg = get_current_registry() if IInterface in provided_by(resource): resource = reg.getUtility(resource, name='collection-class') return reg.getAdapter(resource, IRepository)
def __getattr__(self, name): value = self.__data_element.get_attribute(name) ifcs = provided_by(value) if IMemberDataElement in ifcs: value = DataElementAttributeProxy(value) elif ICollectionDataElement in ifcs: value = [ DataElementAttributeProxy(mb_el) for mb_el in value.get_members() ] return value
def register(cls, value_type, converter_class): if cls.__converters is None: # Lazy initialization. cls.__converters = {} if value_type in cls.__converters: raise ValueError('For %s, a converter has already been ' 'registered (%s).' % (value_type, cls.__converters[value_type])) if not IRepresentationConverter in provided_by(converter_class): raise ValueError('Converter class must provide ' 'IRepresenterConverter.') cls.__converters[value_type] = converter_class
def _dispatch(self, attr_key, attr, node, parent_data, visitor): ifcs = provided_by(node) if IMemberResource in ifcs: self._traverse_member(attr_key, attr, node, parent_data, visitor) elif ICollectionResource in ifcs: self._traverse_collection(attr_key, attr, node, parent_data, visitor) else: raise ValueError('Can only traverse objects that provide' 'IMemberResource or ICollectionResource ' '(key: %s).' % str(attr_key))
def __check_for_link(self, child): # Link handling: look for wrapper tag with *one* link child. if child.countchildren() == 1: grand_child = child.getchildren()[0] if ILinkedDataElement in provided_by(grand_child): # # We inject the id attribute from the wrapper element. # str_xml = child.get('id') # if not str_xml is None: # grand_child.set('id', str_xml) child = grand_child return child
def _data_callback(cls, value, options): # Set the default for the report directory. if options.report_directory is None: options.report_directory = os.path.dirname(value) coll_cls = get_collection_class(cls.registration_resource) rpr = as_representer(object.__new__(coll_cls), JsonMime) reg_items = rpr.from_stream(open(value, 'rU')) # FIXME: This should be treated properly in everest. if IMemberResource in provided_by(reg_items): ents = [reg_items.get_entity()] else: ents = [rc.get_entity() for rc in reg_items] return ents
def __init__(self, attr_type, entity_attr=None, cardinality=None, is_nested=False): """ :param bool is_nested: indicates if the URLs generated for this relation descriptor should be relative to the parent ("nested") or absolute. """ if not (isinstance(attr_type, type) or IInterface in provided_by(attr_type)): raise ValueError('The attribute type of a member or collection ' ' attribute must be a class or an interface.') attribute_base.__init__(self, attr_type, entity_attr, cardinality) self.is_nested = is_nested
def __dump(data_el, stream, offset): name = data_el.__class__.__name__ stream.write("%s%s" % (' ' * offset, name)) offset += 2 ifcs = provided_by(data_el) if ICollectionDataElement in ifcs: stream.write("[") first_member = True for member_data_el in data_el.get_members(): if first_member: stream.write('%s' % os.linesep + ' ' * offset) first_member = False else: stream.write(',%s' % os.linesep + ' ' * offset) __dump(member_data_el, stream, offset) stream.write("]") else: stream.write("(") if ILinkedDataElement in ifcs: stream.write("url=%s, kind=%s, relation=%s" % (data_el.get_url(), data_el.get_kind(), data_el.get_relation())) else: first_attr = True for attr_name, attr_value in iteritems_(data_el.data): if first_attr: first_attr = False else: stream.write(',%s' % os.linesep + ' ' * (offset + len(name) + 1)) if attr_value is None: continue if not IResourceDataElement in provided_by(attr_value): stream.write("%s=%s" % (attr_name, attr_value)) else: stream.write("%s=" % attr_name) __dump(attr_value, stream, offset) stream.write(')')
def register(cls, mime_type): if not [ifc for ifc in provided_by(mime_type) if issubclass(ifc, IMime)]: raise ValueError('MIME type to register must implement the ' 'IMime interface.') if mime_type.mime_type_string in cls.__type_string_map: raise ValueError('Duplicate MIME string detected.') if mime_type.representer_name in cls.__rpr_name_map: raise ValueError('Duplicate MIME name detected.') if mime_type.file_extension in cls.__file_extension_map: raise ValueError('Duplicate file extension detected.') cls.__type_string_map[mime_type.mime_type_string] = mime_type cls.__rpr_name_map[mime_type.representer_name] = mime_type cls.__file_extension_map[mime_type.file_extension] = mime_type
def __init__(self, attr_type, entity_attr=None, cardinality=None, cascade=DEFAULT_CASCADE, backref=None): if not (isinstance(attr_type, type) or IInterface in provided_by(attr_type)): raise ValueError('The attribute type of a member or collection ' ' attribute must be a class or an interface.') if entity_attr is None and backref is None: raise ValueError('Either the entity_attr or the backref parameter ' 'to a relation resource attribute may be None, ' 'but not both.') attribute_base.__init__(self, attr_type, entity_attr) self.cardinality = cardinality self.cascade = cascade self.resource_backref = backref self.__entity_backref = None
def get_member_class(resource): """ Returns the registered member class for the given resource. :param resource: registered resource :type resource: class implementing or instance providing or subclass of a registered resource interface. """ reg = get_current_registry() if IInterface in provided_by(resource): member_class = reg.getUtility(resource, name='member-class') else: member_class = reg.getAdapter(resource, IMemberResource, name='member-class') return member_class
def get_entity_class(resource): """ Returns the entity class registered for the given registered resource. :param resource: registered resource :type collection: class implementing or instance providing a registered resource interface. :return: entity class (class implementing `everest.entities.interfaces.IEntity`) """ reg = get_current_registry() if IInterface in provided_by(resource): ent_cls = reg.getUtility(resource, name='entity-class') else: ent_cls = reg.getAdapter(resource, IEntity, name='entity-class') return ent_cls
def __get__(self, member, member_class): if not member is None: # Create a collection. We can not just return the # entity attribute here as that would load the whole entity # collection; so we construct a related collection instead. parent_coll = member.__parent__ while not getattr(parent_coll, '__parent__', None) is None \ and not ICollectionResource in provided_by(parent_coll): parent_coll = parent_coll.__parent__ # pragma: no cover FIXME: test case! root_coll = parent_coll.get_root_collection(self.attr_type) coll = root_coll.as_related_collection( root_coll.get_aggregate(), self.make_relationship(member)) else: # Class level access. coll = self return coll
def get_collection_class(resource): """ Returns the registered collection resource class for the given marker interface or member resource class or instance. :param rc: registered resource :type rc: class implementing or instance providing or subclass of a registered resource interface. """ reg = get_current_registry() if IInterface in provided_by(resource): coll_class = reg.getUtility(resource, name='collection-class') else: coll_class = reg.getAdapter(resource, ICollectionResource, name='collection-class') return coll_class
def _dispatch(self, attr_key, attr, node, parent_data, visitor): ifcs = provided_by(node) if IMemberDataElement in ifcs: traverse_fn = self._traverse_member elif ICollectionDataElement in ifcs: traverse_fn = self._traverse_collection elif ILinkedDataElement in ifcs: kind = node.get_kind() if kind == RESOURCE_KINDS.MEMBER: traverse_fn = self._traverse_member else: # kind == RESOURCE_KINDS.COLLECTION traverse_fn = self._traverse_collection else: raise ValueError('Need MEMBER or COLLECTION data element; found ' '"%s".' % node) traverse_fn(attr_key, attr, node, parent_data, visitor)
def __getattr__(self, name): try: attr = self.__attr_map[name] except KeyError: raise AttributeError(name) else: if attr.kind == ResourceAttributeKinds.TERMINAL: value = self.__data_element.get_terminal(attr) else: nested_data_el = self.__data_element.get_nested(attr) if nested_data_el is None: value = None elif ILinkedDataElement in provided_by(nested_data_el): value = nested_data_el # links are returned as-is. elif attr.kind == ResourceAttributeKinds.MEMBER: value = DataElementAttributeProxy(nested_data_el) else: value = [DataElementAttributeProxy(mb_el) for mb_el in nested_data_el.get_members()] return value
def add_resource_view(self, resource, view=None, name='', renderer=None, request_method=('GET',), default_content_type=None, default_response_content_type=None, **kw): # FIXME: We should not allow **kw to support setting up standard # views here since some options may have undesired side # effects. if isinstance(request_method, basestring): request_method = (request_method,) if IInterface in provided_by(resource): if not view is None: raise ValueError('Must pass a resource class, not an ' 'interface, when a custom view is ' 'specified.') rcs = [self._get_utility(resource, 'collection-class'), self._get_utility(resource, 'member-class')] else: rcs = [resource] for rc in rcs: self.__add_resource_view(rc, view, name, renderer, request_method, default_content_type, default_response_content_type, kw)
def update_from_data(self, data_element): """ Updates this member from the given data element. :param data_element: data element (hierarchical) to create a resource from :type data_element: object implementing `:class:everest.resources.representers.interfaces.IExplicitDataElement` """ mp = data_element.mapping for attr in mp.attribute_iterator(): if attr.kind == ResourceAttributeKinds.TERMINAL: other_value = data_element.get_terminal(attr) if other_value is None: # Optional attribute - continue. continue else: setattr(self, attr.name, other_value) else: # attr.kind MEMBER or COLLECTION rc_data_el = data_element.get_nested(attr) if rc_data_el is None: # Optional attribute - continue. continue self_rc = getattr(self, attr.name) if ILinkedDataElement in provided_by(rc_data_el): # Found a link. Update if the URL is different. url = rc_data_el.get_url() if not self_rc is None \ and resource_to_url(self_rc) == url: # continue new_rc = url_to_resource(url) setattr(self, attr.name, new_rc) else: if self_rc is None: new_rc = mp.map_to_resource(rc_data_el) setattr(self, attr.name, new_rc) else: self_rc.update_from_data(rc_data_el)
def __call__(self, value, system): context = value.get('context', system.get('context')) if not IResource in provided_by(context): raise ValueError('Context is not a resource.') if not self._validate(context): raise ValueError('Invalid representation.') self._prepare_response(system) # Extract options from parameters. resource = value.get('context', system.get('context')) request = system['request'] params = request.params options = self._extract_from_params(params) # Wrap execution of the _run method. create_exec = WarnAndResubmitExecutor(self._run) result = create_exec(resource, options) if create_exec.do_continue: result = self._handle_success(request, result) else: request.response = result request.response.headers['x-tm'] = 'abort' result = None return result
def add_resource_representer(self, resource, content_type, options=None, attribute_options=None, _info=u''): if IInterface in provided_by(resource): # If we got an interface, we register representers with the same # configuration for the registered member and collection resources. rcs = [self.get_registered_utility(resource, name='member-class'), self.get_registered_utility(resource, name='collection-class')] else: if not issubclass(resource, Resource): raise ValueError('Representers can only be registered for ' 'classes inheriting from the Resource base ' 'class.') rcs = [resource] rpr_reg = self.get_registered_utility(IRepresenterRegistry) mp_reg = rpr_reg.get_mapping_registry(content_type) rpr_config = \ mp_reg.configuration_class(options=options, attribute_options=attribute_options) for rc in rcs: rpr_reg.register(rc, content_type, configuration=rpr_config)
def _prepare_response(self, system): ResourceRenderer._prepare_response(self, system) context = system["context"] if ICollectionResource in provided_by(context): # Disable batching for CSV rendering. context.slice = None
def _validate(self, value): return IResource in provided_by(value)
def _prepare_response(self, system): ResourceRenderer._prepare_response(self, system) context = system['context'] if ICollectionResource in provided_by(context): # Disable batching for CSV rendering. context.slice = None
def _is_link_node(self, node, attr): # pylint: disable=W0613 return ILinkedDataElement in provided_by(node)