Hello community,

here is the log from the commit of package python-pytest-bdd for 
openSUSE:Factory checked in at 2019-09-10 00:03:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-bdd (Old)
 and      /work/SRC/openSUSE:Factory/.python-pytest-bdd.new.7948 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pytest-bdd"

Tue Sep 10 00:03:56 2019 rev:3 rq:729485 version:3.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pytest-bdd/python-pytest-bdd.changes      
2019-07-21 11:33:42.632783723 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pytest-bdd.new.7948/python-pytest-bdd.changes
    2019-09-10 00:04:03.257205193 +0200
@@ -1,0 +2,6 @@
+Mon Sep  9 14:09:26 UTC 2019 - Tomáš Chvátal <tchva...@suse.com>
+
+- Update to 3.2.1:
+  * python 3.8 support
+
+-------------------------------------------------------------------

Old:
----
  pytest-bdd-3.1.1.tar.gz

New:
----
  pytest-bdd-3.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-pytest-bdd.spec ++++++
--- /var/tmp/diff_new_pack.JlWfZN/_old  2019-09-10 00:04:03.997205144 +0200
+++ /var/tmp/diff_new_pack.JlWfZN/_new  2019-09-10 00:04:04.001205143 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pytest-bdd
-Version:        3.1.1
+Version:        3.2.1
 Release:        0
 Summary:        BDD for pytest
 License:        MIT

++++++ pytest-bdd-3.1.1.tar.gz -> pytest-bdd-3.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/.travis.yml 
new/pytest-bdd-3.2.1/.travis.yml
--- old/pytest-bdd-3.1.1/.travis.yml    2019-07-08 11:55:49.000000000 +0200
+++ new/pytest-bdd-3.2.1/.travis.yml    2019-08-21 13:21:08.000000000 +0200
@@ -5,6 +5,7 @@
   - "3.5"
   - "3.6"
   - "3.7"
+  - "3.8-dev"
 
 install: pip install tox tox-travis
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/CHANGES.rst 
new/pytest-bdd-3.2.1/CHANGES.rst
--- old/pytest-bdd-3.1.1/CHANGES.rst    2019-07-08 11:55:49.000000000 +0200
+++ new/pytest-bdd-3.2.1/CHANGES.rst    2019-08-21 13:21:08.000000000 +0200
@@ -4,6 +4,17 @@
 Unreleased
 ----------
 
+3.2.1
+----------
+
+- Fix regression introduced in 3.2.0 where pytest-bdd would break in presence 
of test items that are not functions.
+
+3.2.0
+----------
+
+- Fix Python 3.8 support
+- Remove code that rewrites code. This should help with the maintenance of 
this project and make debugging easier.
+
 3.1.1
 ----------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/pytest_bdd/__init__.py 
new/pytest-bdd-3.2.1/pytest_bdd/__init__.py
--- old/pytest-bdd-3.1.1/pytest_bdd/__init__.py 2019-07-08 11:55:49.000000000 
+0200
+++ new/pytest-bdd-3.2.1/pytest_bdd/__init__.py 2019-08-21 13:21:08.000000000 
+0200
@@ -3,6 +3,6 @@
 from pytest_bdd.steps import given, when, then
 from pytest_bdd.scenario import scenario, scenarios
 
-__version__ = '3.1.1'
+__version__ = '3.2.1'
 
 __all__ = [given.__name__, when.__name__, then.__name__, scenario.__name__, 
scenarios.__name__]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/pytest_bdd/plugin.py 
new/pytest-bdd-3.2.1/pytest_bdd/plugin.py
--- old/pytest-bdd-3.1.1/pytest_bdd/plugin.py   2019-07-08 11:55:49.000000000 
+0200
+++ new/pytest-bdd-3.2.1/pytest_bdd/plugin.py   2019-08-21 13:21:08.000000000 
+0200
@@ -13,12 +13,7 @@
 def pytest_addhooks(pluginmanager):
     """Register plugin hooks."""
     from pytest_bdd import hooks
-    try:
-        # pytest >= 2.8
-        pluginmanager.add_hookspecs(hooks)
-    except AttributeError:
-        # pytest < 2.8
-        pluginmanager.addhooks(hooks)
+    pluginmanager.add_hookspecs(hooks)
 
 
 @given('trace')
@@ -92,3 +87,23 @@
 def pytest_bdd_apply_tag(tag, function):
     mark = getattr(pytest.mark, tag)
     return mark(function)
