diff --git a/CHANGES.rst b/CHANGES.rst index 8c0a6fe..d299c35 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,11 @@ Changelog ========= +Unreleased +---------- +- Fixed performance regression introduced in 4.0.0 where collection time of tests would take way longer than before. (youtux) + + 4.0.0 ----- diff --git a/pytest_bdd/utils.py b/pytest_bdd/utils.py index 8cbe8f7..60e99f2 100644 --- a/pytest_bdd/utils.py +++ b/pytest_bdd/utils.py @@ -1,29 +1,39 @@ """Various utility functions.""" -import inspect +from sys import _getframe +from inspect import getframeinfo import six CONFIG_STACK = [] +if six.PY2: + from inspect import getargspec as _getargspec -def get_args(func): - """Get a list of argument names for a function. + def get_args(func): + """Get a list of argument names for a function. - This is a wrapper around inspect.getargspec/inspect.signature because - getargspec got deprecated in Python 3.5 and signature isn't available on - Python 2. + :param func: The function to inspect. - :param func: The function to inspect. + :return: A list of argument names. + :rtype: list + """ + return _getargspec(func).args - :return: A list of argument names. - :rtype: list - """ - if six.PY2: - return inspect.getargspec(func).args - params = inspect.signature(func).parameters.values() - return [param.name for param in params if param.kind == param.POSITIONAL_OR_KEYWORD] +else: + from inspect import signature as _signature + + def get_args(func): + """Get a list of argument names for a function. + + :param func: The function to inspect. + + :return: A list of argument names. + :rtype: list + """ + params = _signature(func).parameters.values() + return [param.name for param in params if param.kind == param.POSITIONAL_OR_KEYWORD] def get_parametrize_markers_args(node): @@ -36,11 +46,19 @@ def get_parametrize_markers_args(node): def get_caller_module_locals(depth=2): - frame_info = inspect.stack()[depth] - frame = frame_info[0] # frame_info.frame - return frame.f_locals + """Get the caller module locals dictionary. + + We use sys._getframe instead of inspect.stack(0) because the latter is way slower, since it iterates over + all the frames in the stack. + """ + return _getframe(depth).f_locals def get_caller_module_path(depth=2): - frame_info = inspect.stack()[depth] - return frame_info[1] # frame_info.filename + """Get the caller module path. + + We use sys._getframe instead of inspect.stack(0) because the latter is way slower, since it iterates over + all the frames in the stack. + """ + frame = _getframe(depth) + return getframeinfo(frame, context=0).filename