diff --git a/hy/compiler.py b/hy/compiler.py index 5d8f915..e6a888e 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -16,6 +16,7 @@ from hy._compat import ( raise_empty) from hy.macros import require, macroexpand, tag_macroexpand import hy.importer +import hy.inspect import traceback import importlib @@ -24,7 +25,6 @@ import ast import sys import keyword import copy -import inspect from collections import defaultdict from cmath import isnan @@ -441,10 +441,11 @@ class HyASTCompiler(object): # _compile_table[atom_type] is a method for compiling this # type of atom, so call it. If it has an extra parameter, # pass in `atom_type`. - arity = len(inspect.getargspec(_compile_table[atom_type])[0]) - ret = (_compile_table[atom_type](self, atom, atom_type) + atom_compiler = _compile_table[atom_type] + arity = hy.inspect.get_arity(atom_compiler) + ret = (atom_compiler(self, atom, atom_type) if arity == 3 - else _compile_table[atom_type](self, atom)) + else atom_compiler(self, atom)) if not isinstance(ret, Result): ret = Result() + ret return ret diff --git a/hy/importer.py b/hy/importer.py index 450de27..1b368a8 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -2,6 +2,8 @@ # This file is part of Hy, which is free software licensed under the Expat # license. See the LICENSE. +from __future__ import absolute_import + from hy.compiler import hy_compile, HyTypeError from hy.models import HyObject, HyExpression, HySymbol, replace_hy_obj from hy.lex import tokenize, LexException diff --git a/hy/inspect.py b/hy/inspect.py new file mode 100644 index 0000000..ff972ec --- /dev/null +++ b/hy/inspect.py @@ -0,0 +1,37 @@ +# Copyright 2018 the authors. +# This file is part of Hy, which is free software licensed under the Expat +# license. See the LICENSE. + +from __future__ import absolute_import + +import inspect + +try: + # Check if we have the newer inspect.signature available. + # Otherwise fallback to the legacy getargspec. + inspect.signature # noqa +except AttributeError: + def get_arity(fn): + return len(inspect.getargspec(fn)[0]) + + def has_kwargs(fn): + argspec = inspect.getargspec(fn) + return argspec.keywords is not None + + def format_args(fn): + argspec = inspect.getargspec(fn) + return inspect.formatargspec(*argspec) + +else: + def get_arity(fn): + parameters = inspect.signature(fn).parameters + return sum(1 for param in parameters.values() + if param.kind == param.POSITIONAL_OR_KEYWORD) + + def has_kwargs(fn): + parameters = inspect.signature(fn).parameters + return any(param.kind == param.VAR_KEYWORD + for param in parameters.values()) + + def format_args(fn): + return str(inspect.signature(fn)) diff --git a/hy/macros.py b/hy/macros.py index 9b1a743..0613711 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -2,9 +2,8 @@ # This file is part of Hy, which is free software licensed under the Expat # license. See the LICENSE. -from inspect import getargspec, formatargspec +import hy.inspect from hy.models import replace_hy_obj, HyExpression, HySymbol - from hy.errors import HyTypeError, HyMacroExpansionError from collections import defaultdict @@ -36,8 +35,7 @@ def macro(name): def _(fn): fn.__name__ = '({})'.format(name) try: - argspec = getargspec(fn) - fn._hy_macro_pass_compiler = argspec.keywords is not None + fn._hy_macro_pass_compiler = hy.inspect.has_kwargs(fn) except Exception: # An exception might be raised if fn has arguments with # names that are invalid in Python. @@ -136,9 +134,7 @@ def make_empty_fn_copy(fn): # can continue running. Unfortunately, the error message that might get # raised later on while expanding a macro might not make sense at all. - argspec = getargspec(fn) - formatted_args = formatargspec(*argspec) - + formatted_args = hy.inspect.format_args(fn) fn_str = 'lambda {}: None'.format( formatted_args.lstrip('(').rstrip(')')) empty_fn = eval(fn_str)