diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 915c72a..dec88be 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,11 +10,9 @@ jobs: id-token: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: wntrblm/nox@2023.04.22 with: - python-version: '3.10' - - run: pip install -U nox + python-versions: "3.12" - run: nox -s build - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 704517f..de80d78 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -5,17 +5,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: wntrblm/nox@2023.04.22 with: - python-version: | - 3.8 - 3.9 - 3.10 - 3.11 - 3.12 - pypy3.8 - pypy3.9 - 3.7 - - run: pip install -U nox + python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12" - run: nox - run: nox -s build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5fe7cbd..3d5d833 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,4 @@ repos: - - hooks: - - id: black - language_version: python3 - repo: https://github.com/psf/black - rev: 22.6.0 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: @@ -11,24 +6,18 @@ repos: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - - hooks: + - repo: https://github.com/tox-dev/pyproject-fmt + rev: "1.7.0" + hooks: + - id: pyproject-fmt + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.0 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + - repo: https://github.com/codespell-project/codespell + rev: v2.2.2 + hooks: - id: codespell additional_dependencies: ["tomli"] - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 - - hooks: - - id: isort - language_version: python3 - repo: https://github.com/timothycrosley/isort - rev: 5.10.1 - - hooks: - - id: flake8 - language_version: python3 - additional_dependencies: - - flake8-bugbear - - flake8-comprehensions - - flake8-debugger - - flake8-string-format - - flake8-bandit - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 diff --git a/noxfile.py b/noxfile.py index 5f0f1a8..bb60a01 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,18 +1,15 @@ -"""Automation using nox. -""" +"""Automation using nox.""" + import glob -import sys import nox nox.options.reuse_existing_virtualenvs = True -nox.options.sessions = "lint", "tests", "compat" +nox.options.sessions = "lint", "tests" locations = "pytest_test_utils", "tests.py" -@nox.session( - python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.8", "pypy3.9"] -) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]) def tests(session: nox.Session) -> None: session.install(".[tests]") # `pytest --cov` will start coverage after pytest @@ -21,29 +18,18 @@ def tests(session: nox.Session) -> None: session.run("coverage", "report", "--show-missing", "--skip-covered") -@nox.session(python=["3.7", "3.8"]) -@nox.parametrize("pytest", ["3.9.1", "4.0", "5.0", "6.0", "7.0"]) -def compat(session: nox.Session, pytest: str) -> None: - session.install(".[tests]") - session.install(f"pytest=={pytest}") - session.run("coverage", "run", "-m", "pytest", "tests.py") - - @nox.session def lint(session: nox.Session) -> None: session.install("pre-commit") session.install("-e", ".[dev]") if session.posargs: - args = session.posargs + ["--all-files"] + args = [*session.posargs, "--all-files"] else: args = ["--all-files", "--show-diff-on-failure"] session.run("pre-commit", "run", *args) session.run("python", "-m", "mypy") - if sys.version_info >= (3, 11): - return - session.run("python", "-m", "pylint", *locations) @nox.session diff --git a/pyproject.toml b/pyproject.toml index 4eb1099..a8df783 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,30 +1,64 @@ [build-system] -requires = ["setuptools>=48", "setuptools_scm[toml]>=6.3.1"] build-backend = "setuptools.build_meta" +requires = [ + "setuptools>=48", + "setuptools_scm[toml]>=6.3.1", +] + +[project] +name = "pytest-test-utils" +readme = "README.md" +license = { text = "Apache License 2.0" } +authors = [{ name = "Saugat Pachhai", email = "support@dvc.org" }] +requires-python = ">=3.7" +classifiers = [ + "Framework :: Pytest", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] +dynamic = [ + "version", +] +dependencies = [ + "pytest>=3.9", +] +[project.optional-dependencies] +dev = [ + "mypy", + "pytest-test-utils[tests]", +] +tests = [ + "coverage>=6", +] +[project.urls] +Issues = "https://github.com/iterative/pytest-test-utils/issues" +Source = "https://github.com/iterative/pytest-test-utils" +[project.entry-points.pytest11] +pytest_test_utils = "pytest_test_utils.pytest_plugin" + +[tool.setuptools.package-data] +pytest_test_utils = ["py.typed"] [tool.setuptools_scm] -[tool.black] -line-length = 79 -include = '\.pyi?$' -exclude = ''' -/( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist -)/ -''' - -[tool.isort] -profile = "black" -line_length = 79 +[tool.ruff] +output-format = "full" +show-fixes = true + +[tool.ruff.lint] +ignore = ["S101"] +extend-select = ["S", "I", "B", "C4", "T10"] + +[tool.codespell] +ignore-words-list = "cachable" + +[tool.pytest.ini_options] +testpaths = ["tests.py"] [tool.mypy] # Error output @@ -38,18 +72,8 @@ strict = true warn_no_return = true warn_redundant_casts = true warn_unreachable = true -files = ["pytest_test_utils", "tests.py"] +files = ["pytest_test_utils", "tests.py", "noxfile.py"] [[tool.mypy.overrides]] module = ["tests"] strict_equality = false - -[tool.pylint.message_control] -enable = ["no-else-return"] -disable = ["missing-function-docstring", "missing-module-docstring", "missing-class-docstring"] - -[tool.pytest.ini_options] -testpaths = ["tests.py"] - -[tool.codespell] -ignore-words-list = "cachable" diff --git a/pytest_test_utils/_approx.py b/pytest_test_utils/_approx.py index dd3b3e6..b7256df 100644 --- a/pytest_test_utils/_approx.py +++ b/pytest_test_utils/_approx.py @@ -21,9 +21,7 @@ def __init__( """Initialize the approx_datetime with `abs` as tolerance.""" assert isinstance(expected, datetime) abs = abs or self.default_tolerance - assert abs >= timedelta( - 0 - ), f"absolute tolerance can't be negative: {abs}" + assert abs >= timedelta(0), f"absolute tolerance can't be negative: {abs}" super().__init__(expected, abs=abs) def __repr__(self) -> str: # pragma: no cover diff --git a/pytest_test_utils/matchers.py b/pytest_test_utils/matchers.py index 7357f57..19c4ca8 100644 --- a/pytest_test_utils/matchers.py +++ b/pytest_test_utils/matchers.py @@ -28,9 +28,7 @@ def __init__( pattern: Union[AnyStr, Pattern[AnyStr]], flags: Union[int, re.RegexFlag] = 0, ) -> None: - self._regex: Pattern[AnyStr] = re.compile( - pattern, flags # type: ignore[arg-type] - ) + self._regex: Pattern[AnyStr] = re.compile(pattern, flags) # type: ignore[arg-type] def __repr__(self) -> str: flags = self._regex.flags & ~32 # 32 is default @@ -63,9 +61,7 @@ class MatcherDict: # - should not call itself dict or use dict in repr because it creates # confusing error messages (shadowing python builtins is bad anyway) - def __init__( - self, d: Optional[Mapping[Any, Any]] = None, **keys: Any - ) -> None: + def __init__(self, d: Optional[Mapping[Any, Any]] = None, **keys: Any) -> None: self.d: Dict[Any, Any] = {} if d: self.d.update(d) @@ -112,9 +108,7 @@ def __repr__(self) -> str: def __eq__(self, other: Any) -> bool: # Unforturnately this doesn't work with classes with slots # self.__class__ = other.__class__ - return all( - getattr(other, name) == v for name, v in self.attribs.items() - ) + return all(getattr(other, name) == v for name, v in self.attribs.items()) class any_of: diff --git a/pytest_test_utils/tmp_dir_factory.py b/pytest_test_utils/tmp_dir_factory.py index 5dc0152..85875e4 100644 --- a/pytest_test_utils/tmp_dir_factory.py +++ b/pytest_test_utils/tmp_dir_factory.py @@ -11,9 +11,7 @@ def __init__(self, tmp_path_factory: "TempPathFactory") -> None: self.tmp_path_factory: "TempPathFactory" = tmp_path_factory def mktemp(self, basename: str, numbered: bool = True) -> TmpDir: - return TmpDir( - self.tmp_path_factory.mktemp(basename, numbered=numbered) - ) + return TmpDir(self.tmp_path_factory.mktemp(basename, numbered=numbered)) def getbasetemp(self) -> TmpDir: return TmpDir(self.tmp_path_factory.getbasetemp()) diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index c2c6800..0000000 --- a/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -[metadata] -name = pytest_test_utils -long_description = file: README.md -long_description_content_type = text/markdown -license = Apache License 2.0 -license_file = LICENSE -url = https://github.com/iterative/pytest-test-utils -platforms=any -authors = Saugat Pachhai -maintainer_email = support@dvc.org -classifiers = - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Framework :: Pytest - -[options] -python_requires = >=3.7 -zip_safe = False -packages = find: -install_requires= - pytest>=3.9 - -[options.extras_require] -tests = - pytest==8; python_version >= '3.8' - pytest==7; python_version < '3.8' - pytest-sugar - coverage==6 -dev = - %(tests)s - pylint==2.14.3 - mypy==0.961 - -[options.entry_points] -pytest11 = - pytest_test_utils = pytest_test_utils.pytest_plugin - -[options.package_data] -pytest_test_utils = py.typed - -[flake8] -ignore= - E203, # Whitespace before ':' - E266, # Too many leading '#' for block comment - W503, # Line break occurred before a binary operator - P1, # unindexed parameters in the str.format, see: - # https://pypi.org/project/flake8-string-format/ -max_line_length = 79 -max-complexity = 15 -select = B,C,E,F,W,T4,B902,T,P -show_source = true -count = true diff --git a/tests.py b/tests.py index 22bbd73..2da9441 100644 --- a/tests.py +++ b/tests.py @@ -64,9 +64,7 @@ def test_gen_dict_bytes(tmp_dir: TmpDir) -> None: ] assert (tmp_dir / os.fsdecode("file")).read_bytes() == b"lorem" assert (tmp_dir / os.fsdecode("dir")).is_dir() - assert ( - tmp_dir / os.fsdecode("dir") / os.fsdecode("file") - ).read_bytes() == b"ipsum" + assert (tmp_dir / os.fsdecode("dir") / os.fsdecode("file")).read_bytes() == b"ipsum" def test_chdir(tmp_path: Path, tmp_dir: TmpDir) -> None: @@ -113,13 +111,9 @@ def test_matcher_repr(matcher: Type[Matcher]) -> None: == "M.dict(foo='foo', n=123)" ) assert repr(matcher.instance_of(str)) == "instance_of(str)" - assert ( - repr(matcher.instance_of((str, bytes))) == "instance_of((str, bytes))" - ) + assert repr(matcher.instance_of((str, bytes))) == "instance_of((str, bytes))" assert repr(matcher.unordered("foo", "bar")) == "unordered('foo', 'bar')" - assert ( - repr(matcher.re(r"^plots\.csv-\w+$")) == "regex(r'^plots\\.csv-\\w+$')" - ) + assert repr(matcher.re(r"^plots\.csv-\w+$")) == "regex(r'^plots\\.csv-\\w+$')" def test_matcher_dict(matcher: Type[Matcher]) -> None: @@ -152,9 +146,7 @@ def test_matcher_attrs(matcher: Type[Matcher]) -> None: def test_matcher_attrs_nested(matcher: Type[Matcher]) -> None: - obj = SimpleNamespace( - nested=SimpleNamespace(foo="foo", bar="bar"), foobar="bar" - ) + obj = SimpleNamespace(nested=SimpleNamespace(foo="foo", bar="bar"), foobar="bar") assert obj == matcher.attrs(nested=matcher.attrs(foo="foo"))