Skip to content

Commit

Permalink
Merge branch 'master' into pr-129
Browse files Browse the repository at this point in the history
  • Loading branch information
wolever committed Mar 2, 2023
2 parents f19dba7 + 62dfec2 commit e8c47b2
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 472 deletions.
461 changes: 103 additions & 358 deletions .circleci/config.yml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Icon?
.tox
build/
.cache/

\.idea/
venv/
.vscode/
.vscode/
.idea/
15 changes: 13 additions & 2 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
0.9.0 (2023-03-03)
* Drop support for Python 2.X, 3.5, and 3.6;
Add support for Python 3.10, 3.11
(https://github.com/wolever/parameterized/pull/153)
* Modernize from setup.py -> pyproject.toml
(https://github.com/wolever/parameterized/pull/147; thanks @KOLANICH)
* Add ``namespace`` argument to ``@parameterize.expand``
(https://github.com/wolever/parameterized/pull/148; thanks @KOLANICH)
* Add support for ``IsolatedAsyncioTestCase``
(https://github.com/wolever/parameterized/pull/135; thanks @Ronserruya)

0.8.1 (2021-01-09)
* Add README and LICENSE to pypi sdist package
(https://github.com/wolever/parameterized/pull/114; thanks @chohner)
Expand Down Expand Up @@ -91,12 +102,12 @@
* Allow the names of test cases generated by ``parameterized.expand`` to
be customized.
(https://github.com/wolever/nose-parameterized/pull/19;
thanks @curtissiemens)
thanks @curtissiemens)

0.3.4 (2014-10-03)
* Use ``functools.wraps`` to wrap expanded functions
(https://github.com/wolever/nose-parameterized/pull/17;
thanks @toumorokoshi)
thanks @toumorokoshi)

0.3.3 (2014-01-03)
* Replace unsafe characters with "_" in names generated by
Expand Down
74 changes: 29 additions & 45 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,33 +179,27 @@ Compatibility

`Yes`__ (mostly).

__ https://travis-ci.org/wolever/parameterized
__ https://app.circleci.com/pipelines/github/wolever/parameterized?branch=master

.. list-table::
:header-rows: 1
:stub-columns: 1

* -
- Py2.6
- Py2.7
- Py3.4
- Py3.5
- Py3.6
- Py3.7
- Py3.8
- Py3.9
- PyPy
- Py3.10
- Py3.11
- PyPy3
- ``@mock.patch``
* - nose
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- no§
- no§
- yes
* - nose2
- yes
Expand All @@ -215,30 +209,21 @@ __ https://travis-ci.org/wolever/parameterized
- yes
- yes
- yes
- yes
- yes
- yes
* - py.test 2
- yes
- yes
- no*
- no*
- no*
- no*
- yes
- yes
- yes
- yes
- no*
- no*
- no*
* - py.test 3
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- yes
- no*
- no*
- yes
* - py.test 4
- no**
Expand All @@ -248,9 +233,6 @@ __ https://travis-ci.org/wolever/parameterized
- no**
- no**
- no**
- no**
- no**
- no**
* - py.test fixtures
- no†
- no†
Expand All @@ -259,9 +241,6 @@ __ https://travis-ci.org/wolever/parameterized
- no†
- no†
- no†
- no†
- no†
- no†
* - | unittest
| (``@parameterized.expand``)
- yes
Expand All @@ -271,29 +250,29 @@ __ https://travis-ci.org/wolever/parameterized
- yes
- yes
- yes
- yes
- yes
- yes
* - | unittest2
| (``@parameterized.expand``)
- yes
- yes
- yes
- yes
- no§
- no§
- yes
- yes
- yes
- yes
- yes
- yes

\*: py.test 2 does `does not appear to work (#71)`__ under Python 3. Please comment on the related issues if you are affected.
§: nose and unittest2 - both of which were last updated in 2015 - sadly do not
appear to support Python 3.10 or 3.11.

\*: `py.test 2 does not appear to work under Python 3 (#71)`__, and
`py.test 3 does not appear to work under Python 3.10 or 3.11 (#154)`__.

\*\*: py.test 4 is not yet supported (but coming!) in `issue #34`__

†: py.test fixture support is documented in `issue #81`__


__ https://github.com/wolever/parameterized/issues/71
__ https://github.com/wolever/parameterized/issues/154
__ https://github.com/wolever/parameterized/issues/34
__ https://github.com/wolever/parameterized/issues/81

Expand Down Expand Up @@ -388,7 +367,7 @@ class is a subclass of ``unittest.TestCase``):
class AddTestCase(unittest.TestCase):
@parameterized.expand([
("2 and 3", 2, 3, 5),
("3 and 5", 2, 3, 5),
("3 and 5", 3, 5, 8),
])
def test_add(self, _, a, b, expected):
assert_equal(a + b, expected)
Expand Down Expand Up @@ -649,9 +628,10 @@ To migrate a codebase from ``nose-parameterized`` to ``parameterized``:
FAQ
---

What happened to ``nose-parameterized``?
Originally only nose was supported. But now everything is supported, and it
only made sense to change the name!
What happened to Python 2.X, 3.5, and 3.6 support?
As of version 0.9.0, ``parameterized`` no longer supports Python 2.X, 3.5,
or 3.6. Previous versions of ``parameterized`` - 0.8.1 being the latest -
will continue to work, but will not receive any new features or bug fixes.

What do you mean when you say "nose is best supported"?
There are small caveates with ``py.test`` and ``unittest``: ``py.test``
Expand All @@ -668,3 +648,7 @@ Why do I get an ``AttributeError: 'function' object has no attribute 'expand'``
You've likely installed the ``parametrized`` (note the missing *e*)
package. Use ``parameterized`` (with the *e*) instead and you'll be all
set.

What happened to ``nose-parameterized``?
Originally only nose was supported. But now everything is supported, and it
only made sense to change the name!
2 changes: 1 addition & 1 deletion parameterized/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .parameterized import parameterized, param, parameterized_class

__version__ = "0.8.1"
__version__ = "0.9.0"
50 changes: 42 additions & 8 deletions parameterized/parameterized.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
class SkipTest(Exception):
pass

# NOTE: even though Python 2 support has been dropped, these checks have been
# left in place to avoid merge conflicts. They can be removed in the future, and
# future code can be written to assume Python 3.
PY3 = sys.version_info[0] == 3
PY2 = sys.version_info[0] == 2

Expand Down Expand Up @@ -90,11 +93,15 @@ def dummy_func(*args, **kwargs):
return dummy_func

if hasattr(func, 'patchings'):
is_original_async = inspect.iscoroutinefunction(func)
func = dummy_wrapper(func)
tmp_patchings = func.patchings
delattr(func, 'patchings')
for patch_obj in tmp_patchings:
func = patch_obj.decorate_callable(func)
if is_original_async:
func = patch_obj.decorate_async_callable(func)
else:
func = patch_obj.decorate_callable(func)
return func


Expand Down Expand Up @@ -353,7 +360,7 @@ def set_test_runner(name):
def detect_runner():
""" Guess which test runner we're using by traversing the stack and looking
for the first matching module. This *should* be reasonably safe, as
it's done during test disocvery where the test runner should be the
it's done during test discovery where the test runner should be the
stack frame immediately outside. """
if _test_runner_override is not None:
return _test_runner_override
Expand All @@ -376,6 +383,7 @@ def detect_runner():
return _test_runner_guess



class parameterized(object):
""" Parameterize a test case::
Expand Down Expand Up @@ -516,12 +524,29 @@ def check_input_values(cls, input_values):

@classmethod
def expand(cls, input, name_func=None, doc_func=None, skip_on_empty=False,
**legacy):
namespace=None, **legacy):
""" A "brute force" method of parameterizing test cases. Creates new
test cases and injects them into the namespace that the wrapped
function is being defined in. Useful for parameterizing tests in
subclasses of 'UnitTest', where Nose test generators don't work.
:param input: An iterable of values to pass to the test function.
:param name_func: A function that takes a single argument (the
value from the input iterable) and returns a string to use as
the name of the test case. If not provided, the name of the
test case will be the name of the test function with the
parameter value appended.
:param doc_func: A function that takes a single argument (the
value from the input iterable) and returns a string to use as
the docstring of the test case. If not provided, the docstring
of the test case will be the docstring of the test function.
:param skip_on_empty: If True, the test will be skipped if the
input iterable is empty. If False, a ValueError will be raised
if the input iterable is empty.
:param namespace: The namespace (dict-like) to inject the test cases
into. If not provided, the namespace of the test function will
be used.
>>> @parameterized.expand([("foo", 1, 2)])
... def test_add1(name, input, expected):
... actual = add1(input)
Expand All @@ -548,7 +573,9 @@ def expand(cls, input, name_func=None, doc_func=None, skip_on_empty=False,
name_func = name_func or default_name_func

def parameterized_expand_wrapper(f, instance=None):
frame_locals = inspect.currentframe().f_back.f_locals
frame_locals = namespace
if frame_locals is None:
frame_locals = inspect.currentframe().f_back.f_locals

parameters = cls.input_as_callable(input)()

Expand Down Expand Up @@ -577,13 +604,20 @@ def parameterized_expand_wrapper(f, instance=None):
delete_patches_if_need(f)

f.__test__ = False

return parameterized_expand_wrapper

@classmethod
def param_as_standalone_func(cls, p, func, name):
@wraps(func)
def standalone_func(*a):
return func(*(a + p.args), **p.kwargs)
if inspect.iscoroutinefunction(func):
@wraps(func)
async def standalone_func(*a):
return await func(*(a + p.args), **p.kwargs)
else:
@wraps(func)
def standalone_func(*a):
return func(*(a + p.args), **p.kwargs)

standalone_func.__name__ = name

# place_as is used by py.test to determine what source file should be
Expand Down Expand Up @@ -638,7 +672,7 @@ class TestUserAccessLevel(TestCase):
)

class_name_func = class_name_func or default_class_name_func

if classname_func:
warnings.warn(
"classname_func= is deprecated; use class_name_func= instead. "
Expand Down
32 changes: 31 additions & 1 deletion parameterized/test.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# coding=utf-8

import inspect
import sys
import mock
from functools import wraps
from unittest import TestCase
from nose.tools import assert_equal, assert_raises
try:
from nose.tools import assert_equal, assert_raises
except ImportError:
def assert_equal(*args, **kwds):
return TestCase().assertEqual(*args, **kwds)
def assert_raises(*args, **kwds):
return TestCase().assertRaises(*args, **kwds)

from .parameterized import (
PY3, PY2, parameterized, param, parameterized_argument_value_pairs,
Expand Down Expand Up @@ -622,3 +629,26 @@ class TestUnicodeDocstring(object):
def test_with_docstring(self, param):
""" Это док-стринг, содержащий не-ascii символы """
pass

if sys.version_info.major == 3 and sys.version_info.minor >= 8:
from unittest import IsolatedAsyncioTestCase

class TestAsyncParameterizedExpandWithNoMockPatchForClass(IsolatedAsyncioTestCase):
expect([
"test_one_async_function('foo1')",
"test_one_async_function('foo0')",
"test_one_async_function(42)",
"test_one_async_function_patch_decorator('foo1', 'umask')",
"test_one_async_function_patch_decorator('foo0', 'umask')",
"test_one_async_function_patch_decorator(42, 'umask')",
])

@parameterized.expand([(42,), "foo0", param("foo1")])
async def test_one_async_function(self, foo):
missing_tests.remove("test_one_async_function(%r)" % (foo, ))

@parameterized.expand([(42,), "foo0", param("foo1")])
@mock.patch("os.umask")
async def test_one_async_function_patch_decorator(self, foo, mock_umask):
missing_tests.remove("test_one_async_function_patch_decorator(%r, %r)" %
(foo, mock_umask._mock_name))
Loading

0 comments on commit e8c47b2

Please sign in to comment.