Ejemplo n.º 1
0
 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"),
     )
Ejemplo n.º 2
0
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,
    }
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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,
    )
Ejemplo n.º 5
0
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,
    )
Ejemplo n.º 6
0
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
    )