def test_infer_sig_from_docstring_duplicate_args(self) -> None:
     assert_equal(
         infer_sig_from_docstring('\nfunc(x, x) -> str\nfunc(x, y) -> int',
                                  'func'),
         [
             FunctionSig(name='func',
                         args=[ArgSig(name='x'),
                               ArgSig(name='y')],
                         ret_type='int')
         ])
Пример #2
0
def generate_c_function_stub(
        module: ModuleType,
        name: str,
        obj: object,
        output: List[str],
        imports: List[str],
        self_var: Optional[str] = None,
        sigs: Optional[Dict[str, str]] = None,
        class_name: Optional[str] = None,
        class_sigs: Optional[Dict[str, str]] = None) -> None:
    """Generate stub for a single function or method.

    The result (always a single line) will be appended to 'output'.
    If necessary, any required names will be added to 'imports'.
    The 'class_name' is used to find signature of __init__ or __new__ in
    'class_sigs'.
    """
    if sigs is None:
        sigs = {}
    if class_sigs is None:
        class_sigs = {}

    ret_type = 'None' if name == '__init__' and class_name else 'Any'

    if (name in ('__new__', '__init__') and name not in sigs and class_name
            and class_name in class_sigs):
        inferred = [
            FunctionSig(name=name,
                        args=infer_arg_sig_from_anon_docstring(
                            class_sigs[class_name]),
                        ret_type=ret_type)
        ]  # type: Optional[List[FunctionSig]]
    else:
        docstr = getattr(obj, '__doc__', None)
        inferred = infer_sig_from_docstring(docstr, name)
        if inferred and is_pybind11_overloaded_function_docstring(
                docstr, name):
            # Remove pybind11 umbrella (*args, **kwargs) for overloaded functions
            del inferred[-1]
        if not inferred:
            if class_name and name not in sigs:
                inferred = [
                    FunctionSig(name,
                                args=infer_method_sig(name),
                                ret_type=ret_type)
                ]
            else:
                inferred = [
                    FunctionSig(name=name,
                                args=infer_arg_sig_from_anon_docstring(
                                    sigs.get(name, '(*args, **kwargs)')),
                                ret_type=ret_type)
                ]

    is_overloaded = len(inferred) > 1 if inferred else False
    if is_overloaded:
        imports.append('from typing import overload')
    if inferred:
        for signature in inferred:
            sig = []
            for arg in signature.args:
                if arg.name == self_var:
                    arg_def = self_var
                else:
                    arg_def = arg.name
                    if arg_def == 'None':
                        arg_def = '_none'  # None is not a valid argument name

                    if arg.type:
                        arg_def += ": " + strip_or_import(
                            arg.type, module, imports)

                    if arg.default:
                        arg_def += " = ..."

                sig.append(arg_def)

            if is_overloaded:
                output.append('@overload')
            output.append('def {function}({args}) -> {ret}: ...'.format(
                function=name,
                args=", ".join(sig),
                ret=strip_or_import(signature.ret_type, module, imports)))
    def test_infer_sig_from_docstring(self) -> None:
        assert_equal(infer_sig_from_docstring('\nfunc(x) - y', 'func'), [
            FunctionSig(name='func', args=[ArgSig(name='x')], ret_type='Any')
        ])

        assert_equal(infer_sig_from_docstring('\nfunc(x, Y_a=None)', 'func'), [
            FunctionSig(
                name='func',
                args=[ArgSig(name='x'),
                      ArgSig(name='Y_a', default=True)],
                ret_type='Any')
        ])

        assert_equal(infer_sig_from_docstring('\nfunc(x, Y_a=3)', 'func'), [
            FunctionSig(
                name='func',
                args=[ArgSig(name='x'),
                      ArgSig(name='Y_a', default=True)],
                ret_type='Any')
        ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x, Y_a=[1, 2, 3])', 'func'), [
                FunctionSig(
                    name='func',
                    args=[ArgSig(name='x'),
                          ArgSig(name='Y_a', default=True)],
                    ret_type='Any')
            ])

        assert_equal(infer_sig_from_docstring('\nafunc(x) - y', 'func'), [])
        assert_equal(infer_sig_from_docstring('\nfunc(x, y', 'func'), [])
        assert_equal(infer_sig_from_docstring('\nfunc(x=z(y))', 'func'), [
            FunctionSig(name='func',
                        args=[ArgSig(name='x', default=True)],
                        ret_type='Any')
        ])

        assert_equal(infer_sig_from_docstring('\nfunc x', 'func'), [])
        # Try to infer signature from type annotation.
        assert_equal(infer_sig_from_docstring('\nfunc(x: int)', 'func'), [
            FunctionSig(name='func',
                        args=[ArgSig(name='x', type='int')],
                        ret_type='Any')
        ])
        assert_equal(infer_sig_from_docstring('\nfunc(x: int=3)', 'func'), [
            FunctionSig(name='func',
                        args=[ArgSig(name='x', type='int', default=True)],
                        ret_type='Any')
        ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: int=3) -> int', 'func'), [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type='int', default=True)],
                            ret_type='int')
            ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: int=3) -> int   \n', 'func'), [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type='int', default=True)],
                            ret_type='int')
            ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: Tuple[int, str]) -> str',
                                     'func'),
            [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type='Tuple[int,str]')],
                            ret_type='str')
            ])

        assert_equal(
            infer_sig_from_docstring(
                '\nfunc(x: Tuple[int, Tuple[str, int], str], y: int) -> str',
                'func'),
            [
                FunctionSig(name='func',
                            args=[
                                ArgSig(name='x',
                                       type='Tuple[int,Tuple[str,int],str]'),
                                ArgSig(name='y', type='int')
                            ],
                            ret_type='str')
            ])

        assert_equal(infer_sig_from_docstring('\nfunc(x: foo.bar)', 'func'), [
            FunctionSig(name='func',
                        args=[ArgSig(name='x', type='foo.bar')],
                        ret_type='Any')
        ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: list=[1,2,[3,4]])', 'func'), [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type='list', default=True)],
                            ret_type='Any')
            ])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: str="nasty[")', 'func'), [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type='str', default=True)],
                            ret_type='Any')
            ])

        assert_equal(
            infer_sig_from_docstring('\nfunc[(x: foo.bar, invalid]', 'func'),
            [])

        assert_equal(
            infer_sig_from_docstring('\nfunc(x: invalid::type<with_template>)',
                                     'func'),
            [
                FunctionSig(name='func',
                            args=[ArgSig(name='x', type=None)],
                            ret_type='Any')
            ])

        assert_equal(infer_sig_from_docstring('\nfunc(x: str="")', 'func'), [
            FunctionSig(name='func',
                        args=[ArgSig(name='x', type='str', default=True)],
                        ret_type='Any')
        ])
