home / github / issue_comments

Menu
  • Search all tables
  • GraphQL API

issue_comments: 970673085

This data as json

html_url issue_url id node_id user created_at updated_at author_association body reactions issue performed_via_github_app
https://github.com/simonw/datasette/issues/878#issuecomment-970673085 https://api.github.com/repos/simonw/datasette/issues/878 970673085 IC_kwDOBm6k_c4520u9 9599 2021-11-16T20:58:24Z 2021-11-16T20:58:24Z OWNER

New test: ```python

class Complex(AsyncBase): def init(self): self.log = []

async def d(self):
    await asyncio.sleep(random() * 0.1)
    print("LOG: d")
    self.log.append("d")

async def c(self):
    await asyncio.sleep(random() * 0.1)
    print("LOG: c")
    self.log.append("c")

async def b(self, c, d):
    print("LOG: b")
    self.log.append("b")

async def a(self, b, c):
    print("LOG: a")
    self.log.append("a")

async def go(self, a):
    print("LOG: go")
    self.log.append("go")
    return self.log

@pytest.mark.asyncio async def test_complex(): result = await Complex().go() # 'c' should only be called once assert tuple(result) in ( # c and d could happen in either order ("c", "d", "b", "a", "go"), ("d", "c", "b", "a", "go"), ) And this code passes it:python import asyncio from functools import wraps import inspect

try: import graphlib except ImportError: from . import vendored_graphlib as graphlib

class AsyncMeta(type): def new(cls, name, bases, attrs): # Decorate any items that are 'async def' methods registry = {} new_attrs = {"_registry": _registry} for key, value in attrs.items(): if inspect.iscoroutinefunction(value) and not value.__name__ == "resolve": new_attrs[key] = make_method(value) _registry[key] = new_attrs[key] else: new_attrs[key] = value # Gather graph for later dependency resolution graph = { key: { p for p in inspect.signature(method).parameters.keys() if p != "self" and not p.startswith("") } for key, method in _registry.items() } new_attrs["_graph"] = graph return super().new(cls, name, bases, new_attrs)

def make_method(method): parameters = inspect.signature(method).parameters.keys()

@wraps(method)
async def inner(self, _results=None, **kwargs):
    print("\n{}.{}({}) _results={}".format(self, method.__name__, kwargs, _results))

    # Any parameters not provided by kwargs are resolved from registry
    to_resolve = [p for p in parameters if p not in kwargs and p != "self"]
    missing = [p for p in to_resolve if p not in self._registry]
    assert (
        not missing
    ), "The following DI parameters could not be found in the registry: {}".format(
        missing
    )

    results = {}
    results.update(kwargs)
    if to_resolve:
        resolved_parameters = await self.resolve(to_resolve, _results)
        results.update(resolved_parameters)
    return_value = await method(self, **results)
    if _results is not None:
        _results[method.__name__] = return_value
    return return_value

return inner

class AsyncBase(metaclass=AsyncMeta): async def resolve(self, names, results=None): print("\n resolve: ", names) if results is None: results = {}

    # Come up with an execution plan, just for these nodes
    ts = graphlib.TopologicalSorter()
    to_do = set(names)
    done = set()
    while to_do:
        item = to_do.pop()
        dependencies = self._graph[item]
        ts.add(item, *dependencies)
        done.add(item)
        # Add any not-done dependencies to the queue
        to_do.update({k for k in dependencies if k not in done})

    ts.prepare()
    plan = []
    while ts.is_active():
        node_group = ts.get_ready()
        plan.append(node_group)
        ts.done(*node_group)

    print("plan:", plan)

    results = {}
    for node_group in plan:
        awaitables = [
            self._registry[name](
                self,
                _results=results,
                **{k: v for k, v in results.items() if k in self._graph[name]},
            )
            for name in node_group
        ]
        print("    results = ", results)
        print("    awaitables: ", awaitables)
        awaitable_results = await asyncio.gather(*awaitables)
        results.update(
            {p[0].__name__: p[1] for p in zip(awaitables, awaitable_results)}
        )

    print("  End of resolve(), returning", results)
    return {key: value for key, value in results.items() if key in names}

```

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
648435885  
Powered by Datasette · Queries took 0.876ms · About: github-to-sqlite