+
+
+@pytest.mark.tryfirst
+def pytest_collection_modifyitems(session, config, items):
+    """Re-order items using the creation counter as fallback.
+
+    Pytest has troubles to correctly order the test items for python < 3.6.
+    For this reason, we have to apply some better ordering for pytest_bdd 
scenario-decorated test functions.
+
+    This is not needed for python 3.6+, but this logic is safe to apply in 
that case as well.
+    """
+    # TODO: Try to only re-sort the items that have __pytest_bdd_counter__, 
and not the others,
+    #  since there may be other hooks that are executed before this and that 
want to reorder item as well
+    def item_key(item):
+        if isinstance(item, pytest.Function):
+            declaration_order = getattr(item.function, 
'__pytest_bdd_counter__', 0)
+        else:
+            declaration_order = 0
+        return (item.reportinfo()[:2], declaration_order)
+    items.sort(key=item_key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/pytest_bdd/scenario.py 
new/pytest-bdd-3.2.1/pytest_bdd/scenario.py
--- old/pytest-bdd-3.1.1/pytest_bdd/scenario.py 2019-07-08 11:55:49.000000000 
+0200
+++ new/pytest-bdd-3.2.1/pytest_bdd/scenario.py 2019-08-21 13:21:08.000000000 
+0200
@@ -20,39 +20,31 @@
     from _pytest import fixtures as pytest_fixtures
 except ImportError:
     from _pytest import python as pytest_fixtures
-import six
 
 from . import exceptions
 from .feature import (
     Feature,
-    force_encode,
     force_unicode,
     get_features,
 )
 from .steps import (
-    execute,
-    get_caller_function,
     get_caller_module,
     get_step_fixture_name,
     inject_fixture,
-    recreate_function,
 )
 from .types import GIVEN
 from .utils import CONFIG_STACK, get_args
 
-if six.PY3:  # pragma: no cover
-    import runpy
-
-    def execfile(filename, init_globals):
-        """Execute given file as a python script in given globals 
environment."""
-        result = runpy.run_path(filename, init_globals=init_globals)
-        init_globals.update(result)
-
 
 PYTHON_REPLACE_REGEX = re.compile(r"\W")
 ALPHA_REGEX = re.compile(r"^\d+_*")
 
 
+# We have to keep track of the invocation of @scenario() so that we can 
reorder test item accordingly.
+# In python 3.6+ this is no longer necessary, as the order is automatically 
retained.
+_py2_scenario_creation_counter = 0
+
+
 def find_argumented_step_fixture_name(name, type_, fixturemanager, 
request=None):
     """Find argumented step fixture name."""
     # happens to be that _arg2fixturedefs is changed during the iteration so 
we use a copy
@@ -88,9 +80,11 @@
     """
     name = step.name
     try:
+        # Simple case where no parser is used for the step
         return request.getfixturevalue(get_step_fixture_name(name, step.type, 
encoding))
     except pytest_fixtures.FixtureLookupError:
         try:
+            # Could not find a fixture with the same name, let's see if there 
is a parser involved
             name = find_argumented_step_fixture_name(name, step.type, 
request._fixturemanager, request)
             if name:
                 return request.getfixturevalue(name)
@@ -204,63 +198,53 @@
 FakeRequest = collections.namedtuple("FakeRequest", ["module"])
 
 
-def _get_scenario_decorator(feature, feature_name, scenario, scenario_name, 
caller_module, caller_function, encoding):
-    """Get scenario decorator."""
-    g = locals()
-    g["_execute_scenario"] = _execute_scenario
+def _get_scenario_decorator(feature, feature_name, scenario, scenario_name, 
encoding):
+    global _py2_scenario_creation_counter
 
-    scenario_name = force_encode(scenario_name, encoding)
+    counter = _py2_scenario_creation_counter
+    _py2_scenario_creation_counter += 1
 
-    def decorator(_pytestbdd_function):
-        if isinstance(_pytestbdd_function, pytest_fixtures.FixtureRequest):
+    # HACK: Ideally we would use `def decorator(fn)`, but we want to return a 
custom exception
+    # when the decorator is misused.
+    # Pytest inspect the signature to determine the required fixtures, and in 
that case it would look
+    # for a fixture called "fn" that doesn't exist (if it exists then it's 
even worse).
+    # It will error with a "fixture 'fn' not found" message instead.
+    # We can avoid this hack by using a pytest hook and check for misuse 
instead.
+    def decorator(*args):
+        if not args:
             raise exceptions.ScenarioIsDecoratorOnly(
                 "scenario function can only be used as a decorator. Refer to 
the documentation.",
             )
-
-        g.update(locals())
-
-        args = get_args(_pytestbdd_function)
+        [fn] = args
+        args = get_args(fn)
         function_args = list(args)
         for arg in scenario.get_example_params():
             if arg not in function_args:
                 function_args.append(arg)
-        if "request" not in function_args:
-            function_args.append("request")
 
-        code = """def {name}({function_args}):
+        @pytest.mark.usefixtures(*function_args)
+        def scenario_wrapper(request):
             _execute_scenario(feature, scenario, request, encoding)
-            _pytestbdd_function({args})""".format(
-            name=_pytestbdd_function.__name__,
-            function_args=", ".join(function_args),
-            args=", ".join(args))
-
-        execute(code, g)
-
-        _scenario = recreate_function(
-            g[_pytestbdd_function.__name__],
-            module=caller_module,
-            firstlineno=caller_function.f_lineno,
-        )
+            return fn(*[request.getfixturevalue(arg) for arg in args])
 
         for param_set in scenario.get_params():
             if param_set:
-                _scenario = pytest.mark.parametrize(*param_set)(_scenario)
-
+                scenario_wrapper = 
pytest.mark.parametrize(*param_set)(scenario_wrapper)
         for tag in scenario.tags.union(feature.tags):
             config = CONFIG_STACK[-1]
-            config.hook.pytest_bdd_apply_tag(tag=tag, function=_scenario)
+            config.hook.pytest_bdd_apply_tag(tag=tag, 
function=scenario_wrapper)
 
-        _scenario.__doc__ = "{feature_name}: {scenario_name}".format(
+        scenario_wrapper.__doc__ = u"{feature_name}: {scenario_name}".format(
             feature_name=feature_name, scenario_name=scenario_name)
-        _scenario.__scenario__ = scenario
-        scenario.test_function = _scenario
-        return _scenario
-
-    return recreate_function(decorator, module=caller_module, 
firstlineno=caller_function.f_lineno)
+        scenario_wrapper.__scenario__ = scenario
+        scenario_wrapper.__pytest_bdd_counter__ = counter
+        scenario.test_function = scenario_wrapper
+        return scenario_wrapper
+    return decorator
 
 
 def scenario(feature_name, scenario_name, encoding="utf-8", 
example_converters=None,
-             caller_module=None, caller_function=None, features_base_dir=None, 
strict_gherkin=None):
+             caller_module=None, features_base_dir=None, strict_gherkin=None):
     """Scenario decorator.
 
     :param str feature_name: Feature file name. Absolute or relative to the 
configured feature base path.
@@ -269,9 +253,9 @@
     :param dict example_converters: optional `dict` of example converter 
function, where key is the name of the
         example parameter, and value is the converter function.
     """
+
     scenario_name = force_unicode(scenario_name, encoding)
     caller_module = caller_module or get_caller_module()
-    caller_function = caller_function or get_caller_function()
 
     # Get the feature
     if features_base_dir is None:
@@ -280,7 +264,7 @@
         strict_gherkin = get_strict_gherkin()
     feature = Feature.get_feature(features_base_dir, feature_name, 
encoding=encoding, strict_gherkin=strict_gherkin)
 
-    # Get the sc_enario
+    # Get the scenario
     try:
         scenario = feature.scenarios[scenario_name]
     except KeyError:
@@ -298,13 +282,11 @@
     scenario.validate()
 
     return _get_scenario_decorator(
-        feature,
-        feature_name,
-        scenario,
-        scenario_name,
-        caller_module,
-        caller_function,
-        encoding,
+        feature=feature,
+        feature_name=feature_name,
+        scenario=scenario,
+        scenario_name=scenario_name,
+        encoding=encoding,
     )
 
 
@@ -375,7 +357,6 @@
         (attr.__scenario__.feature.filename, attr.__scenario__.name)
         for name, attr in module.__dict__.items() if hasattr(attr, 
'__scenario__'))
 
-    index = 10
     for feature in get_features(abs_feature_paths, 
strict_gherkin=strict_gherkin):
         for scenario_name, scenario_object in feature.scenarios.items():
             # skip already bound scenarios
@@ -386,9 +367,6 @@
                 for test_name in get_python_name_generator(scenario_name):
                     if test_name not in module.__dict__:
                         # found an unique test name
-                        # recreate function to set line number
-                        _scenario = recreate_function(_scenario, 
module=module, firstlineno=index * 4)
-                        index += 1
                         module.__dict__[test_name] = _scenario
                         break
             found = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/pytest_bdd/steps.py 
new/pytest-bdd-3.2.1/pytest_bdd/steps.py
--- old/pytest-bdd-3.1.1/pytest_bdd/steps.py    2019-07-08 11:55:49.000000000 
+0200
+++ new/pytest-bdd-3.2.1/pytest_bdd/steps.py    2019-08-21 13:21:08.000000000 
+0200
@@ -32,7 +32,6 @@
 """
 
 from __future__ import absolute_import
-from types import CodeType
 import inspect
 import sys
 
@@ -41,7 +40,6 @@
     from _pytest import fixtures as pytest_fixtures
 except ImportError:
     from _pytest import python as pytest_fixtures
-import six
 
 from .feature import parse_line, force_encode
 from .types import GIVEN, WHEN, THEN
@@ -90,7 +88,7 @@
         func = pytest.fixture(scope=scope)(lambda: step_func)
         func.__doc__ = 'Alias for the "{0}" fixture.'.format(fixture)
         _, name = parse_line(name)
-        contribute_to_module(module, get_step_fixture_name(name, GIVEN), func)
+        setattr(module, get_step_fixture_name(name, GIVEN), func)
         return _not_a_fixture_decorator
 
     return _step_decorator(GIVEN, name, converters=converters, scope=scope, 
target_fixture=target_fixture)
@@ -185,84 +183,12 @@
             step_func.converters = lazy_step_func.converters = converters
 
         lazy_step_func = pytest.fixture(scope=scope)(lazy_step_func)
-        contribute_to_module(
-            module=get_caller_module(),
-            name=get_step_fixture_name(parsed_step_name, step_type),
-            func=lazy_step_func,
-        )
-
+        setattr(get_caller_module(), get_step_fixture_name(parsed_step_name, 
step_type), lazy_step_func)
         return func
 
     return decorator
 
 
-def recreate_function(func, module=None, name=None, add_args=[], 
firstlineno=None):
-    """Recreate a function, replacing some info.
-
-    :param func: Function object.
-    :param module: Module to contribute to.
-    :param add_args: Additional arguments to add to function.
-
-    :return: Function copy.
-    """
-    def get_code(func):
-        return func.__code__ if six.PY3 else func.func_code
-
-    def set_code(func, code):
-        if six.PY3:
-            func.__code__ = code
-        else:
-            func.func_code = code
-
-    argnames = [
-        "co_argcount", "co_nlocals", "co_stacksize", "co_flags", "co_code", 
"co_consts", "co_names",
-        "co_varnames", "co_filename", "co_name", "co_firstlineno", 
"co_lnotab", "co_freevars", "co_cellvars",
-    ]
-    if six.PY3:
-        argnames.insert(1, "co_kwonlyargcount")
-
-    for arg in get_args(func):
-        if arg in add_args:
-            add_args.remove(arg)
-
-    args = []
-    code = get_code(func)
-    for arg in argnames:
-        if module is not None and arg == "co_filename":
-            args.append(module.__file__)
-        elif name is not None and arg == "co_name":
-            args.append(name)
-        elif arg == "co_argcount":
-            args.append(getattr(code, arg) + len(add_args))
-        elif arg == "co_varnames":
-            co_varnames = getattr(code, arg)
-            args.append(co_varnames[:code.co_argcount] + tuple(add_args) + 
co_varnames[code.co_argcount:])
-        elif arg == "co_firstlineno":
-            args.append(firstlineno if firstlineno else 1)
-        else:
-            args.append(getattr(code, arg))
-
-    set_code(func, CodeType(*args))
-    if name is not None:
-        func.__name__ = name
-    return func
-
-
-def contribute_to_module(module, name, func):
-    """Contribute a function to a module.
-
-    :param module: Module to contribute to.
-    :param name: Attribute name.
-    :param func: Function object.
-
-    :return: New function copy contributed to the module
-    """
-    name = force_encode(name)
-    func = recreate_function(func, module=module)
-    setattr(module, name, func)
-    return func
-
-
 def get_caller_module(depth=2):
     """Return the module of the caller."""
     frame = sys._getframe(depth)
@@ -272,16 +198,6 @@
     return module
 
 
-def get_caller_function(depth=2):
-    """Return caller function."""
-    return sys._getframe(depth)
-
-
-def execute(code, g):
-    """Execute given code in given globals environment."""
-    exec(code, g)
-
-
 def inject_fixture(request, arg, value):
     """Inject fixture into pytest fixture request.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/tests/feature/test_scenario.py 
new/pytest-bdd-3.2.1/tests/feature/test_scenario.py
--- old/pytest-bdd-3.1.1/tests/feature/test_scenario.py 2019-07-08 
11:55:49.000000000 +0200
+++ new/pytest-bdd-3.2.1/tests/feature/test_scenario.py 2019-08-21 
13:21:08.000000000 +0200
@@ -57,11 +57,19 @@
     test2(request)
 
 
-def test_scenario_not_decorator(request):
+def test_scenario_not_decorator(testdir):
     """Test scenario function is used not as decorator."""
-    func = scenario(
-        'comments.feature',
-        'Strings that are not comments')
+    testdir.makefile('.feature', foo="""
+        Scenario: Foo
+            Given I have a bar
+        """)
+    testdir.makepyfile("""
+        from pytest_bdd import scenario
 
-    with pytest.raises(exceptions.ScenarioIsDecoratorOnly):
-        func(request)
+        test_foo = scenario('foo.feature', 'Foo')
+        """)
+
+    result = testdir.runpytest()
+
+    result.assert_outcomes(failed=1)
+    result.stdout.fnmatch_lines("*ScenarioIsDecoratorOnly: scenario function 
can only be used as a decorator*")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/tests/feature/test_steps.py 
new/pytest-bdd-3.2.1/tests/feature/test_steps.py
--- old/pytest-bdd-3.1.1/tests/feature/test_steps.py    2019-07-08 
11:55:49.000000000 +0200
+++ new/pytest-bdd-3.2.1/tests/feature/test_steps.py    2019-08-21 
13:21:08.000000000 +0200
@@ -246,20 +246,20 @@
     """)
     result = testdir.runpytest('-k test_when_fails_inline', '-vv')
     assert result.ret == 1
