diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/inspect.py | 95 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 5 |
2 files changed, 62 insertions, 38 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py index c9d10dc..d6bd8cd 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1954,6 +1954,8 @@ class Signature: if not s: return None + Parameter = cls._parameter_cls + if s.endswith("/)"): kind = Parameter.POSITIONAL_ONLY s = s[:-2] + ')' @@ -1969,55 +1971,74 @@ class Signature: if not isinstance(module, ast.Module): return None - # ast.FunctionDef f = module.body[0] parameters = [] empty = Parameter.empty invalid = object() - def parse_attribute(node): - if not isinstance(node.ctx, ast.Load): - return None - - value = node.value - o = parse_node(value) - if o is invalid: - return invalid - - if isinstance(value, ast.Name): - name = o - if name not in sys.modules: - return invalid - o = sys.modules[name] - - return getattr(o, node.attr, invalid) - - def parse_node(node): - if isinstance(node, ast.arg): - if node.annotation != None: - raise ValueError("Annotations are not currently supported") - return node.arg - if isinstance(node, ast.Num): - return node.n - if isinstance(node, ast.Str): - return node.s - if isinstance(node, ast.NameConstant): - return node.value - if isinstance(node, ast.Attribute): - return parse_attribute(node) - if isinstance(node, ast.Name): + module = None + module_dict = {} + module_name = getattr(func, '__module__', None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + module_dict = module.__dict__ + sys_module_dict = sys.modules + + def parse_name(node): + assert isinstance(node, ast.arg) + if node.annotation != None: + raise ValueError("Annotations are not currently supported") + return node.arg + + def wrap_value(s): + try: + value = eval(s, module_dict) + except NameError: + try: + value = eval(s, sys_module_dict) + except NameError: + raise RuntimeError() + + if isinstance(value, str): + return ast.Str(value) + if isinstance(value, (int, float)): + return ast.Num(value) + if isinstance(value, bytes): + return ast.Bytes(value) + if value in (True, False, None): + return ast.NameConstant(value) + raise RuntimeError() + + class RewriteSymbolics(ast.NodeTransformer): + def visit_Attribute(self, node): + a = [] + n = node + while isinstance(n, ast.Attribute): + a.append(n.attr) + n = n.value + if not isinstance(n, ast.Name): + raise RuntimeError() + a.append(n.id) + value = ".".join(reversed(a)) + return wrap_value(value) + + def visit_Name(self, node): if not isinstance(node.ctx, ast.Load): - return invalid - return node.id - return invalid + raise ValueError() + return wrap_value(node.id) def p(name_node, default_node, default=empty): - name = parse_node(name_node) + name = parse_name(name_node) if name is invalid: return None if default_node: - o = parse_node(default_node) + try: + default_node = RewriteSymbolics().visit(default_node) + o = ast.literal_eval(default_node) + except ValueError: + o = invalid if o is invalid: return None default = o if o is not invalid else default diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 9dc5475..1bfe724 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1601,12 +1601,15 @@ class TestSignatureObject(unittest.TestCase): self.assertTrue(isinstance(signature, inspect.Signature)) def p(name): return signature.parameters[name].default self.assertEqual(p('s'), 'avocado') + self.assertEqual(p('b'), b'bytes') self.assertEqual(p('d'), 3.14) self.assertEqual(p('i'), 35) - self.assertEqual(p('c'), sys.maxsize) self.assertEqual(p('n'), None) self.assertEqual(p('t'), True) self.assertEqual(p('f'), False) + self.assertEqual(p('local'), 3) + self.assertEqual(p('sys'), sys.maxsize) + self.assertEqual(p('exp'), sys.maxsize - 1) def test_signature_on_non_function(self): with self.assertRaisesRegex(TypeError, 'is not a callable object'): |