diff options
author | robot-ydb-importer <robot-ydb-importer@yandex-team.com> | 2024-06-28 14:57:17 +0300 |
---|---|---|
committer | robot-ydb-importer <robot-ydb-importer@yandex-team.com> | 2024-06-28 15:10:57 +0300 |
commit | a75ea2cde045917041437c8859477ba4323503bd (patch) | |
tree | 565ad17417cc162775e29f4b27987e2c322ddc29 /contrib/python/pytest-lazy-fixtures | |
parent | d0dc9572233e6c5a1040c5eb91227066c876b832 (diff) | |
download | ydb-a75ea2cde045917041437c8859477ba4323503bd.tar.gz |
YDB Import 604
a63e035353af5512e5e1c2789d17f495871e0007
Diffstat (limited to 'contrib/python/pytest-lazy-fixtures')
12 files changed, 585 insertions, 0 deletions
diff --git a/contrib/python/pytest-lazy-fixtures/.dist-info/METADATA b/contrib/python/pytest-lazy-fixtures/.dist-info/METADATA new file mode 100644 index 0000000000..260a9b18cc --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/.dist-info/METADATA @@ -0,0 +1,172 @@ +Metadata-Version: 2.1 +Name: pytest-lazy-fixtures +Version: 1.0.7 +Summary: Allows you to use fixtures in @pytest.mark.parametrize. +Home-page: https://github.com/dev-petrov/pytest-lazy-fixtures +License: MIT +Keywords: tests,pytest,lazy,fixture +Author: Petrov Anton +Author-email: antonp2@yandex.ru +Requires-Python: >=3.8,<4.0 +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Dist: pytest (>=7) +Project-URL: Repository, https://github.com/dev-petrov/pytest-lazy-fixtures +Description-Content-Type: text/markdown + +# pytest-lazy-fixtures + +[![codecov](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures/branch/master/graph/badge.svg)](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures) +[![CI](https://github.com/dev-petrov/pytest-lazy-fixtures/workflows/CI/badge.svg)](https://github.com/dev-petrov/pytest-lazy-fixtures/actions/workflows/ci-test.yml) +[![PyPI version](https://badge.fury.io/py/pytest-lazy-fixtures.svg)](https://badge.fury.io/py/pytest-lazy-fixtures) + +Use your fixtures in `@pytest.mark.parametrize`. + +This project was inspired by [pytest-lazy-fixture](https://github.com/TvoroG/pytest-lazy-fixture). + +Improvements that have been made in this project: + +1. You can use fixtures in any data structures +2. You can access the attributes of fixtures +3. You can use functions in fixtures + +## Installation + +```shell +pip install pytest-lazy-fixtures +``` + +## Usage + +To use your fixtures inside `@pytest.mark.parametrize` you can use `lf` (`lazy_fixture`) or `pytest.lazy_fixtures`. + +```python +import pytest +from pytest_lazy_fixtures import lf + +@pytest.fixture() +def one(): + return 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one'))]) +def test_func(arg1, arg2): + assert arg2 == 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', pytest.lazy_fixtures('one'))]) +def test_func(arg1, arg2): + assert arg2 == 1 +``` + +`lf` can be used with any data structures. For example, in the following example, `lf` is used in the dictionary: + +```python +import pytest +from pytest_lazy_fixtures import lf + +@pytest.fixture() +def one(): + return 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', {"value": lf('one')})]) +def test_func(arg1, arg2): + assert arg2 == {"value": 1} +``` + +You can also specify getting an attribute through a dot: + +```python +import pytest +from pytest_lazy_fixtures import lf + +class Entity: + def __init__(self, value): + self.value = value + +@pytest.fixture() +def one(): + return Entity(1) + +@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one.value'))]) +def test_func(arg1, arg2): + assert arg2 == 1 +``` + +And there is some useful wrapper called `lfc` (`lazy_fixture_callable`) or `pytest.lazy_fixtures_callable`. +It can work with any callable and your fixtures, e.g. + +```python +import pytest +from pytest_lazy_fixtures import lf, lfc + +class Entity: + def __init__(self, value): + self.value = value + + def __str__(self) -> str: + return str(self.value) + + def sum(self, value: int) -> int: + return self.value + value + +@pytest.fixture() +def entity(): + return Entity(1) + +@pytest.fixture() +def two(): + return 2 + +@pytest.fixture() +def three(): + return 3 + +@pytest.fixture() +def entity_format(): + def _entity_format(entity: Entity): + return {"value": entity.value} + + return _entity_format + +@pytest.mark.parametrize( + "message", + [ + lfc( + "There is two lazy fixture args, {} and {}! And one kwarg {field}! And also simple value {simple}".format, + lf("entity"), + lf("two"), + field=lf("three"), + simple="value", + ), + ], +) +def test_lazy_fixture_callable_with_func(message): + assert message == "There is two lazy fixture args, 1 and 2! And one kwarg 3! And also simple value value" + + +@pytest.mark.parametrize("formatted", [lfc("entity_format", lf("entity"))]) +def test_lazy_fixture_callable_with_lf(formatted, entity): + assert formatted == {"value": entity.value} + + +@pytest.mark.parametrize("result", [lfc("entity.sum", lf("two"))]) +def test_lazy_fixture_callable_with_attr_lf(result): + assert result == 3 +``` + +## Contributing + +Contributions are very welcome. Tests can be run with `pytest`. + +## License + +Distributed under the terms of the `MIT` license, `pytest-lazy-fixtures` is free and open source software + +## Issues + +If you encounter any problems, please file an issue along with a detailed description. + diff --git a/contrib/python/pytest-lazy-fixtures/.dist-info/entry_points.txt b/contrib/python/pytest-lazy-fixtures/.dist-info/entry_points.txt new file mode 100644 index 0000000000..82c4b1f274 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[pytest11] +pytest_lazyfixture=pytest_lazy_fixtures.plugin + diff --git a/contrib/python/pytest-lazy-fixtures/LICENSE b/contrib/python/pytest-lazy-fixtures/LICENSE new file mode 100644 index 0000000000..f2c87d0e56 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Anton Petrov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/contrib/python/pytest-lazy-fixtures/README.md b/contrib/python/pytest-lazy-fixtures/README.md new file mode 100644 index 0000000000..6c13ec033a --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/README.md @@ -0,0 +1,150 @@ +# pytest-lazy-fixtures + +[![codecov](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures/branch/master/graph/badge.svg)](https://codecov.io/gh/dev-petrov/pytest-lazy-fixtures) +[![CI](https://github.com/dev-petrov/pytest-lazy-fixtures/workflows/CI/badge.svg)](https://github.com/dev-petrov/pytest-lazy-fixtures/actions/workflows/ci-test.yml) +[![PyPI version](https://badge.fury.io/py/pytest-lazy-fixtures.svg)](https://badge.fury.io/py/pytest-lazy-fixtures) + +Use your fixtures in `@pytest.mark.parametrize`. + +This project was inspired by [pytest-lazy-fixture](https://github.com/TvoroG/pytest-lazy-fixture). + +Improvements that have been made in this project: + +1. You can use fixtures in any data structures +2. You can access the attributes of fixtures +3. You can use functions in fixtures + +## Installation + +```shell +pip install pytest-lazy-fixtures +``` + +## Usage + +To use your fixtures inside `@pytest.mark.parametrize` you can use `lf` (`lazy_fixture`) or `pytest.lazy_fixtures`. + +```python +import pytest +from pytest_lazy_fixtures import lf + +@pytest.fixture() +def one(): + return 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one'))]) +def test_func(arg1, arg2): + assert arg2 == 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', pytest.lazy_fixtures('one'))]) +def test_func(arg1, arg2): + assert arg2 == 1 +``` + +`lf` can be used with any data structures. For example, in the following example, `lf` is used in the dictionary: + +```python +import pytest +from pytest_lazy_fixtures import lf + +@pytest.fixture() +def one(): + return 1 + +@pytest.mark.parametrize('arg1,arg2', [('val1', {"value": lf('one')})]) +def test_func(arg1, arg2): + assert arg2 == {"value": 1} +``` + +You can also specify getting an attribute through a dot: + +```python +import pytest +from pytest_lazy_fixtures import lf + +class Entity: + def __init__(self, value): + self.value = value + +@pytest.fixture() +def one(): + return Entity(1) + +@pytest.mark.parametrize('arg1,arg2', [('val1', lf('one.value'))]) +def test_func(arg1, arg2): + assert arg2 == 1 +``` + +And there is some useful wrapper called `lfc` (`lazy_fixture_callable`) or `pytest.lazy_fixtures_callable`. +It can work with any callable and your fixtures, e.g. + +```python +import pytest +from pytest_lazy_fixtures import lf, lfc + +class Entity: + def __init__(self, value): + self.value = value + + def __str__(self) -> str: + return str(self.value) + + def sum(self, value: int) -> int: + return self.value + value + +@pytest.fixture() +def entity(): + return Entity(1) + +@pytest.fixture() +def two(): + return 2 + +@pytest.fixture() +def three(): + return 3 + +@pytest.fixture() +def entity_format(): + def _entity_format(entity: Entity): + return {"value": entity.value} + + return _entity_format + +@pytest.mark.parametrize( + "message", + [ + lfc( + "There is two lazy fixture args, {} and {}! And one kwarg {field}! And also simple value {simple}".format, + lf("entity"), + lf("two"), + field=lf("three"), + simple="value", + ), + ], +) +def test_lazy_fixture_callable_with_func(message): + assert message == "There is two lazy fixture args, 1 and 2! And one kwarg 3! And also simple value value" + + +@pytest.mark.parametrize("formatted", [lfc("entity_format", lf("entity"))]) +def test_lazy_fixture_callable_with_lf(formatted, entity): + assert formatted == {"value": entity.value} + + +@pytest.mark.parametrize("result", [lfc("entity.sum", lf("two"))]) +def test_lazy_fixture_callable_with_attr_lf(result): + assert result == 3 +``` + +## Contributing + +Contributions are very welcome. Tests can be run with `pytest`. + +## License + +Distributed under the terms of the `MIT` license, `pytest-lazy-fixtures` is free and open source software + +## Issues + +If you encounter any problems, please file an issue along with a detailed description. diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/__init__.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/__init__.py new file mode 100644 index 0000000000..0d4dd7a8fd --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/__init__.py @@ -0,0 +1,4 @@ +from .lazy_fixture import lf +from .lazy_fixture_callable import lfc + +__all__ = ("lf", "lfc") diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture.py new file mode 100644 index 0000000000..28fc00d529 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture.py @@ -0,0 +1,34 @@ +from dataclasses import dataclass +from operator import attrgetter +from typing import Optional + +import pytest + + +@dataclass +class LazyFixtureWrapper: + name: str + + @property + def fixture_name(self) -> str: + return self.name.split(".", maxsplit=1)[0] + + def _get_attr(self, fixture) -> Optional[str]: + splitted = self.name.split(".", maxsplit=1) + if len(splitted) == 1: + return fixture + return attrgetter(splitted[1])(fixture) + + def __repr__(self) -> str: + return self.name + + def load_fixture(self, request: pytest.FixtureRequest): + return self._get_attr(request.getfixturevalue(self.fixture_name)) + + def __hash__(self) -> int: + return hash(self.name) + + +def lf(name: str) -> LazyFixtureWrapper: + """lf is a lazy fixture.""" + return LazyFixtureWrapper(name) diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture_callable.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture_callable.py new file mode 100644 index 0000000000..a11b04091d --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/lazy_fixture_callable.py @@ -0,0 +1,33 @@ +from typing import Callable, Optional, Union + +import pytest + +from .lazy_fixture import LazyFixtureWrapper + + +class LazyFixtureCallableWrapper(LazyFixtureWrapper): + _func: Optional[Callable] + args: tuple + kwargs: dict + + def __init__(self, func_or_name: Union[Callable, str], *args, **kwargs): + if callable(func_or_name): + self._func = func_or_name + self.name = func_or_name.__name__ + else: + self.name = func_or_name + self._func = None + self.args = args + self.kwargs = kwargs + + def get_func(self, request: pytest.FixtureRequest) -> Callable: + func = self._func + if func is None: + func = self.load_fixture(request) + assert callable(func) + return func + + +def lfc(name: Union[Callable, str], *args, **kwargs) -> LazyFixtureCallableWrapper: + """lfc is a lazy fixture callable.""" + return LazyFixtureCallableWrapper(name, *args, **kwargs) diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/loader.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/loader.py new file mode 100644 index 0000000000..3c675c2aaf --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/loader.py @@ -0,0 +1,21 @@ +import pytest + +from .lazy_fixture import LazyFixtureWrapper +from .lazy_fixture_callable import LazyFixtureCallableWrapper + + +def load_lazy_fixtures(value, request: pytest.FixtureRequest): + if isinstance(value, LazyFixtureCallableWrapper): + return value.get_func(request)( + *load_lazy_fixtures(value.args, request), + **load_lazy_fixtures(value.kwargs, request), + ) + if isinstance(value, LazyFixtureWrapper): + return value.load_fixture(request) + # we need to check exact type + if type(value) is dict: # noqa: E721 + return {key: load_lazy_fixtures(value, request) for key, value in value.items()} + # we need to check exact type + elif type(value) in {list, tuple, set}: + return type(value)([load_lazy_fixtures(value, request) for value in value]) + return value diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/normalizer.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/normalizer.py new file mode 100644 index 0000000000..fdbfe86402 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/normalizer.py @@ -0,0 +1,85 @@ +import copy +from typing import Any, Dict, List, Tuple + +import pytest + +from .lazy_fixture import LazyFixtureWrapper +from .lazy_fixture_callable import LazyFixtureCallableWrapper + + +def _get_fixturenames_closure_and_arg2fixturedefs(fm, metafunc, value) -> Tuple[List[str], Dict[str, Any]]: + if isinstance(value, LazyFixtureCallableWrapper): + extra_fixturenames_args, arg2fixturedefs_args = _get_fixturenames_closure_and_arg2fixturedefs( + fm, + metafunc, + value.args, + ) + extra_fixturenames_kwargs, arg2fixturedefs_kwargs = _get_fixturenames_closure_and_arg2fixturedefs( + fm, + metafunc, + value.kwargs, + ) + return [*extra_fixturenames_args, *extra_fixturenames_kwargs], { + **arg2fixturedefs_args, + **arg2fixturedefs_kwargs, + } + if isinstance(value, LazyFixtureWrapper): + if pytest.version_tuple >= (8, 0, 0): + fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure(metafunc.definition.parent, [value.name], {}) + else: # pragma: no cover + # TODO: add tox + _, fixturenames_closure, arg2fixturedefs = fm.getfixtureclosure([value.name], metafunc.definition.parent) + + return fixturenames_closure, arg2fixturedefs + extra_fixturenames, arg2fixturedefs = [], {} + # we need to check exact type + if type(value) is dict: # noqa: E721 + value = list(value.values()) + # we need to check exact type + if type(value) in {list, tuple, set}: + for val in value: + ef, arg2f = _get_fixturenames_closure_and_arg2fixturedefs(fm, metafunc, val) + extra_fixturenames.extend(ef) + arg2fixturedefs.update(arg2f) + return extra_fixturenames, arg2fixturedefs + + +def normalize_metafunc_calls(metafunc, used_keys=None): + newcalls = [] + for callspec in metafunc._calls: + calls = _normalize_call(callspec, metafunc, used_keys) + newcalls.extend(calls) + metafunc._calls = newcalls + + +def _copy_metafunc(metafunc): + copied = copy.copy(metafunc) + copied.fixturenames = copy.copy(metafunc.fixturenames) + copied._calls = [] + copied._arg2fixturedefs = copy.copy(metafunc._arg2fixturedefs) + return copied + + +def _normalize_call(callspec, metafunc, used_keys): + fm = metafunc.config.pluginmanager.get_plugin("funcmanage") + + used_keys = used_keys or set() + valtype_keys = callspec.params.keys() - used_keys + + for arg in valtype_keys: + value = callspec.params[arg] + fixturenames_closure, arg2fixturedefs = _get_fixturenames_closure_and_arg2fixturedefs(fm, metafunc, value) + + if fixturenames_closure and arg2fixturedefs: + extra_fixturenames = [fname for fname in fixturenames_closure if fname not in callspec.params] + + newmetafunc = _copy_metafunc(metafunc) + newmetafunc.fixturenames = extra_fixturenames + newmetafunc._arg2fixturedefs.update(arg2fixturedefs) + newmetafunc._calls = [callspec] + fm.pytest_generate_tests(newmetafunc) + normalize_metafunc_calls(newmetafunc, used_keys | {arg}) + return newmetafunc._calls + + used_keys.add(arg) + return [callspec] diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/plugin.py b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/plugin.py new file mode 100644 index 0000000000..de7d75a4f7 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/plugin.py @@ -0,0 +1,30 @@ +import pytest + +from .lazy_fixture import LazyFixtureWrapper, lf +from .lazy_fixture_callable import lfc +from .loader import load_lazy_fixtures +from .normalizer import normalize_metafunc_calls + + +def pytest_configure(): + pytest.lazy_fixtures = lf + pytest.lazy_fixtures_callable = lfc + + +@pytest.hookimpl(tryfirst=True) +def pytest_fixture_setup(fixturedef, request): + val = getattr(request, "param", None) + if val is not None: + request.param = load_lazy_fixtures(val, request) + + +def pytest_make_parametrize_id(config, val, argname): + if isinstance(val, LazyFixtureWrapper): + return val.name + + +@pytest.hookimpl(hookwrapper=True) +def pytest_generate_tests(metafunc): + yield + + normalize_metafunc_calls(metafunc) diff --git a/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/py.typed b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/py.typed new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/pytest_lazy_fixtures/py.typed diff --git a/contrib/python/pytest-lazy-fixtures/ya.make b/contrib/python/pytest-lazy-fixtures/ya.make new file mode 100644 index 0000000000..b672f39330 --- /dev/null +++ b/contrib/python/pytest-lazy-fixtures/ya.make @@ -0,0 +1,32 @@ +# Generated by devtools/yamaker (pypi). + +PY3_LIBRARY() + +VERSION(1.0.7) + +LICENSE(MIT) + +PEERDIR( + contrib/python/pytest +) + +NO_LINT() + +PY_SRCS( + TOP_LEVEL + pytest_lazy_fixtures/__init__.py + pytest_lazy_fixtures/lazy_fixture.py + pytest_lazy_fixtures/lazy_fixture_callable.py + pytest_lazy_fixtures/loader.py + pytest_lazy_fixtures/normalizer.py + pytest_lazy_fixtures/plugin.py +) + +RESOURCE_FILES( + PREFIX contrib/python/pytest-lazy-fixtures/ + .dist-info/METADATA + .dist-info/entry_points.txt + pytest_lazy_fixtures/py.typed +) + +END() |