diff --git a/.darglint b/.darglint index 2b03755a..72ccc6c5 100644 --- a/.darglint +++ b/.darglint @@ -1,2 +1,2 @@ [darglint] -strictness = short +strictness = long diff --git a/README.rst b/README.rst index 1b5c9d87..18d7139a 100644 --- a/README.rst +++ b/README.rst @@ -87,11 +87,16 @@ This works because session functions are passed instances of ``nox_poetry.Sessio a proxy for ``nox.Session`` adding Poetry-related functionality. Behind the scenes, nox-poetry uses Poetry to export a `constraints file`_ and build the package. -For more fine-grained control, additional utilities are available under the ``session.poetry`` attribute: +You can also create a ``PoetrySession`` from a ``session``; +this works both in plain Nox sessions, and in nox-poetry sessions. +The ``PoetrySession`` class provides the same ``install`` method as ``nox_poetry.Session``, +as well as these additional utilities: -- ``session.poetry.installroot(distribution_format=[WHEEL|SDIST])`` -- ``session.poetry.build_package(distribution_format=[WHEEL|SDIST])`` -- ``session.poetry.export_requirements()`` +- ``installroot(distribution_format=[WHEEL|SDIST])`` +- ``build_package(distribution_format=[WHEEL|SDIST])`` +- ``export_requirements()`` + +For more details, please see the API reference in the documentation_. Why? @@ -187,6 +192,7 @@ This project was generated from `@cjolowicz`_'s `Hypermodern Python Cookiecutter .. _Nox: https://nox.thea.codes/ .. _Poetry: https://python-poetry.org/ .. _constraints file: https://pip.pypa.io/en/stable/user_guide/#constraints-files +.. _documentation: https://nox-poetry.readthedocs.io/ .. _file an issue: https://github.com/cjolowicz/nox-poetry/issues .. _nox.sessions.Session.install: https://nox.thea.codes/en/stable/config.html#nox.sessions.Session.install .. _nox.sessions.Session.run: https://nox.thea.codes/en/stable/config.html#nox.sessions.Session.run diff --git a/docs/reference.rst b/docs/reference.rst index 27df359e..ab49dcdf 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -12,10 +12,10 @@ Classes ....... .. autoclass:: Session -.. automethod:: nox_poetry.sessions._PoetrySession.install -.. automethod:: nox_poetry.sessions._PoetrySession.installroot -.. automethod:: nox_poetry.sessions._PoetrySession.export_requirements -.. automethod:: nox_poetry.sessions._PoetrySession.build_package + :members: + +.. autoclass:: PoetrySession + :members: Constants ......... diff --git a/noxfile.py b/noxfile.py index c623d312..d4579438 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,6 +6,7 @@ import nox +from nox_poetry import PoetrySession from nox_poetry import Session from nox_poetry import session @@ -98,7 +99,9 @@ def precommit(session: Session) -> None: @session(python="3.9") def safety(session: Session) -> None: """Scan dependencies for insecure packages.""" - requirements = session.poetry.export_requirements() + poetry = PoetrySession(session) + requirements = poetry.export_requirements() + session.install("safety") # Ignore CVE-2020-28476 affecting all versions of tornado # https://github.com/tornadoweb/tornado/issues/2981 diff --git a/src/nox_poetry/__init__.py b/src/nox_poetry/__init__.py index ba6af1df..2404070f 100644 --- a/src/nox_poetry/__init__.py +++ b/src/nox_poetry/__init__.py @@ -2,9 +2,8 @@ This package provides a drop-in replacement for the :func:`session` decorator, and for the :class:`Session` object passed to user-defined session functions. -This enables :meth:`session.install -<nox_poetry.sessions._PoetrySession.install>` to install packages at the -versions specified in the Poetry lock file. +This enables :meth:`session.install <nox_poetry.PoetrySession.install>` to +install packages at the versions specified in the Poetry lock file. Example: >>> @session(python=["3.8", "3.9"]) @@ -12,14 +11,13 @@ ... session.install("pytest", ".") ... session.run("pytest") -It also provides helper functions that allow more fine-grained control: +The :class:`PoetrySession` class provides utilities that allow more fine-grained +control: -- :meth:`session.poetry.installroot - <nox_poetry.sessions._PoetrySession.installroot>` -- :meth:`session.poetry.build_package - <nox_poetry.sessions._PoetrySession.build_package>` -- :meth:`session.poetry.export_requirements - <nox_poetry.sessions._PoetrySession.export_requirements>` +- :meth:`PoetrySession.install` +- :meth:`PoetrySession.installroot` +- :meth:`PoetrySession.build_package` +- :meth:`PoetrySession.export_requirements` Two constants are defined to specify the format for distribution archives: @@ -31,6 +29,7 @@ from nox_poetry.core import install from nox_poetry.core import installroot from nox_poetry.poetry import DistributionFormat +from nox_poetry.sessions import PoetrySession from nox_poetry.sessions import Session from nox_poetry.sessions import session @@ -46,6 +45,7 @@ "export_requirements", "install", "installroot", + "PoetrySession", "Session", "session", "SDIST", diff --git a/src/nox_poetry/sessions.py b/src/nox_poetry/sessions.py index 6d1bc050..c86d93ce 100644 --- a/src/nox_poetry/sessions.py +++ b/src/nox_poetry/sessions.py @@ -2,11 +2,13 @@ import functools import hashlib import re +import warnings from pathlib import Path from typing import Any from typing import Iterable from typing import Optional from typing import Tuple +from typing import Union import nox @@ -41,6 +43,52 @@ def wrapper(session: nox.Session, *_args, **_kwargs) -> None: return nox.session(wrapper, **kwargs) # type: ignore[call-overload] +class _SessionProxy: + """Proxy for :class:`nox.sessions.Session`.""" + + def __init__(self, session: nox.Session) -> None: + """Initialize.""" + self._session = session + + def __getattr__(self, name: str) -> Any: + """Delegate attribute access to nox.Session.""" + return getattr(self._session, name) + + +class Session(_SessionProxy): + """Proxy for :class:`nox.sessions.Session`, passed to session functions. + + This class overrides :meth:`nox.sessions.Session.install` with + :meth:`PoetrySession.install`. + """ + + def __init__(self, session: nox.Session) -> None: + """Initialize.""" + super().__init__(session) + self._poetry = PoetrySession(session) + + @property + def poetry(self) -> "PoetrySession": + """Provide access to Poetry-related functionality. + + .. deprecated:: 0.9 + Use :class:`PoetrySession` instead. + """ # noqa: DAR + warnings.warn( + "nox_poetry.Session.poetry is deprecated" + ", use nox_poetry.PoetrySession instead", + category=FutureWarning, + ) + return self._poetry + + def install(self, *args: str, **kwargs: Any) -> None: + """Install packages into a Nox session using Poetry. + + See :meth:`PoetrySession.install` for details. + """ + return self.poetry.install(*args, **kwargs) + + _EXTRAS_PATTERN = re.compile(r"^(.+)(\[[^\]]+\])$") @@ -52,12 +100,12 @@ def _split_extras(arg: str) -> Tuple[str, Optional[str]]: return arg, None -class _PoetrySession: +class PoetrySession: """Poetry-related utilities for session functions.""" - def __init__(self, session: nox.Session) -> None: + def __init__(self, session: Union[nox.Session, Session]) -> None: """Initialize.""" - self.session = session + self.session = session if isinstance(session, nox.Session) else session._session self.poetry = Poetry(session) def install(self, *args: str, **kwargs: Any) -> None: @@ -199,40 +247,3 @@ def build_package( url += f"#egg={self.poetry.config.name}" return url - - -class _SessionProxy: - """Proxy for :class:`nox.sessions.Session`.""" - - def __init__(self, session: nox.Session) -> None: - """Initialize.""" - self._session = session - - def __getattr__(self, name: str) -> Any: - """Delegate attribute access to nox.Session.""" - return getattr(self._session, name) - - -class Session(_SessionProxy): - """Proxy for :class:`nox.sessions.Session`, passed to session functions. - - This class overrides :meth:`session.install - <nox_poetry.sessions._PoetrySession.install>`, and provides Poetry-related - utilities: - - - :meth:`Session.poetry.installroot - <nox_poetry.sessions._PoetrySession.installroot>` - - :meth:`Session.poetry.build_package - <nox_poetry.sessions._PoetrySession.build_package>` - - :meth:`Session.poetry.export_requirements - <nox_poetry.sessions._PoetrySession.export_requirements>` - """ - - def __init__(self, session: nox.Session) -> None: - """Initialize.""" - super().__init__(session) - self.poetry = _PoetrySession(session) - - def install(self, *args: str, **kwargs: Any) -> None: - """Install packages into a Nox session using Poetry.""" - return self.poetry.install(*args, **kwargs) diff --git a/src/nox_poetry/sessions.pyi b/src/nox_poetry/sessions.pyi index 9dfabdd7..c25e22f8 100644 --- a/src/nox_poetry/sessions.pyi +++ b/src/nox_poetry/sessions.pyi @@ -18,7 +18,8 @@ import nox.virtualenv Python = Optional[Union[str, Sequence[str], bool]] -class _PoetrySession: +class PoetrySession: + def __init__(self, session: Union[nox.Session, "Session"]) -> None: ... def install(self, *args: str, **kwargs: Any) -> None: ... def installroot( self, *, distribution_format: str = ..., extras: Iterable[str] = ... @@ -27,7 +28,7 @@ class _PoetrySession: def build_package(self, *, distribution_format: str = ...) -> str: ... class Session: - poetry: _PoetrySession + poetry: PoetrySession _session: nox.Session def __init__(self, session: nox.Session) -> None: ... def install(self, *args: str, **kwargs: Any) -> None: ... diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index c0febc9d..2d1fe505 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -23,6 +23,11 @@ def __init__(self, path: Path) -> None: """Initialize.""" self.virtualenv = FakeVirtualenv(path) + @property + def _session(self) -> "FakeSession": + """Allow passing this instance to PoetrySession.""" + return self + def run_always(self, *args: str, **kargs: Any) -> str: """Run.""" path = Path("dist") / "example.whl"