-    result.stdout.fnmatch_lines(['*test_when_fails_inline FAILED'])
+    result.stdout.fnmatch_lines(['*test_when_fails_inline*FAILED'])
     assert 'INTERNALERROR' not in result.stdout.str()
 
     result = testdir.runpytest('-k test_when_fails_decorated', '-vv')
     assert result.ret == 1
-    result.stdout.fnmatch_lines(['*test_when_fails_decorated FAILED'])
+    result.stdout.fnmatch_lines(['*test_when_fails_decorated*FAILED'])
     assert 'INTERNALERROR' not in result.stdout.str()
 
     result = testdir.runpytest('-k test_when_not_found', '-vv')
     assert result.ret == 1
-    result.stdout.fnmatch_lines(['*test_when_not_found FAILED'])
+    result.stdout.fnmatch_lines(['*test_when_not_found*FAILED'])
     assert 'INTERNALERROR' not in result.stdout.str()
 
     result = testdir.runpytest('-k test_when_step_validation_error', '-vv')
     assert result.ret == 1
-    result.stdout.fnmatch_lines(['*test_when_step_validation_error FAILED'])
+    result.stdout.fnmatch_lines(['*test_when_step_validation_error*FAILED'])
     assert 'INTERNALERROR' not in result.stdout.str()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/tests/test_hooks.py 