Пример #4
0
def generate_c_function_stub_costum(
        module: ModuleType,
        name: str,
        obj: object,
        output: List[str],
        imports: List[str],
        self_var: Optional[str] = None,
        sigs: Optional[Dict[str, str]] = None,
        class_name: Optional[str] = None,
        class_sigs: Optional[Dict[str, str]] = None) -> None:
    """Generate stub for a single function or method.
       (Custom modified version, prototype is mypy.stubgenc.generate_c_function_stub)

    The result (always a single line) will be appended to 'output'.
    If necessary, any required names will be added to 'imports'.
    The 'class_name' is used to find signature of __init__ or __new__ in
    'class_sigs'.
    """
    # insert Set type from type for mypy missed it
    imports.append("from typing import Set")

    if sigs is None:
        sigs = {}
    if class_sigs is None:
        class_sigs = {}

    ret_type = 'None' if name == '__init__' and class_name else 'Any'

    if (name in ("__new__", "__init__") and name not in sigs and class_name
            and class_name in class_sigs):
        inferred: Optional[List[FunctionSig]] = [
            FunctionSig(
                name=name,
                args=infer_arg_sig_from_anon_docstring(class_sigs[class_name]),
                ret_type=ret_type,
            )
        ]
    else:
        docstr = getattr(obj, '__doc__', None)
        inferred = infer_sig_from_docstring(docstr, name)
        if inferred:
            assert docstr is not None
            if is_pybind11_overloaded_function_docstring(docstr, name):
                # Remove pybind11 umbrella (*args, **kwargs) for overloaded functions
                del inferred[-1]
        if not inferred:
            if class_name and name not in sigs:
                inferred = [
                    FunctionSig(name,
                                args=infer_method_sig(name, self_var),
                                ret_type=ret_type)
                ]
            else:
                inferred = [
                    FunctionSig(name=name,
                                args=infer_arg_sig_from_anon_docstring(
                                    sigs.get(name, '(*args, **kwargs)')),
                                ret_type=ret_type)
                ]
        elif class_name and self_var:
            args = inferred[0].args
            if not args or args[0].name != self_var:
                args.insert(0, ArgSig(name=self_var))

    is_overloaded = len(inferred) > 1 if inferred else False
    if is_overloaded:
        imports.append('from typing import overload')
    #TODO: logic branch too deep, need split
    if inferred:
        # signature id for overload func, used to pick corresbonding signature from inferred docstring
        sigid = 0
        for signature in inferred:
            arg_sig = []
            # in docstring, overload function signature start from 1.
            sigid += 1
            for arg in signature.args:
                if arg.name == self_var:
                    arg_def = self_var
                else:
                    arg_def = arg.name
                    if arg_def == 'None':
                        arg_def = '_none'  # None is not a valid argument name

                    if arg.type:
                        arg_def += ": " + \
                            strip_or_import(arg.type, module, imports)

                    # get function default value from func signature in __doc__
                    if arg.default:
                        if is_overloaded:
                            doc = docstr.split("\n")[3:-1]
                            for i in range(0, len(doc)):
                                # get signature from overload function docstr
                                func_str = refine_func_signature(
                                    doc[i], name, is_overloaded, sigid)
                                if func_str:
                                    var_str = funcparser.getFuncVarStr(
                                        func_str, arg.name)
                                    default_var = re.search(
                                        r" = .{0,}", var_str)
                                    if default_var:
                                        # parsered default var may contains traill char ",", strip it
                                        arg_def += default_var.group(0).strip(
                                            ",")
                                    else:
                                        arg_def += " = ..."
                                    break
                        else:
                            # similar like overload function
                            func_str = refine_func_signature(
                                docstr.split('\n')[0], name)
                            var_str = funcparser.getFuncVarStr(
                                func_str, arg.name)
                            default_var = re.search(r" = .{0,}", var_str)
                            if default_var:
                                arg_def += default_var.group(0).strip(",")
                            else:
                                arg_def += " = ..."

                arg_sig.append(arg_def)

            if is_overloaded:
                output.append('@overload')
            output.append('def {function}({args}) -> {ret}:'.format(
                function=name,
                args=", ".join(arg_sig),
                ret=strip_or_import(signature.ret_type, module, imports)))
            # append function summary from __doc__
            output.append("    \"\"\"")
            if is_overloaded:
                doc = docstr.split("\n")[3:-1]
                for i in range(0, len(doc)):
                    funcsig_reg = re.compile(
                        str(sigid) + ". " + name + r"\(.*?\) ->.*")
                    next_funcsig_reg = re.compile(
                        str(sigid + 1) + ". " + name + r"\(.*?\) ->.*")
                    if re.match(funcsig_reg, doc[i]):
                        for j in range(i + 2, len(doc)):
                            if re.match(next_funcsig_reg, doc[j]):
                                break
                            output.append(
                                '    {docline}'.format(docline=doc[j]))
                        break
            else:
                funcsig_reg = re.compile(name + r"\(.*?\) ->.*")
                for line in docstr.split("\n")[2:-1]:
                    if re.match(funcsig_reg, line):
                        continue
                    output.append('    {docline}'.format(docline=line))
            output.append("    \"\"\"")
            output.append("    ...\n")