Skip to content

Commit b89336c

Browse files
committed
initial commit
1 parent e4a7dd0 commit b89336c

13 files changed

+295
-1
lines changed

.github/workflows/tests.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Tests
2+
on: push
3+
jobs:
4+
tests:
5+
runs-on: ubuntu-latest
6+
strategy:
7+
matrix:
8+
python-version: ['3.7', '3.8', '3.9', '3.10']
9+
name: Python ${{ matrix.python-version }}
10+
steps:
11+
- uses: actions/checkout@v2
12+
- uses: actions/setup-python@v2
13+
with:
14+
python-version: ${{ matrix.python-version }}
15+
- run: pip install -U nox pip wheel
16+
- run: nox

.pre-commit-config.yaml

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
repos:
2+
- hooks:
3+
- id: black
4+
language_version: python3
5+
repo: https://github.com/ambv/black
6+
rev: 21.11b1
7+
- repo: https://github.com/pre-commit/pre-commit-hooks
8+
rev: v4.0.1
9+
hooks:
10+
- id: check-toml
11+
- id: check-yaml
12+
- id: end-of-file-fixer
13+
- id: trailing-whitespace
14+
- hooks:
15+
- id: codespell
16+
args:
17+
- --ignore-words-list=cachable
18+
repo: https://github.com/codespell-project/codespell
19+
rev: v2.1.0
20+
- hooks:
21+
- id: isort
22+
language_version: python3
23+
repo: https://github.com/timothycrosley/isort
24+
rev: 5.10.1
25+
- hooks:
26+
- id: flake8
27+
language_version: python3
28+
additional_dependencies:
29+
- flake8-bugbear
30+
- flake8-comprehensions
31+
- flake8-debugger
32+
- flake8-string-format
33+
- flake8-bandit
34+
repo: https://gitlab.com/pycqa/flake8
35+
rev: 3.9.2

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# pytest-test-utils
1+
# pytest-test-utils

