diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..9b32546 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @gaborbernat diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1230149 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..14f86b3 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,105 @@ +name: check +on: + push: + pull_request: + schedule: + - cron: "0 8 * * *" + +concurrency: + group: check-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: test ${{ matrix.py }} - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + py: + - "3.11.0-rc.2" + - "3.10" + - "3.9" + - "3.8" + - "3.7" + os: + - ubuntu-22.04 + + steps: + - name: Setup python for tox + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install tox + run: python -m pip install tox + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup python for test ${{ matrix.py }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.py }} + - name: Pick environment to run + run: | + import codecs; import os; import sys + env = "TOXENV=py{}{}\n".format(*sys.version_info[0:2]) + print("Picked:\n{}for{}".format(env, sys.version)) + with codecs.open(os.environ["GITHUB_ENV"], "a", "utf-8") as file_handler: + file_handler.write(env) + shell: python + - name: Setup test suite + run: tox -vv --notest + - name: Run test suite + run: tox --skip-pkg-install + env: + CI_RUN: "yes" + + check: + name: ${{ matrix.tox_env }} - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-22.04 + tox_env: + - dev + - type + - readme + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Python "3.10" + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install tox + run: python -m pip install tox + - name: Setup test suite + run: tox -vv --notest -e ${{ matrix.tox_env }} + - name: Run test suite + run: tox --skip-pkg-install -e ${{ matrix.tox_env }} + + publish: + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + needs: [ test, check ] + runs-on: ubuntu-latest + steps: + - name: Setup python to build package + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install build + run: python -m pip install build + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Build sdist and wheel + run: python -m build -s -w . -o dist + - name: Publish to PyPi + uses: pypa/gh-action-pypi-publish@v1.5.1 + with: + skip_existing: true + user: __token__ + password: ${{ secrets.pypi_password }} diff --git a/.gitignore b/.gitignore index db4561e..f7b9fb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,5 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ +*.pyc +*.egg-info dist/ -downloads/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ .tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ +/src/pytest_env/version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..55b82a8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,53 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-ast + - id: check-builtin-literals + - id: check-docstring-first + - id: check-merge-conflict + - id: check-yaml + - id: check-toml + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/asottile/pyupgrade + rev: v3.0.0 + hooks: + - id: pyupgrade + args: [ "--py37-plus" ] + - repo: https://github.com/PyCQA/isort + rev: 5.10.1 + hooks: + - id: isort + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + args: [ --safe ] + - repo: https://github.com/asottile/blacken-docs + rev: v1.12.1 + hooks: + - id: blacken-docs + additional_dependencies: [ black==22.10 ] + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: rst-backticks + - repo: https://github.com/tox-dev/tox-ini-fmt + rev: "0.5.2" + hooks: + - id: tox-ini-fmt + args: [ "-p", "fix" ] + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 + hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear==22.9.23 + - flake8-comprehensions==3.10 + - flake8-pytest-style==1.6 + - flake8-spellcheck==0.28 + - flake8-unused-arguments==0.0.11 + - flake8-noqa==1.2.9 + - pep8-naming==0.13.2 diff --git a/LICENSE b/LICENSE index 7aa3caf..250e9a0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,6 @@ -The MIT License (MIT) +MIT License + +Copyright (c) 2010-202x The pytest-env developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -16,4 +18,4 @@ 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. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index fa15c94..3b3ae40 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,47 @@ # pytest-env +[![PyPI](https://img.shields.io/pypi/v/pytest-env?style=flat-square)](https://pypi.org/project/pytest-env/) +[![Supported Python +versions](https://img.shields.io/pypi/pyversions/pytest-env.svg)](https://pypi.org/project/pytest-env/) +[![check](https://github.com/pytest-dev/pytest-env/actions/workflows/check.yml/badge.svg)](https://github.com/pytest-dev/pytest-env/actions/workflows/check.yml) +[![Code style: +black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Downloads](https://pepy.tech/badge/pytest-env/month)](https://pepy.tech/project/pytest-env/month) + This is a py.test plugin that enables you to set environment variables in the pytest.ini file. ## Installation Install with pip: - pip install pytest-env - -Uninstall with pip: - - pip uninstall pytest-env +```shell +pip install pytest-env +``` ## Usage In your pytest.ini file add a key value pair with `env` as the key and the environment variables as a line separated list of `KEY=VALUE` entries. The defined variables will be added to the environment before any tests are run: - [pytest] - env = - HOME=~/tmp - RUN_ENV=test +```ini +[pytest] +env = + HOME=~/tmp + RUN_ENV=test +``` You can use `D:` (default) as prefix if you don't want to override existing environment variables: - [pytest] - env = - D:HOME=~/tmp - D:RUN_ENV=test +```ini +[pytest] +env = + D:HOME=~/tmp + D:RUN_ENV=test +``` Lastly, you can use existing environment variables using a python-like format: - [pytest] - env = - RUN_PATH=/run/path/{USER} +```ini +[pytest] +env = + RUN_PATH=/run/path/{USER} +``` diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b67aad2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,66 @@ +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling>=1.11", "hatch-vcs>=0.2"] + +[project] +name = "pytest-env" +description = "py.test plugin that allows you to add environment variables." +readme = "README.md" +license.file = "LICENSE" +maintainers = [{ name = "Bernát Gábor", email = "gaborjbernat@gmail.com" }] +urls.Documentation = "https://bump-deps-index.readthedocs.io" +urls.Homepage = "https://github.com/pytest-dev/pytest-env" +urls.Source = "https://github.com/pytest-dev/pytest-env" +urls.Tracker = "https://github.com/pytest-dev/pytest-env/issues" +requires-python = ">=3.7" +dependencies = ["pytest>=7.1.3"] +optional-dependencies.test = ["coverage>=6.5", "pytest-mock>=3.10", "covdefaults>=2.2"] +keywords = ["pytest", "env"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dynamic = ["version"] + +[project.entry-points.pytest11] +env = "pytest_env.plugin" + +[tool.hatch] +build.hooks.vcs.version-file = "src/pytest_env/version.py" +version.source = "vcs" + +[tool.black] +line-length = 120 + +[tool.coverage] +source = ["${_COVERAGE_SRC}", "${_COVERAGE_TEST}"] +run.dynamic_context = "test_function" +run.plugins = ["covdefaults"] +run.parallel = true +report.fail_under = 100 +html.show_contexts = true +html.skip_covered = false +paths.source = [ + "src", + ".tox*/*/lib/python*/site-packages", + ".tox*/pypy*/site-packages", + ".tox*\\*\\Lib\\site-packages", + "*/src", + "*\\src", +] + +[tool.mypy] +python_version = "3.10" +show_error_codes = true +strict = true + +[tool.isort] +profile = "black" +known_first_party = ["pytest_env"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 213bb9e..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -"""pytest-env setup module.""" - -from setuptools import setup - -DESCRIPTION = 'py.test plugin that allows you to add environment variables.' - -setup( - name='pytest-env', - description=DESCRIPTION, - long_description=DESCRIPTION, - version='0.6.2', - author='dev@mobile-dynasty.com', - author_email='dev@mobile-dynasty.com', - url='https://github.com/MobileDynasty/pytest-env', - packages=['pytest_env'], - entry_points={'pytest11': ['env = pytest_env.plugin']}, - install_requires=['pytest>=2.6.0'], - classifiers=[ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - ] -) diff --git a/src/pytest_env/__init__.py b/src/pytest_env/__init__.py new file mode 100644 index 0000000..b169ad3 --- /dev/null +++ b/src/pytest_env/__init__.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from .version import __version__ + +__all__ = [ + "__version__", +] diff --git a/pytest_env/plugin.py b/src/pytest_env/plugin.py similarity index 65% rename from pytest_env/plugin.py rename to src/pytest_env/plugin.py index 05ce517..30ed1af 100644 --- a/pytest_env/plugin.py +++ b/src/pytest_env/plugin.py @@ -1,26 +1,22 @@ """Adopt environment section in pytest configuration files.""" +from __future__ import annotations import os + import pytest -def pytest_addoption(parser): +def pytest_addoption(parser: pytest.Parser) -> None: """Add section to configuration files.""" - help_msg = ( - "a line separated list of environment variables " - "of the form NAME=VALUE." - ) + help_msg = "a line separated list of environment variables " "of the form NAME=VALUE." - parser.addini( - "env", - type="linelist", - help=help_msg, - default=[] - ) + parser.addini("env", type="linelist", help=help_msg, default=[]) -@pytest.hookimpl(tryfirst=True) -def pytest_load_initial_conftests(args, early_config, parser): +@pytest.hookimpl(tryfirst=True) # type: ignore # untyped decorator +def pytest_load_initial_conftests( + args: list[str], early_config: pytest.Config, parser: pytest.Parser # noqa: U100 +) -> None: """Load environment variables from configuration files.""" for e in early_config.getini("env"): part = e.partition("=") diff --git a/pytest_env/__init__.py b/src/pytest_env/py.typed similarity index 100% rename from pytest_env/__init__.py rename to src/pytest_env/py.typed diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..fd9d35f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,3 @@ +from __future__ import annotations + +pytest_plugins = ["pytester"] diff --git a/tests/example.py b/tests/example.py new file mode 100644 index 0000000..c95eca8 --- /dev/null +++ b/tests/example.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +import os + + +def test_works() -> None: + assert "MAGIC" not in os.environ diff --git a/tests/test_env.py b/tests/test_env.py new file mode 100644 index 0000000..802f098 --- /dev/null +++ b/tests/test_env.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from pathlib import Path +from shutil import copy2 + +import pytest + +_EXAMPLE = Path(__file__).parent / "example.py" + + +@pytest.fixture() +def example(testdir: pytest.Testdir) -> pytest.Testdir: + dest = Path(str(testdir.tmpdir / "test_example.py")) + # dest.symlink_to(_EXAMPLE) # for local debugging use this + copy2(str(_EXAMPLE), str(dest)) + return testdir + + +def test_simple(example: pytest.Testdir) -> None: + result = example.runpytest() + result.assert_outcomes(passed=1) diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..4c9e416 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,7 @@ +from __future__ import annotations + + +def test_version() -> None: + import pytest_env + + assert pytest_env.__version__ is not None diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..89d94f3 --- /dev/null +++ b/tox.ini @@ -0,0 +1,79 @@ +[tox] +envlist = + fix + py311 + py310 + py39 + py38 + py37 + type + readme +isolated_build = true +skip_missing_interpreters = true +minversion = 3.21 + +[testenv] +description = run the tests with pytest +setenv = + COVERAGE_FILE = {env:COVERAGE_FILE:{toxworkdir}{/}.coverage.{envname}} + _COVERAGE_SRC = {envsitepackagesdir}{/}pytest_env + _COVERAGE_TEST = {toxinidir}{/}tests +extras = + test +commands = + coverage erase + coverage run -m pytest {tty:--color=yes} \ + --junitxml {toxworkdir}{/}junit.{envname}.xml \ + {posargs:tests} + coverage combine + coverage report + coverage html -d {envtmpdir}{/}htmlcov +package = wheel +wheel_build_env = .pkg + +[testenv:fix] +description = run static analysis and style check using flake8 +passenv = + HOMEPATH + PROGRAMDATA +skip_install = true +deps = + pre-commit>=2.20 +commands = + pre-commit run --all-files --show-diff-on-failure + +[testenv:type] +description = run type check on code base +setenv = + {tty:MYPY_FORCE_COLOR = 1} +deps = + mypy==0.982 +commands = + mypy --strict src + mypy --strict tests + +[testenv:readme] +description = check that the long description is valid +skip_install = true +deps = + build[virtualenv]>=0.8 + twine>=4.0.1 +changedir = {toxinidir} +commands = + python -m build -o {envtmpdir} . + twine check {envtmpdir}/* + +[testenv:dev] +description = generate a DEV environment +usedevelop = true +extras = + test +commands = + python -m pip list --format=columns + python -c 'import sys; print(sys.executable)' + +[flake8] +max-complexity = 22 +max-line-length = 120 +noqa-require-code = true +dictionaries = en_US,python,technical,django diff --git a/whitelist.txt b/whitelist.txt new file mode 100644 index 0000000..61d044e --- /dev/null +++ b/whitelist.txt @@ -0,0 +1,13 @@ +addini +addoption +conftests +copy2 +dkey +getini +hookimpl +repo +runpytest +testdir +tmpdir +tryfirst +untyped