示例#1
0
def get_walker(fns: t.List[t.Callable[..., t.Any]]) -> Walker:
    from metashape.runtime import get_walker as _get_walker
    from metashape.analyze.config import Config
    from egoist.internal._fnspec import fnspec

    dq = deque(fns)

    seen: t.Set[t.Type[t.Any]] = set()
    for fn in fns:
        spec = fnspec(fn)
        for typ in spec.argspec.annotations.values():
            if typ in seen:
                continue
            seen.add(typ)
            dq.append(typ)

    seen = set()  # clear
    classes: t.List[t.Type[t.Any]] = []

    while dq:
        typ = dq.pop()
        if typ in seen:
            continue
        seen.add(typ)

        if isinstance(typ, type):
            classes.append(typ)

        for sub_type in _get_flatten_args(typ):
            dq.append(sub_type)

    return _get_walker(classes,
                       config=Config(option=Config.Option(strict=False)))
示例#2
0
def parse(fn: t.Callable[..., t.Any]) -> t.Tuple[str, t.List[str], Metadata]:
    spec = fnspec(fn)

    depends = [
        primitive(name, metadata={"component_type": typ})
        if typ.__module__ == "builtins" else typ.__name__
        for name, typ, _ in spec.arguments
    ]

    return_type = spec.return_type
    if not hasattr(return_type, "__origin__"):
        component_type = return_type
    else:
        assert return_type.__origin__ == tuple, return_type.__origin__
        component_type, *_ = t.get_args(spec.return_type)

    metadata: Metadata = {
        "return_type": return_type,
        "component_type": component_type,
        "fnspec": spec,
    }
    return {
        "name": component_type.__name__,
        "depends": depends,
        "metadata": metadata,
    }
示例#3
0
    def emit(self, emit_fn: t.Callable[..., t.Any]) -> Symbol:
        global m
        spec = fnspec(emit_fn)
        methodname = goname(spec.name)

        # todo: type -> gotype
        args = [m.symbol(name) for name, _, _, in spec.arguments]
        return_type = "string"

        with m.method(
                f"{self.typename} *{self.receiver}",
                methodname,
                *[f"{x} string" for x in args],
                returns=return_type,
        ):
            emit_fn(m.symbol(self.receiver), *args)
        return m.symbol(methodname)
示例#4
0
def emit(w: Walker,
         routes: t.List[t.Tuple[t.Callable[..., t.Any], Metadata]],
         *,
         title: str = "egoist",
         version: str = "0.0.0") -> t.Dict[str, t.Any]:
    from metashape.outputs.openapi.emit import scan

    root = {
        "openapi": "3.0.2",
        "info": {
            "title": title,
            "version": version
        },
        "paths": defaultdict(dict),
        "components": {
            "schemas": {}
        },
    }
    paths = root["paths"]

    # TODO: lazy
    ctx = scan(w)
    r = Resolver(ctx)
    root.update(ctx.result.result)
    h = Handler(root, resolver=r)

    for fn, metadata in routes:
        spec = fnspec(fn)
        path = metadata["path"]
        method = metadata["method"]

        d = paths[path][method] = {}
        d["summary"] = metadata.get("summary") or r.resolve_doc(spec)
        if "description" in metadata:
            d["description"] = metadata["description"]
        if "tags" in metadata:
            d["tags"] = metadata["tags"]
        d.update(r.resolve_request_body(spec))

        responses = d["responses"] = {}
        responses.update(h.handle_successful_response(typ=spec.return_type))
        responses.update(h.handle_validation_error_response())
    return root
示例#5
0
    def _emit(receiver: str) -> Symbol:
        global m

        spec = fnspec(emit_fn)
        methodname = goname(spec.name)
        self = m.symbol(receiver.lstrip("* ")[0].lower())

        # todo: type -> gotype
        args = [m.symbol(name) for name, _, _, in spec.arguments]
        return_type = "string"

        with m.method(
                f"{self} *{receiver}",
                methodname,
                *[f"{x} string" for x in args],
                returns=return_type,
        ):
            emit_fn(self, *args)
        return m.symbol(methodname)