noxfile.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Automation using nox.
2+
"""
3+
import nox
4+
5+
nox.options.reuse_existing_virtualenvs = True
6+
nox.options.sessions = "lint", "tests"
7+
locations = "pytest_test_utils", "tests.py"
8+
9+
10+
@nox.session(python=["3.7", "3.8", "3.9", "3.10"])
11+
def tests(session: nox.Session) -> None:
12+
session.install("-e", ".[dev]")
13+
session.run("pytest")
14+
15+
16+
@nox.session
17+
def lint(session: nox.Session) -> None:
18+
session.install("pre-commit")
19+
session.install("-e", ".[dev]")
20+
21+
if session.posargs:
22+
args = session.posargs + ["--all-files"]
23+
else:
24+
args = ["--all-files", "--show-diff-on-failure"]
25+
26+
session.run("pre-commit", "run", *args)
27+
session.run("python", "-m", "mypy")
28+
session.run("python", "-m", "pylint", *locations)

pyproject.toml

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[build-system]
2+
requires = ["setuptools>=48", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[tool.black]
6+
line-length = 79
7+
include = '\.pyi?$'
8+
exclude = '''
9+
/(
10+
\.eggs
11+
| \.git
12+
| \.hg
13+
| \.mypy_cache
14+
| \.tox
15+
| \.venv
16+
| _build
17+
| buck-out
18+
| build
19+
| dist
20+
)/
21+
'''
22+
23+
[tool.isort]
24+
profile = "black"
25+
line_length = 79
26+
27+
[tool.mypy]
28+
# Error output
29+
show_column_numbers = true
30+
show_error_codes = true
31+
show_error_context = true
32+
show_traceback = true
33+
pretty = true
34+
check_untyped_defs = true
35+
strict = true
36+
warn_no_return = true
37+
warn_redundant_casts = true
38+
warn_unreachable = true
39+
files = ["pytest_test_utils"]
40+
41+
[tool.pylint.message_control]
42+
enable = ["no-else-return"]
43+
disable = ["missing-function-docstring", "missing-module-docstring", "missing-class-docstring"]
44+
45+
[tool.pytest.ini_options]
46+
testpaths = ["./tests.py"]

pytest_test_utils/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from ._any import ANY
2+
from .tmp_dir import TmpDir
3+
4+
__all__ = ["ANY", "TmpDir"]

pytest_test_utils/_any.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import Any, Type
2+
3+
4+
class ANY:
5+
def __init__(self, expected_type: Type[object]) -> None:
6+
self.expected_type: Type[object] = expected_type
7+
8+
def __repr__(self) -> str:
9+
return "Any({self.expected_type.__name__})"
10+
11+
def __eq__(self, other: Any) -> bool:
12+
return isinstance(other, self.expected_type)

pytest_test_utils/conftest.py

Whitespace-only changes.

pytest_test_utils/pytest_plugin.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from pathlib import Path
2+
from typing import Iterator
3+
4+
import pytest
5+
6+
from .tmp_dir import TmpDir
7+
8+
9+
@pytest.fixture
10+
def tmp_dir(
11+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
12+
) -> Iterator[TmpDir]:
13+
tmp = TmpDir(tmp_path)
14+
monkeypatch.chdir(tmp)
15+
yield tmp

pytest_test_utils/tmp_dir.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import os
2+
from contextlib import contextmanager
3+
from pathlib import Path
4+
5+
6+
class TmpDir(type(Path())): # type: ignore
7+
def gen(self, struct, text=""):
8+
if isinstance(struct, (str, bytes, os.PathLike)):
9+
struct = {struct: text}
10+
for name, contents in struct.items():
11+
path = self / os.fsdecode(name)
12+
13+
if isinstance(contents, dict):
14+
if not contents:
15+
path.mkdir(parents=True, exist_ok=True)
16+
else:
17+
path.gen(contents)
18+
else:
19+
path.parent.mkdir(parents=True, exist_ok=True)
20+
if isinstance(contents, bytes):
21+
path.write_bytes(contents)
22+
else:
23+
path.write_text(contents, encoding="utf-8")
24+
25+
return list(struct.keys())
26+
27+
@contextmanager
28+
def chdir(self):
29+
old = os.getcwd()
30+
try:
31+
os.chdir(self)
32+
yield
33+
finally:
34+
os.chdir(old)

pytest_test_utils/tmp_dir.pyi

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import os
2+
from pathlib import Path
3+
from typing import Any, Dict, List, TypeVar, Union, overload
4+
5+
T = TypeVar("T", str, bytes)
6+
Text = Union[str, bytes]
7+
AnyPath = Union[T, os.PathLike[T]]
8+
AnyStruct = Dict[AnyPath[T], Union[Text, Dict[AnyPath[T], Any]]]
9+
StrStruct = AnyStruct[str]
10+
BytesStruct = AnyStruct[bytes]
11+
12+
class TmpDir(Path):
13+
@overload
14+
def gen(self, struct: AnyPath[T], text: Text = "") -> List[T]: ...
15+
@overload
16+
def gen(self, struct: BytesStruct, text: Text = "") -> List[bytes]: ...
17+
@overload
18+
def gen(self, struct: StrStruct, text: Text = "") -> List[str]: ...

setup.cfg

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
[metadata]
2+
name = pytest_test_utils
3+
version = 0.0.1
4+
license = Apache License 2.0
5+
license_file = LICENSE
6+
url = https://github.com/iterative/pytest-test-utils
7+
platforms=any
8+
authors = Saugat Pachhai
9+
maintainer_email = [email protected]
10+
classifiers =
11+
Programming Language :: Python :: 3
12+
Programming Language :: Python :: 3.7
13+
Programming Language :: Python :: 3.8
14+
Programming Language :: Python :: 3.9
15+
Programming Language :: Python :: 3.10
16+
Framework :: Pytest
17+
18+
[options]
19+
python_requires = >=3.7
20+
zip_safe = False
21+
packages = find:
22+
install_requires=
23+
pytest
24+
25+
[options.extras_require]
26+
dev =
27+
pytest==6.2.5
28+
pytest-sugar==0.9.4
29+
pylint==2.11.1
30+
mypy==0.910
31+
32+
[options.entry_points]
33+
pytest11 =
34+
pytest_test_utils = pytest_test_utils.pytest_plugin
35+
36+
[flake8]
37+
ignore=
38+
E203, # Whitespace before ':'
39+
E266, # Too many leading '#' for block comment
40+
W503, # Line break occurred before a binary operator
41+
P1, # unindexed parameters in the str.format, see:
42+
# https://pypi.org/project/flake8-string-format/
43+
max_line_length = 79
44+
max-complexity = 15
45+
select = B,C,E,F,W,T4,B902,T,P
46+
show_source = true
47+
count = true

tests.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
3+
from pytest_test_utils import TmpDir
4+
5+
6+
def test_is_tmp_dir(tmp_dir: TmpDir) -> None:
7+
assert isinstance(tmp_dir, TmpDir)
8+
9+
10+
def test_gen_str(tmp_dir: TmpDir) -> None:
11+
assert tmp_dir.gen("foo", "lorem ipsum") == ["foo"]
12+
assert (tmp_dir / "foo").read_text() == "lorem ipsum"
13+
14+
15+
def test_gen_bytes(tmp_dir: TmpDir) -> None:
16+
assert tmp_dir.gen(b"foo", "lorem ipsum") == [b"foo"]
17+
assert (tmp_dir / os.fsdecode(b"foo")).read_bytes() == b"lorem ipsum"
18+
19+
20+
def test_gen_dict_str(tmp_dir: TmpDir) -> None:
21+
assert tmp_dir.gen({"file": "lorem", "dir": {"file": "ipsum"}}) == [
22+
"file",
23+
"dir",
24+
]
25+
assert (tmp_dir / "file").read_text() == "lorem"
26+
assert (tmp_dir / "dir").is_dir()
27+
assert (tmp_dir / "dir" / "file").read_text() == "ipsum"
28+
29+
30+
def test_gen_dict_bytes(tmp_dir: TmpDir) -> None:
31+
assert tmp_dir.gen({b"file": b"lorem", b"dir": {b"file": b"ipsum"}}) == [
32+
b"file",
33+
b"dir",
34+
]
35+
assert (tmp_dir / os.fsdecode("file")).read_bytes() == b"lorem"
36+
assert (tmp_dir / os.fsdecode("dir")).is_dir()
37+
assert (
38+
tmp_dir / os.fsdecode("dir") / os.fsdecode("file")
39+
).read_bytes() == b"ipsum"

0 commit comments

Comments
 (0)