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"