def collect(cls: _Value, *, w: Walker) -> _Value: props = {} for name, typeinfo, metadata in w.walk_fields(cls): fieldname = w.resolver.metadata.resolve_name(metadata, default=name) value = getattr(cls, name, None) if value is None: value = typeinfo.raw props[fieldname] = collect(value, w=w) return props
def scan(walker: Walker, ) -> Context: ctx = Context(walker) builder = Builder(ctx, walker=walker) try: for cls in walker.walk(): schema = builder.build_schema_data(cls) ctx.register_schema(cls, schema) finally: ctx.config.callbacks.teardown() # xxx: return ctx
def walk( w: MetashapeWalker, *, metadata_handler: metadata_.MetadataHandlerFunction, _nonetype: t.Type[t.Any] = type(None), kinds: t.Optional[t.List[t.Optional[NodeKind]]] = None, ) -> t.Iterator[Item]: kinds = kinds or ["object", None, "enum"] for cls in w.walk(kinds=kinds): origin = getattr(cls, "__origin__", None) if origin is not None: args = list(typing_get_args(cls)) if origin == t.Union and _nonetype not in args: # union yield Item(name=guess_name(cls), type_=cls, fields=[], args=args, origin=origin) # fixme for subtype in _get_flatten_args(cls): w.append(subtype) continue elif origin == tx.Literal: # literal yield Item( name=resolve_name(cls), type_=cls, fields=[], args=args, origin=origin, ) # fixme name continue else: raise RuntimeError("unexpected type {cls!r}") fields: t.List[Row] = [] for name, info, _metadata in w.for_type(cls).walk( ignore_private=False): if name.startswith("_") and name.endswith("_"): continue filled_metadata: metadata_.Metadata = metadata_.metadata() filled_metadata.update(_metadata) # type:ignore if filled_metadata.get("default") == MISSING: filled_metadata.pop("default") if info.is_optional: filled_metadata["required"] = False # handling tags metadata_handler(cls, name=name, info=info, metadata=filled_metadata) fields.append((name, info, filled_metadata)) # append to walker, if needed for subtype in _get_flatten_args(info.type_): w.append(subtype) yield Item(name=resolve_name(cls), type_=cls, fields=fields, args=[])
def emit_class(m: Module, item: Item, *, w: Walker, resolver: TypeResolver) -> Symbol: name = item.name cls = item.type_ i = 1 m.stmt(f"message {name} {{") with m.scope(): for name, info, _metadata in w.for_type(cls).walk( ignore_private=False): typ = resolver.resolve_type(info.type_) m.stmt(f"{typ} {name} = {i};") # todo: deprecated i += 1 m.stmt("}") return m.symbol(name)
def scan(walker: Walker, *, definitions: t.Optional[str] = None) -> Context: ctx = Context(walker) scanner = Scanner(ctx) try: for i, cls in enumerate(walker.walk()): scanner.scan(cls) if i == 0 and definitions is None: scanner.ctx.result.result[ "$ref" ] = f"#/definitions/{walker.resolver.resolve_typename(cls)}" finally: ctx.config.callbacks.teardown() # xxx: return ctx
def _mark_recursive( w: Walker, members: t.List[Member], *, seen: t.Set[t.Type[t.Any]], guess_member: GuessMemberFunc, ) -> t.Iterable[t.Type[t.Any]]: from collections import deque q: t.Deque[t.Type[t.Any]] = deque() for m in members: q.append(m) while True: try: m = q.popleft() except IndexError: break if m in seen: continue seen.add(m) yield m kind = guess_member(m) if kind != "object": logger.debug("skip recursive walk, kind=%r, type=%r", kind, m) assert kind is not None mark(m, kind=kind) continue for _, info, _ in w.walk_fields(m): if info.type_ in seen: continue for x in info.args or [info]: if x.type_ in seen: continue kind = guess_member(x.type_) if kind is None: continue mark(x.type_, kind=kind) yield x.type_ q.append(x.type_)
def scan(walker: Walker) -> Context: ctx = Context(walker) resolver = ctx.walker.resolver result = ctx.result scanned = _scan(walker) for enum in scanned.enums: result.enums[enum.__name__] = enum for cls in scanned.objects: schema = make_dict() typename = resolver.resolve_typename(cls) for field_name, info, metadata in walker.walk_fields(cls): field_name = resolver.metadata.resolve_name(metadata, default=field_name) prop = {"type": (scanned.get_name(info.type_) or detect.schema_type(info))} resolver.metadata.fill_extra_metadata(prop, metadata, name="graphql") schema[field_name] = prop result.types[typename] = schema return ctx
def emit(walker: Walker, *, output: t.Optional[t.IO[str]] = None) -> None: output = output or walker.config.option.output for m in walker.walk(ignore_private=walker.config.option.ignore_private): print(guess_mark(m), m, file=output)
def get_walker( target: t.Union[ None, types.ModuleType, t.Type[t.Any], t.List[t.Type[t.Any]], t.Dict[str, t.Type[t.Any]], ] = None, *, config: t.Optional[Config] = None, aggressive: bool = False, recursive: bool = False, sort: bool = False, only: t.Optional[t.List[str]] = None, unwrap_type: t.Callable[[t.Type[t.Any]], t.Type[t.Any]] = _unwrap_type, _depth: int = 1, # xxx: for black magic _extra_target_name: str = "__ADDITIONAL_TARGETS__", _seen_modules: t.Optional[t.Set[types.ModuleType]] = None, _guess_kind: t.Callable[[t.Any], t.Optional[Kind]] = _guess_kind, ) -> Walker: if _seen_modules is None: _seen_modules = set() config = config or Config() if target is None: if aggressive: logger.info( "aggressive=True and target=None, guessing target module... this is unsafe action" ) # xxx: extract caller module (black magic) frame = sys._getframe(_depth) here = frame.f_globals["__name__"] try: target = sys.modules[here] except KeyError: raise ValueError("supported only module name") if target is None: raise ValueError("support target=None, only aggresive=True") elif isinstance(target, types.ModuleType): _seen_modules.add(target) d = target.__dict__ if aggressive and only is None: only = [get_name(target)] elif isinstance(target, dict): d = target for x in d.values(): mark(x, kind=_guess_kind(x) or "object") elif isinstance(target, (list, tuple)): d = {get_name(x): x for x in target} for x in target: mark(x, kind=_guess_kind(x) or "object") else: d = {get_name(target): target} mark(target, kind=_guess_kind(target) or "object") if only is not None: d = { k: v for k, v in d.items() if getattr(v, "__module__", "") in only or hasattr(v, "__origin__") } # xxx: for supporting enum, see __origin__ if aggressive: for name, v in list(d.items()): kind = _guess_kind(v) if kind is not None: if kind == "enum": v.__name__ = name # xxx TODO: use tx.Annotated mark(v, kind=kind) recursive = recursive or config.option.recursive sort = sort or config.option.sort itr = sorted(d.items()) if sort else d.items() members = [unwrap_type(v) for _, v in itr if is_marked(v)] named = {id(val): name for name, val in d.items()} resolver = Resolver(config=config, named=named) w = Walker(members, resolver=resolver, config=config) if recursive: if aggressive: guess_member = _guess_kind else: guess_member = guess_mark w._members = list( _mark_recursive(w, w._members, seen=set(), guess_member=guess_member) ) # xxx: if isinstance(target, types.ModuleType): for x in getattr(target, _extra_target_name, None) or []: if isinstance(x, types.ModuleType): _seen_modules.add(x) sw = get_walker( x, config=config, aggressive=aggressive, recursive=recursive, sort=sort, only=None, _depth=_depth + 1, _extra_target_name=_extra_target_name, _seen_modules=_seen_modules, _guess_kind=_guess_kind, ) w._members.extend(sw._members) else: mark(x, kind=_guess_kind(x) or "object") w._members.append(x) return w