nox-uv 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ # For information about this file, see: https://editorconfig.org/
2
+ root = true
3
+
4
+ # For ease of fitting multiple editor panes side by side consistently, set all files types to use
5
+ # the same relaxed max line length permitted in PEP 8.
6
+ [*]
7
+ max_line_length = 99
@@ -0,0 +1,48 @@
1
+ name: CI
2
+
3
+ on: [push, pull_request]
4
+
5
+ env:
6
+ UV_VERSION: "0.6.9"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-24.04
11
+ strategy:
12
+ matrix:
13
+ python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v5
18
+ # Caching is enabled by default for GitHub-hosted runners:
19
+ # https://github.com/astral-sh/setup-uv?tab=readme-ov-file#enable-caching
20
+ with:
21
+ version: ${{ env.UV_VERSION }}
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ - name: Install Package
27
+ run: uv sync --locked --no-default-groups
28
+ - name: Test with Nox
29
+ run: uv run nox -s test-${{ matrix.python-version }}
30
+ quality:
31
+ runs-on: ubuntu-24.04
32
+ strategy:
33
+ matrix:
34
+ nox-session: ["lint", "type_check"]
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - name: Install uv
38
+ uses: astral-sh/setup-uv@v5
39
+ with:
40
+ version: ${{ env.UV_VERSION }}
41
+ - name: Set up Python
42
+ uses: actions/setup-python@v5
43
+ with:
44
+ python-version-file: ".python-version"
45
+ - name: Install dependencies
46
+ run: uv sync --locked --no-default-groups
47
+ - name: Test with Nox
48
+ run: uv run nox -s ${{ matrix.nox-session }}
@@ -0,0 +1,13 @@
1
+ name: container
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-24.04
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - name: Build Container Image
11
+ uses: docker/build-push-action@v6
12
+ with:
13
+ push: false
@@ -0,0 +1,12 @@
1
+ .vscode
2
+ .idea
3
+ .mypy_cache
4
+ .nox
5
+ .pytest_cache
6
+ dist
7
+ htmlcov
8
+ __pycache__
9
+ *.pyc
10
+ *.egg-info
11
+ .coverage
12
+ .coverage.*
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,97 @@
1
+ # Use a multi-stage build to reduce the size of the final image.
2
+ # This example is optimized to reduce final image size rather than for simplicity.
3
+ # Using a -slim image also greatly reduces image size.
4
+ # It is possible to use -alpine images instead to further reduce image size, but this comes
5
+ # with several important caveats.
6
+ # - Alpine images use MUSL rather than GLIBC (as used in the default Debian-based images).
7
+ # - Most Python packages that require C code are tested against GLIBC, so there could be
8
+ # subtle errors when using MUSL.
9
+ # - These Python packages usually only provide binary wheels for GLIBC, so the packages
10
+ # will need to be recompiled fully within the container images, increasing build times.
11
+ FROM python:3.11-slim-bookworm AS python_builder
12
+
13
+ # Pin uv to a specific version to make container builds reproducible.
14
+ ENV UV_VERSION=0.6.9
15
+
16
+ # Set ENV variables that make Python more friendly to running inside a container.
17
+ ENV PYTHONDONTWRITEBYTECODE=1
18
+ ENV PYTHONBUFFERED=1
19
+
20
+ # By default, pip caches copies of downloaded packages from PyPI. These are not useful within
21
+ # a contain image, so disable this to reduce the size of images.
22
+ ENV PIP_NO_CACHE_DIR=1
23
+ ENV WORKDIR=/src
24
+
25
+ WORKDIR ${WORKDIR}
26
+
27
+ # Install any system dependencies required to build wheels, such as C compilers or system packages
28
+ # For example:
29
+ #RUN apt-get update && apt-get install -y \
30
+ # gcc \
31
+ # && rm -rf /var/lib/apt/lists/*
32
+
33
+ # Install uv into the global environment to isolate it from the venv it creates.
34
+ RUN pip install "uv==${UV_VERSION}"
35
+
36
+ # Pre-download/compile wheel dependencies into a virtual environment.
37
+ # Doing this in a multi-stage build allows omitting compile dependencies from the final image.
38
+ # This must be the same path that is used in the final image as the virtual environment has
39
+ # absolute symlinks in it.
40
+ ENV VIRTUAL_ENV=/opt/venv
41
+ RUN python -m venv ${VIRTUAL_ENV}
42
+ ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
43
+
44
+ # Copy in project dependency specification.
45
+ COPY pyproject.toml uv.lock ./
46
+
47
+ # Don't install the package itself with uv because it will install it as an editable install.
48
+ RUN uv sync --locked --no-default-groups --no-install-project
49
+
50
+ # Copy in source files.
51
+ COPY README.md ./
52
+ COPY src src
53
+
54
+ # Manually build/install the package.
55
+ RUN uv build && \
56
+ pip install dist/*.whl
57
+
58
+ ## Final Image
59
+ # The image used in the final image MUST match exactly to the python_builder image.
60
+ FROM python:3.11-slim-bookworm
61
+
62
+ ENV PYTHONDONTWRITEBYTECODE=1
63
+ ENV PYTHONBUFFERED=1
64
+ ENV PIP_NO_CACHE_DIR=1
65
+ ENV VIRTUAL_ENV=/opt/venv
66
+
67
+ ENV HOME=/home/user
68
+ ENV APP_HOME=${HOME}/app
69
+
70
+ # Create the home directory for the new user.
71
+ RUN mkdir -p ${HOME}
72
+
73
+ # Create the user so the program doesn't run as root. This increases security of the container.
74
+ RUN groupadd -r user && \
75
+ useradd -r -g user -d ${HOME} -s /sbin/nologin -c "Container image user" user
76
+
77
+ # Setup application install directory.
78
+ RUN mkdir ${APP_HOME}
79
+
80
+ # If you use Docker Compose volumes, you might need to create the directories in the image,
81
+ # otherwise when Docker Compose creates them they are owned by the root user and are inaccessible
82
+ # by the non-root user. See https://github.com/docker/compose/issues/3270
83
+
84
+ WORKDIR ${APP_HOME}
85
+
86
+ # Copy and activate pre-built virtual environment.
87
+ COPY --from=python_builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
88
+ ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
89
+
90
+ # For Python applications that are not installable libraries, you may need to copy in source
91
+ # files here in the final image rather than in the python_builder image.
92
+
93
+ # Give access to the entire home folder to the new user so that files and folders can be written
94
+ # there. Some packages such as matplotlib, want to write to the home folder.
95
+ RUN chown -R user:user ${HOME}
96
+
97
+ ENTRYPOINT ["fact"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dan Tebben
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
nox_uv-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: nox-uv
3
+ Version: 0.1.0
4
+ Summary: Facilitate nox integration with uv for Python projects
5
+ Project-URL: Homepage, https://github.com/dantebben/nox-uv
6
+ Author-email: Dan Tebben <dantebben@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE.txt
9
+ Requires-Python: <3.14,>=3.9
10
+ Requires-Dist: nox>=2025.2.9
11
+ Description-Content-Type: text/markdown
12
+
13
+ ## Intro
14
+
15
+ This is heavliy influcenced by, but much more limited than,
16
+ [nox-poetry](https://nox-poetry.readthedocs.io).
17
+
18
+ This is a basic drop-in replacement for `nox.session` of [nox](https://nox.thea.codes/) to be used
19
+ with the [uv](https://docs.astral.sh/uv/) package manager.
20
+
21
+ To use, import `session` from `nox-uv` in your `nox-file`.
22
+
23
+ **NOTE**: All `@session(...)` parameters are keyword-only, no positional parameters are allowed.
24
+
25
+ **NOTE**: The `default_groups` defined in `pyproject.toml` are _not_ installed by default. The
26
+ user must explicitly list the desired groups in the `uv_groups` parameter.
27
+
28
+ ## Added parameters
29
+
30
+ - `uv_groups`: list of `uv` dependency groups
31
+ - `uv_extras`: list of `uv` extras
32
+ - `uv_all_extras`: boolean to install all extras from `pyproject.toml`
33
+ - `uv_all_groups`: boolean to install all dependency groups
34
+
nox_uv-0.1.0/README.md ADDED
@@ -0,0 +1,22 @@
1
+ ## Intro
2
+
3
+ This is heavliy influcenced by, but much more limited than,
4
+ [nox-poetry](https://nox-poetry.readthedocs.io).
5
+
6
+ This is a basic drop-in replacement for `nox.session` of [nox](https://nox.thea.codes/) to be used
7
+ with the [uv](https://docs.astral.sh/uv/) package manager.
8
+
9
+ To use, import `session` from `nox-uv` in your `nox-file`.
10
+
11
+ **NOTE**: All `@session(...)` parameters are keyword-only, no positional parameters are allowed.
12
+
13
+ **NOTE**: The `default_groups` defined in `pyproject.toml` are _not_ installed by default. The
14
+ user must explicitly list the desired groups in the `uv_groups` parameter.
15
+
16
+ ## Added parameters
17
+
18
+ - `uv_groups`: list of `uv` dependency groups
19
+ - `uv_extras`: list of `uv` extras
20
+ - `uv_all_extras`: boolean to install all extras from `pyproject.toml`
21
+ - `uv_all_groups`: boolean to install all dependency groups
22
+
@@ -0,0 +1,70 @@
1
+ from nox import Session, options, parametrize
2
+
3
+ from nox_uv import session
4
+
5
+ options.error_on_external_run = True
6
+ options.reuse_existing_virtualenvs = True
7
+ options.sessions = ["lint", "type_check", "test"]
8
+
9
+
10
+ # Including Python 3.9 here just to test when UV_PYTHON_DOWNLOADS=never
11
+ @session(
12
+ venv_backend="uv",
13
+ reuse_venv=True,
14
+ python=["3.9", "3.10", "3.11", "3.12", "3.13"],
15
+ uv_groups=["test"],
16
+ uv_all_groups=True,
17
+ )
18
+ def test(s: Session) -> None:
19
+ s.run(
20
+ "pytest",
21
+ *s.posargs,
22
+ )
23
+
24
+
25
+ # For some sessions, set venv_backend="none" to simply execute scripts within the existing Poetry
26
+ # environment. This requires that nox is run within `poetry shell` or using `poetry run nox ...`.
27
+ @session(venv_backend="none")
28
+ @parametrize(
29
+ "command",
30
+ [
31
+ # During formatting, additionally sort imports and remove unused imports.
32
+ [
33
+ "ruff",
34
+ "check",
35
+ ".",
36
+ "--select",
37
+ "I",
38
+ "--select",
39
+ "F401",
40
+ "--extend-fixable",
41
+ "F401",
42
+ "--fix",
43
+ ],
44
+ ["ruff", "format", "."],
45
+ ],
46
+ )
47
+ def fmt(s: Session, command: list[str]) -> None:
48
+ s.run(*command)
49
+
50
+
51
+ @session(venv_backend="uv", uv_groups=["lint"])
52
+ @parametrize(
53
+ "command",
54
+ [
55
+ ["ruff", "check", "."],
56
+ ["ruff", "format", "--check", "."],
57
+ ],
58
+ )
59
+ def lint(s: Session, command: list[str]) -> None:
60
+ s.run(*command)
61
+
62
+
63
+ @session(venv_backend="none")
64
+ def lint_fix(s: Session) -> None:
65
+ s.run("ruff", "check", ".", "--extend-fixable", "F401", "--fix")
66
+
67
+
68
+ @session(venv_backend="none")
69
+ def type_check(s: Session) -> None:
70
+ s.run("mypy", "src", "tests", "noxfile.py")
@@ -0,0 +1,122 @@
1
+ [project]
2
+ name = "nox-uv"
3
+ version = "0.1.0"
4
+ description = "Facilitate nox integration with uv for Python projects"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Dan Tebben", email = "dantebben@gmail.com" }
8
+ ]
9
+ license = "MIT"
10
+ license-files = ["LICENSE.txt"]
11
+ requires-python = ">=3.9, <3.14"
12
+ dependencies = [
13
+ "nox>=2025.2.9",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/dantebben/nox-uv"
18
+
19
+ [build-system]
20
+ requires = ["hatchling"]
21
+ build-backend = "hatchling.build"
22
+
23
+ [dependency-groups]
24
+ docs = [
25
+ "mkdocs>=1.6.1",
26
+ "mkdocs-gen-files>=0.5.0",
27
+ "mkdocs-htmlproofer-plugin>=1.3.0",
28
+ "mkdocs-literate-nav>=0.6.1",
29
+ "mkdocs-material>=9.6.5",
30
+ "mkdocstrings[python]>=0.28.2",
31
+ ]
32
+ lint = [
33
+ "ruff>=0.9.7",
34
+ ]
35
+ test = [
36
+ "pytest-cov>=6.0.0",
37
+ "pytest-randomly>=3.16.0",
38
+ ]
39
+ type-check = [
40
+ "mypy>=1.15.0",
41
+ ]
42
+
43
+ [tool.uv]
44
+ default-groups = [
45
+ "lint",
46
+ "test",
47
+ "type-check",
48
+ ]
49
+
50
+
51
+ [tool.mypy]
52
+ ignore_missing_imports = true
53
+ strict = true
54
+ # TODO: Remove this when explicit-override is enabled by default in strict mode
55
+ # https://github.com/python/mypy/issues/17511
56
+ enable_error_code = ["explicit-override"]
57
+ # If certain strict config options are too pedantic for a project,
58
+ # disable them selectively here by setting to false.
59
+
60
+ [tool.ruff]
61
+ line-length = 99
62
+ src = ["src"]
63
+ # Ruff will automatically exclude all files listed in .gitignore as well as common temporary Python
64
+ # tool directories.
65
+ # To exclude additional folders, use extend-exclude.
66
+
67
+ [tool.ruff.lint]
68
+ select = [
69
+ "F", # pyflakes
70
+ "E", # pycodestyle
71
+ "I", # isort
72
+ "N", # pep8-naming
73
+ "UP", # pyupgrade
74
+ "RUF", # ruff
75
+ "B", # flake8-bugbear
76
+ "C4", # flake8-comprehensions
77
+ "ISC", # flake8-implicit-str-concat
78
+ "PIE", # flake8-pie
79
+ "PT", # flake-pytest-style
80
+ "PTH", # flake8-use-pathlib
81
+ "SIM", # flake8-simplify
82
+ "TID", # flake8-tidy-imports
83
+ ]
84
+ extend-ignore = [
85
+ "RUF005",
86
+ "RUF012",
87
+ ]
88
+ unfixable = [
89
+ # Disable removing unused imports by default and only enable within nox so editors don't delete
90
+ # unused imports while the user is in the middle of editing a file on save.
91
+ "F401",
92
+ ]
93
+
94
+ [tool.ruff.lint.isort]
95
+ force-sort-within-sections = true
96
+ split-on-trailing-comma = false
97
+ # For non-src directory projects, explicitly set top level package names:
98
+ # known-first-party = ["my-app"]
99
+
100
+ [tool.ruff.lint.flake8-tidy-imports]
101
+ ban-relative-imports = "all"
102
+
103
+ [tool.ruff.lint.flake8-bugbear]
104
+ extend-immutable-calls = ["typer.Argument"]
105
+
106
+ [tool.pytest.ini_options]
107
+ addopts = [
108
+ "--strict-config",
109
+ "--strict-markers",
110
+ ]
111
+ xfail_strict = true
112
+ filterwarnings = [
113
+ # When running tests, treat warnings as errors (e.g. -Werror).
114
+ # See: https://docs.pytest.org/en/latest/reference/reference.html#confval-filterwarnings
115
+ "error",
116
+ # Add additional warning suppressions as needed here. For example, if a third-party library
117
+ # is throwing a deprecation warning that needs to be fixed upstream:
118
+ # "ignore::DeprecationWarning:typer",
119
+ ]
120
+
121
+ [tool.coverage.run]
122
+ branch = true
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+ import functools
5
+ from typing import Any, Callable, TypeVar
6
+
7
+ import nox
8
+
9
+ R = TypeVar("R")
10
+
11
+ Python = Sequence[str] | str | bool | None
12
+
13
+
14
+ def session(
15
+ *args: Any,
16
+ python: Python | None = None,
17
+ reuse_venv: bool | None = None,
18
+ name: str | None = None,
19
+ venv_backend: Any | None = None,
20
+ venv_params: Sequence[str] = (),
21
+ tags: Sequence[str] | None = None,
22
+ default: bool = True,
23
+ requires: Sequence[str] | None = None,
24
+ uv_groups: Sequence[str] = (),
25
+ uv_extras: Sequence[str] = (),
26
+ uv_all_extras: bool = False,
27
+ uv_all_groups: bool = False,
28
+ ) -> Callable[..., R]:
29
+ """Drop-in replacement for the :func:`nox.session` decorator.
30
+
31
+ Use this decorator instead of ``@nox.session``. Session functions are passed
32
+ :class:`Session` instead of :class:`nox.sessions.Session`; otherwise, the
33
+ decorators work exactly the same.
34
+
35
+ Args:
36
+ args: Positional arguments are forwarded to ``nox.session``.
37
+ kwargs: Keyword arguments are forwarded to ``nox.session``.
38
+
39
+ Returns:
40
+ The decorated session function.
41
+ """
42
+ if not args:
43
+ return functools.partial(
44
+ session,
45
+ python=python,
46
+ reuse_venv=reuse_venv,
47
+ name=name,
48
+ venv_backend=venv_backend,
49
+ venv_params=venv_params,
50
+ tags=tags,
51
+ default=default,
52
+ requires=requires,
53
+ uv_groups=uv_groups,
54
+ uv_extras=uv_extras,
55
+ uv_all_extras=uv_all_extras,
56
+ uv_all_groups=uv_all_groups,
57
+ ) # type: ignore
58
+
59
+ [function] = args
60
+
61
+ is_uv = venv_backend == "uv"
62
+
63
+ # Create the `uv sync` command
64
+ sync_cmd = ["uv", "sync", "--no-default-groups"]
65
+
66
+ # Add the groups
67
+ for g in uv_groups:
68
+ sync_cmd.append(f"--group={g}")
69
+
70
+ # Add the extras
71
+ for e in uv_extras:
72
+ sync_cmd.append(f"--extra={e}")
73
+
74
+ if uv_all_groups:
75
+ sync_cmd.append("--all-groups")
76
+
77
+ if uv_all_extras:
78
+ sync_cmd.append("--all-extras")
79
+
80
+ @functools.wraps(function)
81
+ def wrapper(s: nox.Session, *_args: Any, **_kwargs: Any) -> None:
82
+ if is_uv:
83
+ env: dict[str, Any] = {"UV_PROJECT_ENVIRONMENT": s.virtualenv.location}
84
+
85
+ # UV called from Nox does not respect the Python version set in the Nox session.
86
+ # We need to pass the Python version to UV explicitly.
87
+ if s.python is not None:
88
+ env["UV_PYTHON"] = s.python
89
+
90
+ s.run_install(
91
+ *sync_cmd,
92
+ env=env,
93
+ )
94
+ function(nox.Session(s._runner), *_args, **_kwargs)
95
+
96
+ return nox.session( # type: ignore
97
+ wrapper,
98
+ python=python,
99
+ reuse_venv=reuse_venv,
100
+ name=name,
101
+ venv_backend=venv_backend,
102
+ venv_params=venv_params,
103
+ tags=tags,
104
+ default=default,
105
+ requires=requires,
106
+ )
File without changes
@@ -0,0 +1,2 @@
1
+ def test_1() -> None:
2
+ assert 5 == 5