示例#6
0
def parse(fn: t.Callable[..., t.Any]) -> AddNodeParamsDict:
    spec = fnspec(fn)

    depends: t.List[t.Union[str, _Seed]] = []
    levels: t.Dict[str, int] = {}
    for name, typ, _ in spec.arguments:  # parameters?
        if typ.__module__ == "builtins":
            depends.append(primitive(name, metadata={"component_type": typ}))
            continue
        typ, level = _unwrap_pointer_type(typ)
        depends.append(typ.__name__)
        levels[name] = level

    return_type = spec.return_type
    return_level = 0
    if not hasattr(return_type, "__origin__"):
        component_type = return_type
    elif return_type.__origin__ == tuple:
        component_type, *_ = typing_get_args(return_type)
        component_type, return_level = _unwrap_pointer_type(component_type)
    elif return_type.__origin__ == GoPointer:
        component_type, return_level = _unwrap_pointer_type(return_type)
    else:
        import inspect

        raise RuntimeError(
            f"unexpected return-type. {inspect.signature(spec.body)}")
    levels["return"] = return_level

    metadata: Metadata = {
        "return_type": return_type,
        "component_type": component_type,
        "fnspec": spec,
        "levels": levels,
    }

    return {
        "name": component_type.__name__,
        "depends": depends,
        "metadata": metadata,
    }
示例#7
0
    def fnspec(self) -> Fnspec:
        from egoist.internal._fnspec import fnspec

        return fnspec(self.fn)
示例#8
0
import typing as t
import typing_extensions as tx
from egoist.internal._fnspec import fnspec
from dataclasses import dataclass

T = t.TypeVar("T")


class Query(t.Generic[T]):
    @classmethod
    def __emit__(cls, name: str) -> None:
        print("emit", name)


@dataclass
class Name:
    name: str


s = Query[str]
print(s)
print(type(s))
print(vars(tx.Annotated[Query[str], Name("s")]))


def hello(name: tx.Annotated[Query[str], Name("s")]) -> None:
    pass


print(fnspec(hello).arguments[0][1].__metadata__[0].name)
示例#9
0
from egoist.internal._fnspec import fnspec


def hello(name: str):
    pass


print(fnspec(hello))
示例#10
0
import typing as t
import typing_extensions as tx
from egoist.internal._fnspec import fnspec


class Query:
    pass


def hello(name: tx.Annotated[str, Query]):
    pass


