示例#1
0
    def get_filters(
        self,
        *,
        query: Optional[QueryType] = None,
        query_schema: SchemaTypeOrStr = "query_schema",
    ) -> Dict[str, Any]:
        """Collect a dictionary of filters from the request query string.
        If :attr:`path_schema` is defined, it collects filter data
        from path variables as well.

        :param query: optional query dictionary (will be overwritten
            by the request.query)
        :param query_schema: a dataclass or an the name of an attribute in Operation
            for collecting query filters
        """
        combined = MultiDict(query or ())
        combined.update(self.request.query)
        try:
            params = self.cleaned(query_schema, combined, multiple=True)
        except web.HTTPNotImplemented:
            params = {}
        if self.path_schema:
            path = self.cleaned("path_schema", self.request.match_info)
            params.update(path)
        return params
示例#2
0
文件: __init__.py 项目: asvetlov/yarl
    def update_query(self, *args, **kwargs):
        """Return a new URL with query part updated."""
        s = self._get_str_query(*args, **kwargs)
        new_query = MultiDict(parse_qsl(s, keep_blank_values=True))
        query = MultiDict(self.query)
        query.update(new_query)

        return URL(self._val._replace(query=self._get_str_query(query)), encoded=True)
示例#3
0
    def update_query(self, *args, **kwargs):
        """Return a new URL with query part updated."""
        s = self._get_str_query(*args, **kwargs)
        new_query = MultiDict(parse_qsl(s, keep_blank_values=True))
        query = MultiDict(self.query)
        query.update(new_query)

        return URL(self._val._replace(query=self._get_str_query(query)), encoded=True)
示例#4
0
 async def statements(self,
                      triple=(None, None, None),
                      context=None,
                      infer=None,
                      **kwargs):
     """
     An async generator over statements and contexts in a RDF4J Triple store.
     This returns the triples in a streaming matter. Which allows direct processing of the incoming data,
     but does not allow sorting or slicing.
     THIS IS AN ASYNC GENERATOR
     :param triple_pattern: an rdflib triple pattern for (S, P, O)
     :param context: the context. If None (default) the default graph is used.
     Which usually means the union of all graphs is iterated.
     Context should be an URI or but can also accept a rdflib.Graph.
     List/set/tuple of URIs/Graphs are also accepted.
     :param infer: Overwrites the default infer parameter.
     :param kwargs: Get merged with the POST kwargs. Overwrites already set kwargs.
     Uses this to add a custom timeout=X (X=0 by default), for example.
     The keyword "headers" is removed from kwargs.
     :return: An async generator of :class Statements ((S,P,O), C)
     """
     #_ = kwargs.pop("params", None)
     _ = kwargs.pop("header", None)
     url = self.rest_services["statements"]
     header = {"ACCEPT": "application/x-binary-rdf"}
     infer = self._infer if infer is None else infer
     assert type(infer) == bool, f"infer ist not a boolean: {infer}"
     s, p, o = triple
     params = MultiDict(dict(infer=str(infer).lower()))
     if s:
         assert isinstance(
             s, (URIRef, BNode)), f"{s}: {type(s)} -> not a valid subject"
         params["subj"] = s.n3()
     if p:
         assert isinstance(
             p, (URIRef, BNode)), f"{p}: {type(p)} -> not a valid predicate"
         params["pred"] = p.n3()
     if o:
         assert isinstance(
             o, (URIRef, BNode,
                 Literal)), f"{o}: {type(o)} -> not a valid object"
         params["obj"] = o.n3()
     if context:
         if isinstance(context, (set, list, tuple)):
             params.update([("context", repair_context(i, as_n3=True))
                            for i in context])
         else:
             params.update([("context", repair_context(context,
                                                       as_n3=True))])
     # params.update([("context", 'null')])
     xkwargs = dict(params=params, timeout=0, headers=header)
     xkwargs.update(kwargs)
     logger.debug(f"GET params are: {xkwargs}")
     resp = await self._session.get(url, **xkwargs)
     async for i in BinaryRDFParser(resp.content):
         yield i
