async def create_feed(ctx: Context, reader: Union[str, type] = None, **reader_args) -> FeedReader: """ Create and start a syndication feed. .. note:: This function does **NOT** add the feed to the context as a resource. :param ctx: a context object (passed to the :meth:`~asphalt.feedreader.api.FeedReader.start` method) :param reader: specifies the feed reader class by one of the following means: * a subclass of :class:`~asphalt.feedreader.api.FeedReader` * the entry point name of one * a ``module:varname`` reference to one * ``None`` to attempt automatic detection of the feed type :param reader_args: keyword arguments passed to the feed reader class :return: a feed reader """ assert check_argument_types() if isinstance(reader, type): feed_class = reader elif reader: feed_class = feed_readers.resolve(reader) else: try: url = reader_args['url'] except KeyError: raise LookupError('no "url" option was specified – it is required for feed reader ' 'autodetection') from None feed_class = None async with aiohttp.request('GET', url) as response: response.raise_for_status() text = await response.text() for cls in feed_readers.all(): logger.info('Attempting autodetection of feed reader class for %s', url) reason = cls.can_parse(text, response.content_type) if reason: logger.info('%s: %s', qualified_name(cls), reason) else: logger.info('Selected reader class %s for %s', qualified_name(cls), url) feed_class = cls break else: raise RuntimeError('unable to detect the feed type for url: ' + url) feed = feed_class(**reader_args) await feed.start(ctx) return feed
async def test_sentry_extras_provider_procedure(self, wampclient: WAMPClient, context: Context, monkeypatch): class DummyReporter(ExceptionReporter): def report_exception(self, ctx: Context, exception: BaseException, message: str, extra: Dict[str, Any]) -> None: errors.append((exception, message, extra)) def handler(ctx): raise Exception('foo') errors = [] context.add_resource(DummyReporter(), types=[ExceptionReporter]) context.add_resource(WAMPExtrasProvider(), types=[ExtrasProvider]) await wampclient.register(handler, 'dummyprocedure') monkeypatch.setattr('asphalt.wamp.extras_providers.SENTRY_CLASS_NAME', qualified_name(DummyReporter)) with pytest.raises(ApplicationError): await wampclient.call('dummyprocedure') assert len(errors) == 1 exc, message, extra = errors[0] assert type(exc) is Exception assert str(exc) == 'foo' assert message == "Error running handler for procedure 'dummyprocedure'" assert extra == {'extra': {'procedure': 'dummyprocedure'}, 'user_context': {'auth_role': 'authorized_users', 'id': 'device1', 'session_id': wampclient.session_id} }
async def test_sentry_extras_provider_subscriber(self, wampclient: WAMPClient, context: Context, monkeypatch): class DummyReporter(ExceptionReporter): def report_exception(self, ctx: Context, exception: BaseException, message: str, extra: Dict[str, Any]) -> None: errors.append((exception, message, extra)) def handler(ctx): ctx.loop.call_soon(event.set) raise Exception('foo') event = asyncio.Event() errors = [] context.add_resource(DummyReporter(), types=[ExceptionReporter]) context.add_resource(WAMPExtrasProvider(), types=[ExtrasProvider]) await wampclient.subscribe(handler, 'dummytopic') monkeypatch.setattr('asphalt.wamp.extras_providers.SENTRY_CLASS_NAME', qualified_name(DummyReporter)) await wampclient.publish('dummytopic', options=dict(acknowledge=True, exclude_me=False)) await event.wait() assert len(errors) == 1 exc, message, extra = errors[0] assert type(exc) is Exception assert str(exc) == 'foo' assert message == "Error running subscription handler for topic 'dummytopic'" assert extra == {'extra': {'topic': 'dummytopic'}, 'user_context': {'auth_role': 'authorized_users', 'id': 'device1', 'session_id': wampclient.session_id} }
async def start(self, ctx: Context): proxymaker = partial(TemplateRendererProxy, renderer=self.renderer) types = [TemplateRenderer, type(self.renderer)] ctx.add_resource_factory(proxymaker, types, self.resource_name) logger.info( "Configured template renderer (%s; class=%s)", self.resource_name, qualified_name(self.renderer), )
def default_encoder(self, obj): obj_type = obj.__class__ try: typename, marshaller, wrap_state = self.serializer.marshallers[obj_type] except KeyError: raise LookupError('no marshaller found for type "{}"' .format(qualified_name(obj_type))) from None state = marshaller(obj) return self.wrap_callback(typename, state) if wrap_state else state
async def start(self, ctx: Context): for resource_name, store in self.stores: await store.start(ctx) ctx.add_resource(store, resource_name) logger.info('Configured feed state store (%s; class=%s)', resource_name, qualified_name(store)) for resource_name, context_attr, config in self.feeds: feed = await create_feed(ctx, **config) ctx.add_resource(feed, resource_name, context_attr, types=[type(feed), FeedReader]) logger.info('Configured feed (%s / ctx.%s; url=%s)', resource_name, context_attr, feed.url)
def cbor_tag_encoder(self, encoder: cbor2.CBOREncoder, obj): try: typename, marshaller, wrap_state = self.serializer.marshallers[obj.__class__] except KeyError: raise LookupError('no marshaller found for type "{}"' .format(qualified_name(type(obj)))) from None marshalled_state = marshaller(obj) if wrap_state: serialized_state = encoder.encode_to_bytes(marshalled_state) wrapped_state = [typename, serialized_state] with encoder.disable_value_sharing(): encoder.encode(cbor2.CBORTag(self.type_tag, wrapped_state)) else: encoder.encode(marshalled_state)
def default_marshaller(obj): """ Retrieve the state of the given object. Calls the ``__getstate__()`` method of the object if available, otherwise returns the ``__dict__`` of the object. :param obj: the object to marshal :return: the marshalled object state """ if hasattr(obj, '__getstate__'): return obj.__getstate__() try: return obj.__dict__ except AttributeError: raise TypeError('{!r} has no __dict__ attribute and does not implement __getstate__()' .format(qualified_name(obj.__class__))) from None
def default_unmarshaller(instance, state) -> None: """ Restore the state of an object. If the ``__setstate__()`` method exists on the instance, it is called with the state object as the argument. Otherwise, the instance's ``__dict__`` is replaced with ``state``. :param instance: an uninitialized instance :param state: the state object, as returned by :func:`default_marshaller` """ if hasattr(instance, '__setstate__'): instance.__setstate__(state) else: try: instance.__dict__.update(state) except AttributeError: raise TypeError('{!r} has no __dict__ attribute and does not implement __setstate__()' .format(qualified_name(instance.__class__))) from None
def default_marshaller(obj): """ Retrieve the state of the given object. Calls the ``__getstate__()`` method of the object if available, otherwise returns the ``__dict__`` of the object. :param obj: the object to marshal :return: the marshalled object state """ if hasattr(obj, '__getstate__'): return obj.__getstate__() try: return obj.__dict__ except AttributeError: raise TypeError( '{!r} has no __dict__ attribute and does not implement __getstate__()' .format(qualified_name(obj.__class__))) from None
def default_unmarshaller(instance, state) -> None: """ Restore the state of an object. If the ``__setstate__()`` method exists on the instance, it is called with the state object as the argument. Otherwise, the instance's ``__dict__`` is replaced with ``state``. :param instance: an uninitialized instance :param state: the state object, as returned by :func:`default_marshaller` """ if hasattr(instance, '__setstate__'): instance.__setstate__(state) else: try: instance.__dict__.update(state) except AttributeError: raise TypeError( '{!r} has no __dict__ attribute and does not implement __setstate__()' .format(qualified_name(instance.__class__))) from None
def get_extras(self, ctx: Context, reporter: ExceptionReporter) -> Optional[Dict[str, Any]]: from asphalt.wamp import CallContext, EventContext extra = None if qualified_name(reporter) == SENTRY_CLASS_NAME: if isinstance(ctx, CallContext): extra = {'extra': {'procedure': ctx.procedure}} if ctx.caller_auth_id: extra['user_context'] = { 'id': ctx.caller_auth_id, 'auth_role': ctx.caller_auth_role, 'session_id': ctx.caller_session_id } elif isinstance(ctx, EventContext): extra = {'extra': {'topic': ctx.topic}} if ctx.publisher_auth_id: extra['user_context'] = { 'id': ctx.publisher_auth_id, 'auth_role': ctx.publisher_auth_role, 'session_id': ctx.publisher_session_id } return extra