print(fnspec(hello))
print(fnspec(hello).arguments[0][1].__metadata__)
print(t.get_args(fnspec(hello).arguments[0][1]))
示例#11
0
def emit(
    api: runtime.API,
    *,
    title: str = "my-api",
    version: str = "0.0.0",
    license: t.Optional[str] = None,  # e.g. "mit"
    servers: t.Optional[t.List[str]] = None,
    default_error_response: t.Optional[runtime.Response] = None,
    autotags: bool = False,
) -> t.Dict[str, t.Any]:
    from metashape.outputs.openapi.emit import scan
    from metashape.marker import mark

    root = {
        "openapi": "3.0.2",
        "info": {
            "title": title,
            "version": version
        },
    }
    if license is not None:
        root["info"]["license"] = {"name": license}
    if servers is not None:
        root["servers"] = [{"url": url} for url in servers]
    root["paths"] = defaultdict(dict)

    # TODO: lazy
    routes = list(api.routes)
    stack = api._stack

    w = get_walker([fn for fn, _ in routes])
    if default_error_response is not None:
        mark(default_error_response.result)  # mark
        w.append(default_error_response.result)

    ctx = scan(w)
    root.update(ctx.result)  # inject #/components/schemas
    refs = ctx.state.refs

    resolver = Resolver(refs=refs)
    tags_map = {}

    default_error_response_dict: t.Dict[str, t.Any] = None
    if default_error_response is not None:
        default_error_response._asdict = lambda x: resolver.resolve_schema(
            default_error_response.result)  # xxx
        default_error_response_dict = resolver.resolve_response(
            default_error_response,
            description=default_error_response.description)
    # default_part = DefaultPartInjector()

    for fn, metadata in routes:
        path, path_type_map = resolver.resolve_path(metadata["path"])
        method = metadata["method"]
        d = root["paths"][path][method] = {}

        spec = fnspec(fn)
        param_names = [name for name, _, _ in spec.parameters] + ["return_"]
        if path_type_map is not None:
            param_names.extend(path_type_map.keys())

        stack.push(param_names)
        c = stack.current

        args = []
        kwargs = {}
        parameter_args = []
        for name, typ, kind in spec.parameters:
            origin = runtime.Body
            first_arg = typ
            if hasattr(typ, "__origin__") and hasattr(typ.__origin__,
                                                      "__emit__"):
                origin = typ.__origin__  # for Query
                first_arg = typ.__args__[0]

            p = origin.__emit__(name, first_arg, d)
            if issubclass(origin, runtime.Body):
                p._asdict = partial(resolver.resolve_request_body, first_arg)
            setattr(c, name, p)

            if kind.startswith("arg"):
                args.append(p)
            else:
                kwargs[name] = p
            parameter_args.append(p)
        spec.body(*args, **kwargs)

        parameters = []
        if path_type_map is not None:
            for name, type_schema in path_type_map.items():
                p = getattr(c, name)
                p.schema = type_schema
                parameters.append(p.asdict())
        if parameter_args:
            for p in parameter_args:
                if p.schema is None:
                    p.schema = resolver.resolve_schema(p._type)
                parameters.append(p.asdict())

        # basic
        d["operationId"] = spec.name
        if "summary" in metadata:
            d["summary"] = metadata["summary"]
        else:
            d["summary"] = resolver.resolve_summary(spec)
        # if "description" in metadata:
        #     d["description"] = metadata["description"]
        # else:
        #     d["description"] = resolver.resolve_description(spec)

        if "tags" in metadata:
            d["tags"] = metadata["tags"]
        elif autotags:
            modname = spec.body.__module__
            tags = tags_map.get(modname)
            if tags is None:
                mod = sys.modules.get(modname)
                tags = tags_map[modname] = getattr(mod, "__TAGS__",
                                                   None) or [modname]
            d["tags"] = tags

        # parameters
        if parameters:
            d["parameters"] = parameters

        # responses
        responses = d["responses"] = {}

        # 200
        default_status = 200
        default_asdict = resolver.resolve_response
        if spec.return_type is not None:
            return_type = spec.return_type
            # for tx.Annotated[T, DefaultStatus(N)]
            if hasattr(return_type, "__metadata__"):
                for m in return_type.__metadata__:
                    if hasattr(m, "code"):  # DefaultStatus
                        default_status = m.code
                return_type = t.get_args(return_type)[0]

        responses[str(default_status)] = default_asdict(
            return_type,
            description=c.return_.description,
            extra_data=c.return_.extra_data,
        )
        if default_error_response_dict is not None:
            responses["default"] = default_error_response_dict

        # # 422
        # default_part.inject_error_part(root)
        # responses["422"] = {
        #     "description": "Validation Error",
        #     "content": {
        #         "application/json": {
        #             "schema": {"$ref": "#/components/schemas/HTTPValidationError"}
        #         }
        #     },
        # }
        stack.pop()

    return root
示例#12
0
def emit(
    routes: t.List[t.Tuple[t.Callable[..., t.Any], Metadata]],
    *,
    title: str = "egoist",
    version: str = "0.0.0"
) -> t.Dict[str, t.Any]:
    from metashape.outputs.openapi.emit import scan

    root = {
        "openapi": "3.0.2",
        "info": {"title": title, "version": version},
        "paths": defaultdict(dict),
    }

    # TODO: lazy
    w = get_walker([fn for fn, _ in routes])
    ctx = scan(w)
    root.update(ctx.result.result)  # inject #/components/schemas
    refs = ctx.state.refs

    resolver = Resolver(refs=refs)
    default_part = DefaultPartInjector()

    for fn, metadata in routes:
        path = metadata["path"]
        method = metadata["method"]
        d = root["paths"][path][method] = {}

        spec = fnspec(fn)

        d["summary"] = metadata.get("summary") or resolver.resolve_doc(spec)
        if "description" in metadata:
            d["description"] = metadata["description"]
        if "tags" in metadata:
            d["tags"] = metadata["tags"]

        if spec.arguments:
            typ = [typ for _, typ, _ in spec.arguments][0]
            d.update(resolver.resolve_request_body(typ))

        # responses
        responses = d["responses"] = {}

        # 200
        value = {}
        if spec.return_type is not None:
            value = resolver.resolve_schema(spec.return_type)
        responses["200"] = {
            "description": "Successful Response",
            "content": {"application/json": value},
        }

        # 422
        default_part.inject_error_part(root)
        responses["422"] = {
            "description": "Validation Error",
            "content": {
                "application/json": {
                    "schema": {"$ref": "#/components/schemas/HTTPValidationError"}
                }
            },
        }

    return root