Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change configuration to use uv & ruff #10

Merged
merged 4 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 22 additions & 19 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Pytest-run-parallel tests
name: Tests

on:
push:
Expand All @@ -11,32 +11,35 @@ on:
jobs:
test:
runs-on: ubuntu-latest
name: Run tests with ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.13t', 'pypy-3.8']
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3

- uses: actions/setup-python@v5
if: ${{ !endsWith(matrix.python-version, 't') && matrix.python-version != '3.13' }}
with:
python-version: ${{ matrix.python-version }}

- name: Setup free-threaded variables
if: ${{ endsWith(matrix.python-version, 't') }}
- name: Install tox
run: |
echo "FREE_THREADED=1" >> "$GITHUB_ENV"
echo "PYTHON_GIL=0" >> "$GITHUB_ENV"
uv tool install \
--python-preference only-managed \
--python 3.13 \
tox \
--with tox-uv \
--with tox-gh

- uses: deadsnakes/action@6c8b9b82fe0b4344f4b98f2775fcc395df45e494 # v3.1.0
if: ${{ endsWith(matrix.python-version, 't') || matrix.python-version == '3.13' }}
with:
python-version: "3.13"
nogil: ${{ env.FREE_THREADED == '1' }}
- name: Install Python
# We've installed Python 3.13 above already
if: matrix.python-version != '3.13'
run: uv python install --python-preference only-managed ${{ matrix.python-version }}

- name: Install tox
run: pip install tox
- name: Setup test suite
run: tox run -vv --notest --skip-missing-interpreters false
env:
TOX_GH_MAJOR_MINOR: ${{ matrix.python-version }}

- name: Test
run: tox -e py
- name: Run test suite
run: tox run --skip-pkg-install
env:
TOX_GH_MAJOR_MINOR: ${{ matrix.python-version }}
51 changes: 48 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ description = "A simple pytest plugin to run tests concurrently"
version = "0.1.1-dev"
readme = "README.rst"
requires-python = ">=3.8"
dependencies = [
"pytest>=6.2.0",
]

authors = [
{ name = "Quansight Labs", email = "[email protected]" },
]
Expand All @@ -29,15 +33,56 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"pytest>=6.2.0",
]

[project.urls]
Repository = "https://github.com/Quansight-Labs/pytest-run-parallel"
[project.entry-points.pytest11]
run-parallel = "pytest_run_parallel.plugin"

[dependency-groups]
dev = [
"pytest-cov>=5.0.0",
"pytest-order>=1.3.0",
"ruff>=0.7.2",
]

[tool.ruff]
exclude = ["docs/conf.py"]

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I"]

[tool.tox]
env_list = ["py38", "py39", "py310", "py311", "py312", "py313", "py313t", "pypy3", "ruff"]

[tool.tox.env_run_base]
deps = ["pytest>=6.2.0", "pytest-cov", "pytest-order"]
commands = [[
"pytest",
"-v",
"--cov-report", "lcov",
"--cov", "src/pytest_run_parallel",
"--cov", "tests",
"{posargs:tests}"
]]

[tool.tox.env.ruff]
skip_install = true
deps = ["ruff"]
commands = [["ruff", "check"]]

[tool.tox.gh.python]
"3.8" = ["py38"]
"3.9" = ["py39"]
"3.10" = ["py310"]
"3.11" = ["py311"]
"3.12" = ["py312"]
"3.13" = ["py313", "ruff"]
"3.13t" = ["py313t"]
"pypy-3.8" = ["pypy3"]
42 changes: 22 additions & 20 deletions src/pytest_run_parallel/plugin.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,52 @@
import pytest
import threading
import functools
import threading
import types

from _pytest.outcomes import Skipped, Failed
import pytest
from _pytest.outcomes import Failed, Skipped

try:
import numpy as np

numpy_available = True
except ImportError:
numpy_available = False


def pytest_addoption(parser):
group = parser.getgroup('run-parallel')
group = parser.getgroup("run-parallel")
group.addoption(
'--parallel-threads',
action='store',
dest='parallel_threads',
"--parallel-threads",
action="store",
dest="parallel_threads",
default=1,
type=int,
help='Set the number of threads used to execute each test concurrently.'
help="Set the number of threads used to execute each test concurrently.",
)


def pytest_configure(config):
config.addinivalue_line(
'markers',
'parallel_threads(n): run the given test function in parallel '
'using `n` threads.')
"markers",
"parallel_threads(n): run the given test function in parallel "
"using `n` threads.",
)


def wrap_function_parallel(fn, n_workers=10):
barrier = threading.Barrier(n_workers)

@functools.wraps(fn)
def inner(*args, **kwargs):
errors = []
skip = None
failed = None

def closure(*args, **kwargs):
barrier.wait()
try:
fn(*args, **kwargs)
except Warning as w:
except Warning:
pass
except Exception as e:
errors.append(e)
Expand All @@ -56,9 +60,9 @@ def closure(*args, **kwargs):
workers = []
for _ in range(0, n_workers):
worker_kwargs = kwargs
workers.append(threading.Thread(
target=closure,
args=args, kwargs=worker_kwargs))
workers.append(
threading.Thread(target=closure, args=args, kwargs=worker_kwargs)
)

for worker in workers:
worker.start()
Expand All @@ -79,7 +83,7 @@ def closure(*args, **kwargs):
@pytest.hookimpl(trylast=True)
def pytest_itemcollected(item):
n_workers = item.config.option.parallel_threads
m = item.get_closest_marker('parallel_threads')
m = item.get_closest_marker("parallel_threads")
if m is not None:
n_workers = int(m.args[0])
if n_workers is not None and n_workers > 1:
Expand All @@ -90,14 +94,13 @@ def pytest_itemcollected(item):
def num_parallel_threads(request):
node = request.node
n_workers = request.config.option.parallel_threads
m = node.get_closest_marker('parallel_threads')
m = node.get_closest_marker("parallel_threads")
if m is not None:
n_workers = int(m.args[0])
return n_workers


class ThreadComparator:

def __init__(self, n_threads):
self._barrier = threading.Barrier(n_threads)
self._reset_evt = threading.Event()
Expand Down Expand Up @@ -147,8 +150,7 @@ def __call__(self, **values):
if len(value_a.shape) == 0:
assert value_a == value_b
else:
assert np.allclose(
value_a, value_b, equal_nan=True)
assert np.allclose(value_a, value_b, equal_nan=True)
elif isinstance(value_a, types.FunctionType):
assert id(value_a) == id(value_b)
elif value_a != value_a:
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pytest_plugins = 'pytester'
pytest_plugins = "pytester"
Loading