def test_to_docstring_fails(self) -> None: """ Tests docstring failure conditions """ self.assertRaises( NotImplementedError, lambda: to_docstring(deepcopy(intermediate_repr_no_default_doc), docstring_format="numpydoc"), )
def json_schema(intermediate_repr): """ Construct a JSON schema dict :param intermediate_repr: a dictionary of form { "name": Optional[str], "type": Optional[str], "doc": Optional[str], "params": OrderedDict[str, {'typ': str, 'doc': Optional[str], 'default': Any}] "returns": Optional[OrderedDict[Literal['return_type'], {'typ': str, 'doc': Optional[str], 'default': Any}),)]] } :type intermediate_repr: ```dict``` """ required = [] _param2json_schema_property = partial(param2json_schema_property, required=required) properties = dict( map(_param2json_schema_property, intermediate_repr["params"].items()) ) return { "$id": "https://offscale.io/json.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": deindent( to_docstring( { "doc": intermediate_repr["doc"].lstrip() + "\n\n" if intermediate_repr["returns"] else "", "params": OrderedDict(), "returns": intermediate_repr["returns"], }, emit_default_doc=True, emit_types=True, ).strip() ), "type": "object", "properties": properties, "required": required, }
def function( intermediate_repr, function_name, function_type, emit_default_doc=False, docstring_format="rest", indent_level=2, emit_separating_tab=PY3_8, inline_types=True, emit_as_kwonlyargs=True, ): """ Construct a function from our IR :param intermediate_repr: a dictionary of form { "name": Optional[str], "type": Optional[str], "doc": Optional[str], "params": OrderedDict[str, {'typ': str, 'doc': Optional[str], 'default': Any}] "returns": Optional[OrderedDict[Literal['return_type'], {'typ': str, 'doc': Optional[str], 'default': Any}),)]] } :type intermediate_repr: ```dict``` :param function_name: name of function_def :type function_name: ```Optional[str]``` :param function_type: Type of function, static is static or global method, others just become first arg :type function_type: ```Optional[Literal['self', 'cls', 'static']]``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpy', 'google']``` :param emit_default_doc: Whether help/docstring should include 'With default' text :type emit_default_doc: ```bool``` :param indent_level: docstring indentation level whence: 0=no_tabs, 1=one tab; 2=two tabs :type indent_level: ```int``` :param emit_separating_tab: docstring decider for whether to put a tab between :param and return and desc :type emit_separating_tab: ```bool``` :param inline_types: Whether the type should be inline or in docstring :type inline_types: ```bool``` :param emit_as_kwonlyargs: Whether argument(s) emitted must be keyword only :type emit_as_kwonlyargs: ```bool``` :return: AST node for function definition :rtype: ```FunctionDef``` """ params_no_kwargs = tuple( filter( lambda param: not param[0].endswith("kwargs"), intermediate_repr["params"].items(), )) function_name = function_name or intermediate_repr["name"] function_type = function_type or intermediate_repr["type"] args = ([] if function_type in frozenset( (None, "static")) else [set_arg(function_type)]) from doctrans.emitter_utils import ast_parse_fix args_from_params = list( map( lambda param: set_arg( annotation=(Name(param[1]["typ"], Load()) if param[1]["typ"] in simple_types else ast_parse_fix(param[1]["typ"])) if inline_types and "typ" in param[1] else None, arg=param[0], ), params_no_kwargs, ), ) defaults_from_params = list( map( lambda param: set_value(None) if param[1].get("default") in (None, "None", NoneStr) else set_value(param[1].get("default")), params_no_kwargs, )) if emit_as_kwonlyargs: kwonlyargs, kw_defaults, defaults = args_from_params, defaults_from_params, [] else: kwonlyargs, kw_defaults, defaults = [], [], defaults_from_params args += args_from_params internal_body = get_internal_body( target_name=function_name, target_type=function_type, intermediate_repr=intermediate_repr, ) return_val = (Return( value=ast.parse(intermediate_repr["returns"]["return_type"] ["default"].strip("`")).body[0].value, expr=None, ) if (intermediate_repr.get("returns") or { "return_type": {} })["return_type"].get("default") else None) return FunctionDef( args=arguments( args=args, defaults=defaults, kw_defaults=kw_defaults, kwarg=next( map( lambda param: set_arg(param[0]), filter( lambda param: param[0].endswith("kwargs"), intermediate_repr["params"].items(), ), ), None, ), kwonlyargs=kwonlyargs, posonlyargs=[], vararg=None, arg=None, ), body=list( filter( None, ( Expr( set_value( to_docstring( intermediate_repr, emit_default_doc=emit_default_doc, docstring_format=docstring_format, emit_types=not inline_types, indent_level=indent_level, emit_separating_tab=emit_separating_tab, ))), *(internal_body[:-1] if internal_body and isinstance(internal_body[-1], Return) and return_val else internal_body), return_val, ), )), decorator_list=[], name=function_name, returns=(ast.parse( intermediate_repr["returns"]["return_type"]["typ"]).body[0].value if inline_types and (intermediate_repr.get("returns") or { "return_type": {} })["return_type"].get("typ") else None), lineno=None, arguments_args=None, identifier_name=None, stmt=None, **maybe_type_comment)
def class_( intermediate_repr, emit_call=False, class_name="ConfigClass", class_bases=("object", ), decorator_list=None, emit_default_doc=False, ): """ Construct a class :param intermediate_repr: a dictionary of form { "name": Optional[str], "type": Optional[str], "doc": Optional[str], "params": OrderedDict[str, {'typ': str, 'doc': Optional[str], 'default': Any}] "returns": Optional[OrderedDict[Literal['return_type'], {'typ': str, 'doc': Optional[str], 'default': Any}),)]] } :type intermediate_repr: ```dict``` :param emit_call: Whether to emit a `__call__` method from the `_internal` IR subdict :type emit_call: ```bool``` :param class_name: name of class :type class_name: ```str``` :param class_bases: bases of class (the generated class will inherit these) :type class_bases: ```Iterable[str]``` :param decorator_list: List of decorators :type decorator_list: ```Optional[Union[List[Str], List[]]]``` :param emit_default_doc: Whether help/docstring should include 'With default' text :type emit_default_doc: ```bool``` :return: Class AST of the docstring :rtype: ```ClassDef``` """ returns = (intermediate_repr["returns"] if "return_type" in intermediate_repr.get("returns", {}) else OrderedDict()) param_names = frozenset(intermediate_repr["params"].keys()) if returns: intermediate_repr["params"].update(returns) del intermediate_repr["returns"] internal_body = intermediate_repr.get("_internal", {}).get("body", []) # TODO: Add correct classmethod/staticmethod to decorate function using `annotate_ancestry` and first-field checks # Such that the `self.` or `cls.` rewrite only applies to non-staticmethods # assert internal_body, "Expected `internal_body` to have contents" if internal_body and param_names: internal_body = list( map( ast.fix_missing_locations, map(RewriteName(param_names).visit, internal_body), )) return ClassDef( bases=list(map(rpartial(Name, Load()), class_bases)), body=list( chain.from_iterable(( (Expr( set_value( to_docstring( intermediate_repr, indent_level=0, emit_separating_tab=False, emit_default_doc=emit_default_doc, emit_types=False, ).replace("\n:param ", "{tab}:cvar ".format(tab=tab)).replace( "{tab}:cvar ".format(tab=tab), "\n{tab}:cvar ".format(tab=tab), 1, ).rstrip())), ), map(param2ast, intermediate_repr["params"].items()), iter((_make_call_meth( internal_body, returns["return_type"]["default"] if "default" in ( (returns or { "return_type": iter(()) }).get("return_type") or iter(())) else None, param_names, ), ) if emit_call and internal_body else tuple()), ))), decorator_list=list(map(rpartial(Name, Load()), decorator_list)) if decorator_list else [], keywords=[], name=class_name, expr=None, identifier_name=None, )
def sqlalchemy( intermediate_repr, emit_repr=True, class_name="Config", class_bases=("Base",), decorator_list=None, table_name=None, docstring_format="rest", word_wrap=True, emit_default_doc=True, ): """ Construct an SQLAlchemy declarative class :param intermediate_repr: a dictionary of form { "name": Optional[str], "type": Optional[str], "doc": Optional[str], "params": OrderedDict[str, {'typ': str, 'doc': Optional[str], 'default': Any}] "returns": Optional[OrderedDict[Literal['return_type'], {'typ': str, 'doc': Optional[str], 'default': Any}),)]] } :type intermediate_repr: ```dict``` :param emit_repr: Whether to generate a `__repr__` method :type emit_repr: ```bool``` :param class_name: name of class :type class_name: ```str``` :param class_bases: bases of class (the generated class will inherit these) :type class_bases: ```Iterable[str]``` :param decorator_list: List of decorators :type decorator_list: ```Optional[Union[List[Str], List[]]]``` :param table_name: Table name, defaults to `class_name` :type table_name: ```str``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpydoc', 'google']``` :param word_wrap: Whether to word-wrap. Set `DOCTRANS_LINE_LENGTH` to configure length. :type word_wrap: ```bool``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpydoc', 'google']``` :param emit_default_doc: Whether help/docstring should include 'With default' text :type emit_default_doc: ```bool``` :returns: SQLalchemy declarative class AST :rtype: ```ClassDef``` """ return ClassDef( name=class_name, bases=list(map(lambda class_base: Name(class_base, Load()), class_bases)), decorator_list=decorator_list or [], keywords=[], body=list( filter( None, ( Expr( set_value( "{doc}\n{tab}".format( doc=indent( "\n".join( map( str.strip, to_docstring( { "doc": intermediate_repr[ "doc" ].lstrip() + "\n\n" if intermediate_repr["returns"] else "", "params": OrderedDict(), "returns": intermediate_repr[ "returns" ], }, emit_default_doc=emit_default_doc, docstring_format=docstring_format, word_wrap=word_wrap, emit_types=True, ).splitlines(), ) ), tab, ).rstrip(), tab=tab, ) ) ) if intermediate_repr["doc"] or intermediate_repr["returns"].get("return_type", {}).get("doc") else None, Assign( targets=[Name("__tablename__", Store())], value=set_value(table_name or class_name), expr=None, lineno=None, **maybe_type_comment ), *map( lambda param: Assign( targets=[Name(param[0], Store())], value=param_to_sqlalchemy_column_call( param, include_name=True ), expr=None, lineno=None, **maybe_type_comment ), intermediate_repr["params"].items(), ), generate_repr_method( intermediate_repr["params"], class_name, docstring_format ) if emit_repr else None, ), ) ), expr=None, identifier_name=None, )
def sqlalchemy_table( intermediate_repr, name="config_tbl", docstring_format="rest", word_wrap=True, emit_default_doc=True, ): """ Construct an `name = sqlalchemy.Table(name, metadata, Column(…), …)` :param intermediate_repr: a dictionary of form { "name": Optional[str], "type": Optional[str], "doc": Optional[str], "params": OrderedDict[str, {'typ': str, 'doc': Optional[str], 'default': Any}] "returns": Optional[OrderedDict[Literal['return_type'], {'typ': str, 'doc': Optional[str], 'default': Any}),)]] } :type intermediate_repr: ```dict``` :param name: name of binding + table :type name: ```str``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpydoc', 'google']``` :param word_wrap: Whether to word-wrap. Set `DOCTRANS_LINE_LENGTH` to configure length. :type word_wrap: ```bool``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpydoc', 'google']``` :param emit_default_doc: Whether help/docstring should include 'With default' text :type emit_default_doc: ```bool``` :returns: AST of the Table expression + assignment :rtype: ```ClassDef``` """ return Assign( targets=[Name(name, Store())], value=Call( func=Name("Table", Load()), args=list( chain.from_iterable( ( iter( ( set_value(name), Name("metadata", Load()), ) ), map( partial(param_to_sqlalchemy_column_call, include_name=True), intermediate_repr["params"].items(), ), ) ) ), keywords=[ keyword( arg="comment", value=set_value( deindent( to_docstring( { "doc": intermediate_repr["doc"].lstrip() + "\n\n" if intermediate_repr["returns"] else "", "params": OrderedDict(), "returns": intermediate_repr["returns"], }, emit_default_doc=emit_default_doc, docstring_format=docstring_format, word_wrap=word_wrap, emit_types=True, ).strip() ) ), identifier=None, ) ] if intermediate_repr["doc"] else [], expr=None, expr_func=None, ), lineno=None, expr=None, **maybe_type_comment )