示例#5
0
class View(web.View):
    def __init__(self,
                 request: web.Request,
                 match_dict: T.Optional[T.Mapping[str, str]] = None,
                 *args,
                 **kwargs):
        super().__init__()
        if match_dict is None:
            rel_url = request.rel_url
        else:
            rel_url = self.aiohttp_resource().url_for(**match_dict)
        self.__rel_url = rel_url
        self.__embed = None
        self.__query = None
        self.__canonical_rel_url = None
        self.__match_dict = dict(
            # Ugly: we're using non-public member ``_match()`` of
            # :class:`aiohttp.web.Resource`.  But most alternatives are
            # equally ugly.
            self.aiohttp_resource()._match(self.rel_url.raw_path))

    def __getitem__(self, item):
        # language=rst
        """Shorthand for :meth:`self.match_dict[item] <match_dict>`"""
        return self.__match_dict[item]

    @property
    def match_dict(self):
        # language=rst
        """

        Todo:
            Add documentation, explaining the difference between this method and
            :attr:`self.request.match_info <aiohttp.web.Request.match_info>`.

        """
        return self.__match_dict if self.__match_dict is not None else self.request.match_info

    @match_dict.setter
    def match_dict(self, value):
        self.__match_dict = dict(value)

    @property
    def rel_url(self) -> web.URL:
        # language=rst
        """The relative URL as passed to the constructor."""
        return self.__rel_url

    @property
    def canonical_rel_url(self) -> web.URL:
        # language=rst
        """Like :meth:`rel_url`, but with all default query parameters explicitly listed."""
        if self.__canonical_rel_url is None:
            self.__canonical_rel_url = self.__rel_url.with_query(self.query)
        # noinspection PyTypeChecker
        return self.__canonical_rel_url

    @property
    def query(self):
        # language=rst
        """Like ``self.rel_url.query``, but with default parameters added.

        These default parameters are retrieved from the swagger definition.

        """
        if self.__query is None:
            self.__query = MultiDict(self.default_query_params)
            self.__query.update(self.__rel_url.query)
        return self.__query

    @property
    def default_query_params(self) -> T.Dict[str, str]:
        return {}

    @classmethod
    def add_to_router(cls,
                      router: web.UrlDispatcher,
                      path: str,
                      expect_handler: T.Callable = None):
        # language=rst
        """Adds this View class to the aiohttp router."""
        cls._aiohttp_resource = router.add_resource(path)
        # Register the current class in the appropriate registry:
        # if isinstance(cls._aiohttp_resource, web.DynamicResource):
        #     View.PATTERNS[cls._aiohttp_resource.get_info()['pattern']] = cls
        # elif isinstance(cls._aiohttp_resource, web.PlainResource):
        #     View.PATHS[cls._aiohttp_resource.get_info()['path']] = cls
        # else:
        #     _logger.critical("aiohttp router method 'add_resource()' returned resource object of unexpected type %s", cls._aiohttp_resource.__class__)
        if (not isinstance(cls._aiohttp_resource, web.DynamicResource)
                and not isinstance(cls._aiohttp_resource, web.PlainResource)):
            _logger.critical(
                "aiohttp router method 'add_resource()' returned resource object of unexpected type %s",
                cls._aiohttp_resource.__class__)
        cls._aiohttp_resource.rest_utils_class = cls
        cls._aiohttp_resource.add_route('*',
                                        cls,
                                        expect_handler=expect_handler)
        return cls._aiohttp_resource

    @classmethod
    def aiohttp_resource(
            cls) -> T.Union[web.PlainResource, web.DynamicResource]:
        assert hasattr(cls, '_aiohttp_resource'), \
            f"{cls!s}.aiohttp_resource() called before {cls!s}.add_to_router()"
        try:
            return cls._aiohttp_resource
        except AttributeError:
            raise AssertionError

    async def get(self) -> web.StreamResponse:
        if _GET_IN_PROGRESS in self.request:
            raise web.HTTPInternalServerError()
        self.request[_GET_IN_PROGRESS] = True

        if self.request.method == 'GET':
            data = await self.to_json()
        response = web.StreamResponse()
        if isinstance(await self.etag(), str):
            response.headers.add('ETag', await self.etag())
        response.content_type = self.best_content_type
        response.enable_compression()
        if str(self.canonical_rel_url) != str(self.request.rel_url):
            response.headers.add('Content-Location',
                                 str(self.canonical_rel_url))
        await response.prepare(self.request)
        if self.request.method == 'GET':
            pass  # TODO: implement.
        response.write_eof()
        del self.request[_GET_IN_PROGRESS]
        return response

    async def head(self):
        return await self.get()
