Thank you for considering it. This page covers how to set up, how to run the checks, and the conventions every module follows.
By participating you agree to the Code of Conduct.
The project uses uv.
git clone https://github.com/LitePy/PythonWrap
cd PythonWrap
uv sync --all-groupsThis creates .venv/, installs the package in editable mode and installs the dev and
docs groups. You never need to activate the virtualenv: prefix commands with uv run.
There is no Makefile; the commands below are the whole workflow.
Run these checks before opening or merging a pull request. CI and docs run after
changes land on main; pull requests and feature-branch pushes do not start them.
Release publishing remains tied to version tags.
uv run pytest # tests
uv run pytest --cov --cov-report=term-missing # with coverage
uv run pytest tests/test_retry.py -k async -x # a subset
uv run ruff check src tests # lint
uv run ruff format src tests # format (CI runs it with --check)
uv run mypy # strict type check of src/
uv run mkdocs serve # live docs preview
uv run mkdocs build --strict # what the docs CI job runsTo test another interpreter: uv run --python 3.10 pytest.
Optional, and worth installing once: uvx pre-commit install.
The authoritative version is the docstring at the top of src/python_wrap/_core.py.
In short:
-
Standard library only. No runtime dependencies. Python 3.10 and newer.
-
Sync and async. Every decorator works on plain functions,
async deffunctions and methods (instance, class, static). Forasync def, return anasync defwrapper soinspect.iscoroutinefunction(wrapped)stays true, and never block the event loop. -
No logging configuration. Never call
logging.basicConfigor add handlers. Log through_core.get_logger(logger); logging decorators takelogger: LoggerLike = Noneandlevel. -
A patchable clock. Read time through
_core.monotonic()and_core.perf_counter()and wait through_core.sleep()andawait _core.async_sleep(), always as attributes of the module (from python_wrap import _core), never imported by name. -
Full typing.
mypy --strictclean, signatures preserved withParamSpec. Decorators usable both bare and with options use the overload pattern:@overload def timing(func: Callable[P, R], /) -> Callable[P, R]: ... @overload def timing(func: None = None, /, *, logger: LoggerLike = None) -> Callable[[Callable[P, R]], Callable[P, R]]: ... def timing(func=None, /, *, logger=None): def decorate(f): ... return decorate if func is None else decorate(func)
-
Thread safety where state is shared: locks around caches, counters and buckets.
-
functools.wrapson every wrapper, and a Google-style docstring (Args, Returns, Raises, Example) on every public function; the docs site renders them. -
__all__in every module.
Put tests in tests/test_<module>.py. They should be behaviour-focused and fast, cover
sync and async paths and error paths, and test thread safety where relevant.
Use the fake_clock fixture from tests/conftest.py instead of real sleeping. It
patches every time source in _core, and sleep / async_sleep advance the fake clock
instead of waiting:
def test_retry_waits_between_attempts(fake_clock):
...
assert fake_clock.sleeps == [0.5, 1.0]No real sleep over 0.05 seconds.
Keep them focused, add a line under Unreleased in CHANGELOG.md, and explain the
reasoning in the description. Releases are described in RELEASING.md.