Skip to content

Commit

Permalink
Merge pull request #1152 from procrastinate-org/sphinx-autodoc
Browse files Browse the repository at this point in the history
  • Loading branch information
ewjoachim authored Aug 11, 2024
2 parents 6500165 + 86ea2a2 commit e28d38c
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 5 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ repos:
- psycopg2-binary==2.9.9
- psycopg[pool]==3.2.1
- python-dateutil==2.9.0.post0
- sphinx==7.1.2
- sqlalchemy==2.0.32
- typing-extensions==4.12.2
- repo: https://github.com/astral-sh/ruff-pre-commit
Expand Down
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"sphinxcontrib.programoutput",
"sphinx_github_changelog",
"sphinx_copybutton",
"procrastinate.contrib.sphinx",
]

myst_enable_extensions = [
Expand Down
1 change: 1 addition & 0 deletions docs/howto/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ advanced/events
advanced/sync_defer
advanced/custom_json_encoder_decoder
advanced/blueprints
advanced/sphinx
:::
30 changes: 30 additions & 0 deletions docs/howto/advanced/sphinx.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Document my tasks with Sphinx & Autodoc

If you use Sphinx's `autodoc` extension to document your project, you might
have noticed that your tasks are absent from the documentation. This is because
when you apply the `@app.task` decorator, you're actually replacing the
function with a Procrastinate Task object, which `autodoc` doesn't know how to
process.

Procrastinate provides a small Sphinx extension to fix that. You may want to
ensure procrastinate is installed with the `sphinx` extra in the environment
where you build your doc. This is not mandatory, as it only adds sphinx itself
as a dependency, but if the extension ever needs other dependencies in the
future, they will be installed through the `sphinx` extra as well.

```bash
$ pip install procrastinate[sphinx]
```

Then, add the following to your `conf.py`:

```python
extensions = [
# ...
"procrastinate.contrib.sphinx",
# ...
]
```

That's it. Your tasks will now be picked up by `autodoc` and included in your
documentation.
5 changes: 3 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions procrastinate/contrib/sphinx/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

from typing import Any

from sphinx.application import Sphinx
from sphinx.ext.autodoc import FunctionDocumenter

from procrastinate import tasks


class ProcrastinateTaskDocumenter(FunctionDocumenter):
objtype = "procrastinate_task"
directivetype = "function"
member_order = 40

@classmethod
def can_document_member(
cls,
member: Any,
membername: str,
isattr: bool,
parent: Any,
) -> bool:
return isinstance(member, tasks.Task)


def setup(app: Sphinx):
app.setup_extension("sphinx.ext.autodoc") # Require autodoc extension

app.add_autodocumenter(ProcrastinateTaskDocumenter)

return {
"version": "1",
"parallel_read_safe": True,
}
2 changes: 2 additions & 0 deletions procrastinate_demos/demo_async/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

@app.task(queue="sums")
async def sum(a, b):
"""Sum two numbers."""
return a + b


@app.task(queue="defer")
async def defer():
"""Defer a another task."""
await sum.defer_async(a=1, b=2)
16 changes: 13 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ psycopg2-binary = { version = "*", optional = true }
python-dateutil = "*"
sqlalchemy = { version = "^2.0", optional = true }
typing-extensions = { version = "*", python = "<3.8" }
sphinx = { version = "*", optional = true }

[tool.poetry.extras]
django = ["django"]
sqlalchemy = ["sqlalchemy"]
aiopg = ["aiopg", "psycopg2-binary"]
psycopg2 = ["psycopg2-binary"]
sphinx = ["sphinx"]

[tool.poetry.group.types]
optional = true
Expand All @@ -62,9 +64,17 @@ aiopg = "*"
sqlalchemy = { extras = ["mypy"], version = "*" }
psycopg2-binary = "*"
psycopg = [
{ version = "^3.1.13", extras = ["binary", "pool"], markers = "sys_platform != 'darwin' or platform_machine != 'arm64'"},
{ version = "^3.1.13", extras = ["binary", "pool"], markers = "sys_platform == 'darwin' and platform_machine == 'arm64'", python = ">=3.10"},
{ version = "^3.1.13", extras = ["pool"], markers = "sys_platform == 'darwin' and platform_machine == 'arm64'", python = "<3.10" }
{ version = "^3.1.13", extras = [
"binary",
"pool",
], markers = "sys_platform != 'darwin' or platform_machine != 'arm64'" },
{ version = "^3.1.13", extras = [
"binary",
"pool",
], markers = "sys_platform == 'darwin' and platform_machine == 'arm64'", python = ">=3.10" },
{ version = "^3.1.13", extras = [
"pool",
], markers = "sys_platform == 'darwin' and platform_machine == 'arm64'", python = "<3.10" },
]

[tool.poetry.group.django.dependencies]
Expand Down
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
if key.startswith("PROCRASTINATE_"):
os.environ.pop(key)

# Unfortunately, we need the sphinx fixtures even though they generate an "app" fixture
# that conflicts with our own "app" fixture
pytest_plugins = ["sphinx.testing.fixtures"]


def cursor_execute(cursor, query, *identifiers):
if identifiers:
Expand Down
Empty file.
Empty file.
8 changes: 8 additions & 0 deletions tests/integration/contrib/sphinx/test-root/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

extensions = [
"sphinx.ext.autodoc",
"procrastinate.contrib.sphinx",
]

buildername = "html"
5 changes: 5 additions & 0 deletions tests/integration/contrib/sphinx/test-root/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Tasks
=====

.. automodule:: procrastinate_demos.demo_async.tasks
:members:
26 changes: 26 additions & 0 deletions tests/integration/contrib/sphinx/test_autodoc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from __future__ import annotations

import pytest
from sphinx.testing.path import path


@pytest.fixture
def rootdir():
return path(__file__).parent


@pytest.fixture
def sphinx_app(app_params, make_app):
"""
Provides the 'sphinx.application.Sphinx' object
"""
args, kwargs = app_params
return make_app(*args, **kwargs)


@pytest.mark.sphinx(buildername="html", testroot="root")
def test_autodoc_task(sphinx_app):
sphinx_app.build()
content = (sphinx_app.outdir / "index.html").read_text()
# Check that the docstring of one of the task appears in the generated documentation
assert "Sum two numbers." in content

0 comments on commit e28d38c

Please sign in to comment.