示例#6
0
class View(web.View):

    SEGMENT_RE = re.compile(r'([^/{}]+)/?$')
    PATHS = {}
    PATTERNS = {}

    def __init__(self,
                 request: web.Request,
                 match_dict: T.Optional[collections.abc.Mapping] = None,
                 embed=None):
        super().__init__(request)
        if match_dict is None:
            rel_url = request.rel_url
        else:
            rel_url = self.aiohttp_resource().url_for(**match_dict)
        if embed is not None:
            rel_url = rel_url.update_query(embed=embed)
        self.__rel_url = rel_url
        self.__embed = None
        self.__query = None
        self.__canonical_rel_url = None
        self.__etag = None
        self.__match_dict = dict(
            # Ugly: we're using non-public member ``_match()`` of
            # :class:`aiohttp.web.Resource`.  But most alternatives are
            # equally ugly.
            self.aiohttp_resource()._match(self.rel_url.raw_path))

    def __getitem__(self, item):
        # language=rst
        """Shorthand for ``self.match_dict[item]``"""
        return self.__match_dict[item]

    @property
    def match_dict(self):
        return self.__match_dict

    def add_embed_to_url(self, url: web.URL, link_relation):
        embed = self.embed.get(link_relation)
        if embed is None:
            return url
        return url.update_query(embed=embed)

    @property
    def rel_url(self) -> web.URL:
        # language=rst
        """The relative URL as passed to the constructor."""
        return self.__rel_url

    @property
    def canonical_rel_url(self) -> web.URL:
        # language=rst
        """Like :meth:`rel_url`, but with all default query parameters explicitly listed."""
        if self.__canonical_rel_url is None:
            self.__canonical_rel_url = self.__rel_url.with_query(self.query)
        # noinspection PyTypeChecker
        return self.__canonical_rel_url

    @property
    def to_link(self) -> T.Dict[str, str]:
        """The HAL JSON link object to this resource."""
        result = {'href': str(self.canonical_rel_url)}
        if self.link_name is not None:
            result['name'] = self.link_name
        if self.link_title is not None:
            result['title'] = self.link_title
        return result

    async def etag(self) -> T.Union[None, bool, str]:
        # language=rst
        """

        Return values have the following meanings:

        ``True``
            Resource exists but doesn't support ETags
        ``False``
            Resource doesn't exist and doesn't support ETags
        ``None``
            Resource doesn't exist and supports ETags.
        ETag string:
            Resource exists and supports ETags.

        """
        return isinstance(self.aiohttp_resource(), web.PlainResource)

    @property
    def query(self):
        # language=rst
        """Like ``self.rel_url.query``, but with default parameters added.

        These default parameters are retrieved from the swagger definition.

        """
        if self.__query is None:
            self.__query = MultiDict(self.default_query_params)
            self.__query.update(self.__rel_url.query)
        return self.__query

    @property
    def embed(self):
        if self.__embed is None:
            embed = ','.join(self.query.getall('embed', default=[]))
            self.__embed = parse_embed(embed)
        return self.__embed

    @staticmethod
    def construct_resource_for(request: web.Request, rel_url: web.URL) \
            -> T.Optional[web.View]:
        # language=rst
        """

        .. deprecated:: v0

            Only used by :meth:`PlainView._links` and
            :meth:`DynamicView._links`, which are themselves deprecated.

        """
        for resource in request.app.router.resources():
            match_dict = resource._match(rel_url.raw_path)
            if match_dict is not None:
                if hasattr(resource, 'rest_utils_class'):
                    return resource.rest_utils_class(request, rel_url)
                _logger.error(
                    "Path %s doesn't resolve to rest_utils.Resource.",
                    str(rel_url))
                return None
        return None

    @property
    def default_query_params(self) -> T.Dict[str, str]:
        return {}

    @classmethod
    def add_to_router(cls,
                      router: web.UrlDispatcher,
                      path: str,
                      expect_handler: T.Callable = None):
        # language=rst
        """Adds this View class to the aiohttp router."""
        cls._aiohttp_resource = router.add_resource(path)
        # Register the current class in the appropriate registry:
        if isinstance(cls._aiohttp_resource, web.DynamicResource):
            View.PATTERNS[cls._aiohttp_resource.get_info()['pattern']] = cls
        elif isinstance(cls._aiohttp_resource, web.PlainResource):
            View.PATHS[cls._aiohttp_resource.get_info()['path']] = cls
        else:
            _logger.critical(
                "aiohttp router method 'add_resource()' returned resource object of unexpected type %s",
                cls._aiohttp_resource.__class__)
        cls._aiohttp_resource.rest_utils_class = cls
        cls._aiohttp_resource.add_route('*',
                                        cls,
                                        expect_handler=expect_handler)
        return cls._aiohttp_resource

    @classmethod
    def aiohttp_resource(
            cls) -> T.Union[web.PlainResource, web.DynamicResource]:
        assert hasattr(cls, '_aiohttp_resource'), \
            "%s.aiohttp_resource() called before .add_to_router()" % cls
        return cls._aiohttp_resource

    @property
    def link_name(self) -> T.Optional[str]:
        # language=rst
        """A more or less unique name for the resource.

        This default implementation returns the last path segment of the url of
        this resource if that last path segment is templated.  Otherwise `None`
        is returned (in which case there's no `name` attribute in link objects
        for this resource).  See also :meth:`to_link`.

        Subclasses can override this default implementation.

        """
        formatter = self.aiohttp_resource().get_info().get('formatter')
        if formatter is not None and re.search(r'\}[^/]*/?$', formatter):
            return self.rel_url.name or self.rel_url.parent.name
        return None

    @property
    def link_title(self) -> T.Optional[str]:
        # language=rst
        """The title of this resource, to be used in link objects.

        This default implementation returns `None`, and there's no `title`
        attribute in HAL link objects.  See also :meth:`to_link`.

        Subclasses can override this default implementation.

        """
        return None

    async def attributes(self):
        # language=rst
        """

        This default implementation returns *no* attributes, ie. an empty
        `dict`.

        Most subclasses should override this default implementation.

        """
        return {}

    async def _links(self) -> T.Dict[str, T.Any]:
        # language=rst
        """

        Called by :meth:`.links` and :meth:`.embedded`.  See the
        documentation of these methods for more info.

        Most subclasses should override this default implementation.

        :returns: This method must return a dict.  The values must have one of
            the following types:

            -   asynchronous generator of `.View` objects
            -   generator of `.View` objects
            -   a `.View` object
            -   a *link object*
            -   Iterable of `.View`\s and/or *link objects* (may be mixed)

            where *link object* means a HALJSON link object, ie. a `dict` with
            at least a key ``href``.

        """
        return {}

    async def embedded(self) -> T.Dict[str, T.Any]:
        result = {}
        _links = await self._links()
        for key, value in _links.items():
            if key in self.embed:
                if (inspect.isasyncgen(value) or inspect.isgenerator(value)
                        or isinstance(value, View)
                        or isinstance(value, collections.abc.Iterable)):
                    result[key] = value
                else:
                    _logger.error("Don't know how to embed object: %s", value)
        return result

    async def links(self) -> T.Dict[str, T.Any]:
        result = {}
        _links = await self._links()
        for key, value in _links.items():
            if key == 'item':
                key = 'item'
            if isinstance(value, View):
                if key not in self.embed:
                    result[key] = value.to_link
            elif inspect.isasyncgen(value):
                if key not in self.embed:

                    async def g1(resources):
                        async for resource in resources:
                            yield resource.to_link

                    result[key] = g1(value)
            elif inspect.isgenerator(value):
                if key not in self.embed:

                    def g2(resources):
                        for resource in resources:
                            yield resource.to_link

                    result[key] = g2(value)
            elif isinstance(value, collections.Mapping):
                if key in self.embed:
                    _logger.info(
                        'Client asked to embed unembeddable object: %s', value)
                result[key] = value
            elif isinstance(value, collections.abc.Iterable):

                def g3(key, value):
                    for o in value:
                        if not isinstance(o, View):
                            if key in self.embed:
                                _logger.info(
                                    'Client asked to embed unembeddable object: %s',
                                    o)
                            yield o
                        elif key not in self.embed:
                            yield o.to_link

                result[key] = g3(key, value)
            elif key not in self.embed:
                _logger.error("Don't know how to render object as link: %s",
                              value)
        return result

    async def get(self) -> web.StreamResponse:

        # Assert we're not calling `get()` recursively within a single request:
        assert 'GET_IN_PROGRESS' not in self.request
        self.request['GET_IN_PROGRESS'] = True

        etag = await self.etag()
        if not etag:
            raise web.HTTPNotFound()
        assert_preconditions(self.request, etag)
        if self.request.method == 'GET':
            data = await self.to_dict()
        response = web.StreamResponse()
        if isinstance(await self.etag(), str):
            response.headers.add('ETag', await self.etag())
        response.content_type = self.request[BEST_CONTENT_TYPE]
        response.enable_compression()
        if str(self.canonical_rel_url) != str(self.request.rel_url):
            response.headers.add('Content-Location',
                                 str(self.canonical_rel_url))
        await response.prepare(self.request)
        if self.request.method == 'GET':
            async for chunk in _json.json_encode(data):
                response.write(chunk)
        response.write_eof()
        del self.request['GET_IN_PROGRESS']
        return response

    async def head(self):
        return await self.get()

    async def to_dict(self):
        result = await self.attributes()
        if isinstance(await self.etag(), str):
            result['_etag'] = await self.etag()
        result['_links'] = await self.links()
        if 'self' not in result['_links']:
            result['_links']['self'] = self.to_link
        result['_embedded'] = await self.embedded()
        if len(result['_embedded']) == 0:
            del result['_embedded']
        return result
示例#7
0
def as_params(*, params=None, **kwargs):
    params = MultiDict(params if params is not None else {})
    params.update(kwargs)
    return params