def subscribe_fields(exe_context, parent_type, source_value, fields): exe_context = SubscriberExecutionContext(exe_context) def on_error(error): exe_context.report_error(error) def map_result(data): print("MAP RESULT:", data) if exe_context.errors: result = ExecutionResult(data=data, errors=exe_context.errors) else: result = ExecutionResult(data=data) exe_context.reset() return result # def catch_error(error): # print("CATCH ERROR:", error) # exe_context.errors.append(error) # return None assert len(fields) == 1, "Can only subscribe one element at a time." for response_name, field_asts in fields.items(): result = subscribe_field(exe_context, parent_type, source_value, field_asts) if result is Undefined: continue # Map observable results # error_obs = AsyncObservable.unit(None) | op.map(catch_error) obs = result | op.map(lambda x: {response_name: x}) | op.map( map_result) return obs
async def test_forward_pipe_complex_pipe() -> None: xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value) -> int: return value * 10 async def predicate(value) -> bool: await asyncio.sleep(0.1) return value > 1 async def long_running(value) -> AsyncObservable[int]: return AsyncObservable.from_iterable([value]) ys = (xs | _.filter(predicate) | _.map(mapper) | _.flat_map(long_running) | _.to_async_iterable() ) async for value in ys: result.append(value) assert result == [20, 30]
def subscribe_field(exe_context, parent_type, source, field_asts): field_ast = field_asts[0] field_name = field_ast.name.value field_def = get_field_def(exe_context.schema, parent_type, field_name) if not field_def: return Undefined return_type = field_def.type resolve_fn = field_def.resolver or default_resolve_fn # We wrap the resolve_fn from the middleware resolve_fn_middleware = exe_context.get_field_resolver(resolve_fn) # Build a dict of arguments from the field.arguments AST, using the variables scope to # fulfill any variable references. args = exe_context.get_argument_values(field_def, field_ast) # The resolve function's optional third argument is a context value that # is provided to every resolve function within an execution. It is commonly # used to represent an authenticated user, or request-specific caches. context = exe_context.context_value # The resolve function's optional third argument is a collection of # information about the current execution state. info = ResolveInfo(field_name, field_asts, return_type, parent_type, schema=exe_context.schema, fragments=exe_context.fragments, root_value=exe_context.root_value, operation=exe_context.operation, variable_values=exe_context.variable_values, context=context, path=[field_name]) executor = exe_context.executor if isinstance(executor, SyncExecutor): raise TypeError("Cannot use subscriptions with the SyncExecutor") result = resolve_or_error(resolve_fn_middleware, source, info, args, executor) if isinstance(result, Exception): raise result if isinstance(result, Promise): result = executor.loop.run_until_complete(result) if not isinstance(result, AsyncObservable): raise GraphQLError( 'Subscription must return an AsyncObservable. Received: {}'.format( repr(result))) return result | op.map( functools.partial(complete_value_catching_error, exe_context, return_type, field_asts, info))
async def test_pipe_map(): xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value): return value * 10 ys = pipe(xs, _.map(mapper)) async def asend(value): result.append(value) await run(ys, AsyncAnonymousObserver(asend)) assert result == [10, 20, 30]
async def test_forward_pipe_map() -> None: xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value) -> int: return value * 10 ys = xs | _.map(mapper) async def asend(value) -> None: result.append(value) await run(ys, AsyncAnonymousObserver(asend)) assert result == [10, 20, 30]
async def test_map_happy(): xs = from_iterable([1, 2, 3]) # type: AsyncObservable[int] values = [] async def asend(value): values.append(value) def mapper(value: int) -> int: return value * 10 ys = xs | _.map(mapper) result = await run(ys, AsyncAnonymousObserver(asend)) assert result == 30 assert values == [10, 20, 30]
async def test_forward_pipe_simple_pipe() -> None: xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value) -> int: return value * 10 async def predicate(value) -> bool: await asyncio.sleep(0.1) return value > 1 ys = xs | _.filter(predicate) | _.map(mapper) async def asend(value) -> None: result.append(value) await run(ys, AsyncAnonymousObserver(asend)) assert result == [20, 30]
async def test_pipe_simple_pipe(): xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value): return value * 10 async def predicate(value): await asyncio.sleep(0.1) return value > 1 ys = pipe(xs, _.filter(predicate), _.map(mapper)) async def asend(value): result.append(value) await run(ys, AsyncAnonymousObserver(asend)) assert result == [20, 30]
async def test_pipe_complex_pipe(): xs = AsyncObservable.from_iterable([1, 2, 3]) result = [] def mapper(value): return value * 10 async def predicate(value): await asyncio.sleep(0.1) return value > 1 async def long_running(value): return AsyncObservable.from_iterable([value]) ys = pipe(xs, _.filter(predicate), _.map(mapper), _.flat_map(long_running), _.to_async_iterable()) async for value in ys: result.append(value) assert result == [20, 30]
async def test_map_subscription_cancel(): xs = AsyncStream() result = [] sub = None def mapper(value): return value * 10 ys = xs | _.map(mapper) async def asend(value): result.append(value) await sub.adispose() await asyncio.sleep(0) async with subscribe(ys, AsyncAnonymousObserver(asend)) as sub: await xs.asend(10) await asyncio.sleep(0) await xs.asend(20) assert result == [100]
async def test_map_mapper_throws(): xs = from_iterable([1]) exception = None error = Exception("ex") async def asend(value): pass async def athrow(ex): nonlocal exception exception = ex def mapper(x): raise error ys = xs | _.map(mapper) try: await run(ys, AsyncAnonymousObserver(asend, athrow)) except Exception as ex: assert ex == error assert exception == error
async def config_observable(self, cfg): return self.connection.start_with_and_changes(self.connection.db().table(cfg.name))\ | Operators.map(lambda elem: {**elem, "config": cfg.name})\
async def new_task_watch(): db_host = os.environ.get('DATABASE_HOST') db_port = os.environ.get('DATABASE_PORT') conn = await connection(db_host, db_port) async def new_change(arg): try: row, webhooks = arg[0], arg[1] logger.debug(f"Dispatching row: {row}") event = row.get('event', 'undefined') hooks = webhooks.get(event, []) if len(hooks) > 0: webhook_future = asyncio.ensure_future( service.webhook(hooks, row)) try: handler = dispatch[event] await handler(conn, row) except KeyError: logger.debug(f"No handler for event type {row['event']}") except: logger.exception("Unknown exception in task processing") try: if len(hooks) > 0: await webhook_future except: logger.exception("Unknown exception in webhook.") except: logger.exception("Unknown exception in task watch.") async def delayed(tasks): logger.debug(f"Got update in delayed tasks: {tasks}") if len(tasks) == 0: await asyncio.sleep(60) return task = next(iter(tasks)) delta = datetime.fromtimestamp( task['at'], timezone.utc) - datetime.now(timezone.utc) await asyncio.sleep(delta.total_seconds()) await conn.run(new_task(conn, task['event'], task['parameters'])) await asyncio.shield(conn.run(remove_delayed(conn, task))) ready_tasks = conn.start_with_and_changes(conn.db().table('tasks') \ .filter(lambda row: row['status'] == 'ready') ) | Operators.map(lambda c: c['new_val'])\ | Operators.filter(lambda x: x is not None) webhooks = conn.changes_accumulate(conn.db().table('webhooks'))\ | Operators.map(group_by("event")) return await asyncio.gather( subscribe( ready_tasks | with_latest_from(webhooks), AsyncAnonymousObserver(new_change) ), subscribe( conn.changes_accumulate(conn.db().table('delayed_tasks'))\ | Operators.map(lambda tasks: sorted(tasks, key=lambda task: task['at'])), AsyncRepeatedlyCallWithLatest(delayed) ) )