def test_signature_from_str_positionaly_only_args(): sig = inspect.signature_from_str('(a, b=0, /, c=1)') assert list(sig.parameters.keys()) == ['a', 'b', 'c'] assert sig.parameters['a'].kind == Parameter.POSITIONAL_ONLY assert sig.parameters['a'].default == Parameter.empty assert sig.parameters['b'].kind == Parameter.POSITIONAL_ONLY assert sig.parameters['b'].default == '0' assert sig.parameters['c'].kind == Parameter.POSITIONAL_OR_KEYWORD assert sig.parameters['c'].default == '1'
def test_typing_overload_from_import(): source = ('from typing import overload\n' '\n' '@overload\n' 'def func(x: int, y: int) -> int: pass\n' '\n' '@overload\n' 'def func(x: str, y: str) -> str: pass\n' '\n' 'def func(x, y): pass\n') parser = Parser(source) parser.parse() assert parser.overloads == { 'func': [ signature_from_str('(x: int, y: int) -> int'), signature_from_str('(x: str, y: str) -> str') ] }
def test_signature_from_str_annotations(): signature = '(a: int, *args: bytes, b: str = "blah", **kwargs: float) -> None' sig = inspect.signature_from_str(signature) assert list(sig.parameters.keys()) == ['a', 'args', 'b', 'kwargs'] assert sig.parameters['a'].annotation == "int" assert sig.parameters['args'].annotation == "bytes" assert sig.parameters['b'].annotation == "str" assert sig.parameters['kwargs'].annotation == "float" assert sig.return_annotation == 'None'
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist(arglist) sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / params += addnodes.desc_parameter( '', '', addnodes.desc_sig_operator('', '/')) if param.kind == param.KEYWORD_ONLY and last_kind in ( param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * params += addnodes.desc_parameter( '', '', addnodes.desc_sig_operator('', '*')) node = addnodes.desc_parameter() if param.kind == param.VAR_POSITIONAL: node += addnodes.desc_sig_operator('', '*') node += addnodes.desc_sig_name('', param.name) elif param.kind == param.VAR_KEYWORD: node += addnodes.desc_sig_operator('', '**') node += addnodes.desc_sig_name('', param.name) else: node += addnodes.desc_sig_name('', param.name) if param.annotation is not param.empty: children = _parse_annotation(param.annotation) node += addnodes.desc_sig_punctuation('', ':') node += nodes.Text(' ') node += addnodes.desc_sig_name('', '', *children) # type: ignore if param.default is not param.empty: if param.annotation is not param.empty: node += nodes.Text(' ') node += addnodes.desc_sig_operator('', '=') node += nodes.Text(' ') else: node += addnodes.desc_sig_operator('', '=') node += nodes.inline('', param.default, classes=['default_value'], support_smartquotes=False) params += node last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) return params
def test_signature_from_str_default_values(): signature = ('(a=0, b=0.0, c="str", d=b"bytes", e=..., f=True, ' 'g=[1, 2, 3], h={"a": 1}, i={1, 2, 3}, ' 'j=lambda x, y: None, k=None, l=object(), m=foo.bar.CONSTANT)') sig = inspect.signature_from_str(signature) assert sig.parameters['a'].default == '0' assert sig.parameters['b'].default == '0.0' assert sig.parameters['c'].default == "'str'" assert sig.parameters['d'].default == "b'bytes'" assert sig.parameters['e'].default == '...' assert sig.parameters['f'].default == 'True' assert sig.parameters['g'].default == '[1, 2, 3]' assert sig.parameters['h'].default == "{'a': 1}" assert sig.parameters['i'].default == '{1, 2, 3}' assert sig.parameters['j'].default == 'lambda x, y: ...' assert sig.parameters['k'].default == 'None' assert sig.parameters['l'].default == 'object()' assert sig.parameters['m'].default == 'foo.bar.CONSTANT'
def _cleanup_signature(s: str) -> str: """Clean up signature using inspect.signautre() for mangle_signature()""" try: sig = signature_from_str(s) parameters = list(sig.parameters.values()) for i, param in enumerate(parameters): if param.annotation is not Parameter.empty: # Remove typehints param = param.replace(annotation=Parameter.empty) if param.default is not Parameter.empty: # Replace default value by "None" param = param.replace(default=None) parameters[i] = param sig = sig.replace(parameters=parameters, return_annotation=Parameter.empty) return str(sig) except Exception: # Return the original signature string if failed to clean (ex. parsing error) return s
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist(arglist) sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / params += addnodes.desc_parameter('', nodes.Text('/')) if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * params += addnodes.desc_parameter('', nodes.Text('*')) node = addnodes.desc_parameter() if param.kind == param.VAR_POSITIONAL: node += nodes.Text('*' + param.name) elif param.kind == param.VAR_KEYWORD: node += nodes.Text('**' + param.name) else: node += nodes.Text(param.name) if param.annotation is not param.empty: node += nodes.Text(': ' + param.annotation) if param.default is not param.empty: if param.annotation is not param.empty: node += nodes.Text(' = ' + str(param.default)) else: node += nodes.Text('=' + str(param.default)) params += node last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / params += addnodes.desc_parameter('', nodes.Text('/')) return params
def test_signature_from_str_invalid(): with pytest.raises(SyntaxError): inspect.signature_from_str('')
def test_signature_from_str_complex_annotations(): sig = inspect.signature_from_str('() -> Tuple[str, int, ...]') assert sig.return_annotation == 'Tuple[str, int, ...]' sig = inspect.signature_from_str('() -> Callable[[int, int], int]') assert sig.return_annotation == 'Callable[[int, int], int]'
def test_signature_from_str_positionaly_only_args(): sig = inspect.signature_from_str('(a, /, b)') assert list(sig.parameters.keys()) == ['a', 'b'] assert sig.parameters['a'].kind == Parameter.POSITIONAL_ONLY assert sig.parameters['b'].kind == Parameter.POSITIONAL_OR_KEYWORD