new/pytest-bdd-3.2.1/tests/test_hooks.py
--- old/pytest-bdd-3.1.1/tests/test_hooks.py    2019-07-08 11:55:49.000000000 
+0200
+++ new/pytest-bdd-3.2.1/tests/test_hooks.py    2019-08-21 13:21:08.000000000 
+0200
@@ -30,3 +30,26 @@
 
     assert result.stdout.lines.count('pytest_pyfunc_call hook') == 1
     assert result.stdout.lines.count('pytest_generate_tests hook') == 1
+
+
+def test_item_collection_does_not_break_on_non_function_items(testdir):
+    """Regression test for 
https://github.com/pytest-dev/pytest-bdd/issues/317""";
+    testdir.makeconftest("""
+    import pytest
+
+    @pytest.mark.tryfirst
+    def pytest_collection_modifyitems(session, config, items):
+        items[:] = [CustomItem(name=item.name, parent=item.parent) for item in 
items]
+
+    class CustomItem(pytest.Item):
+        def runtest(self):
+            assert True
+    """)
+
+    testdir.makepyfile("""
+    def test_convert_me_to_custom_item_and_assert_true():
+        assert False
+    """)
+
+    result = testdir.runpytest()
+    result.assert_outcomes(passed=1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pytest-bdd-3.1.1/tox.ini new/pytest-bdd-3.2.1/tox.ini
--- old/pytest-bdd-3.1.1/tox.ini        2019-07-08 11:55:49.000000000 +0200
+++ new/pytest-bdd-3.2.1/tox.ini        2019-08-21 13:21:08.000000000 +0200
@@ -3,7 +3,7 @@
 envlist = py27-pytestlatest-linters,
           py27-pytest{36,37,38,39,310,4,41,42,43,44,45,46},
           py37-pytest{36,37,38,39,310,4,41,42,43,44,45,46,5,latest},
-          py{35,36}-pytestlatest,
+          py{35,36,38}-pytestlatest,
           py27-pytestlatest-xdist
 skip_missing_interpreters = true
 


Reply via email to