afterpython 0.4.0__tar.gz → 0.4.2__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.
Files changed (73) hide show
  1. {afterpython-0.4.0 → afterpython-0.4.2}/PKG-INFO +18 -16
  2. {afterpython-0.4.0 → afterpython-0.4.2}/README.md +3 -2
  3. {afterpython-0.4.0 → afterpython-0.4.2}/pyproject.toml +16 -15
  4. {afterpython-0.4.0 → afterpython-0.4.2}/pyproject.toml.orig +16 -16
  5. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/__init__.py +4 -0
  6. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/build_pdoc.py +7 -0
  7. afterpython-0.4.2/src/afterpython/builders/comments_json.py +107 -0
  8. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/jupyter_notebook.py +23 -7
  9. afterpython-0.4.2/src/afterpython/builders/marimo_content.py +467 -0
  10. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/marimo_notebook.py +7 -6
  11. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/build.py +74 -55
  12. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/clean.py +6 -0
  13. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/dev.py +19 -1
  14. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/init.py +113 -51
  15. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/main.py +3 -1
  16. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/const.py +1 -1
  17. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/afterpython-template.toml +6 -0
  18. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/ci-workflow-template.yml +10 -10
  19. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/deploy-workflow-template.yml +2 -2
  20. afterpython-0.4.2/src/afterpython/templates/pdoc/custom.css +37 -0
  21. afterpython-0.4.2/src/afterpython/templates/pdoc/frame.html.jinja2 +21 -0
  22. afterpython-0.4.2/src/afterpython/templates/pdoc/syntax-highlighting.css +155 -0
  23. afterpython-0.4.2/src/afterpython/templates/pdoc/theme.css +44 -0
  24. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/release-workflow-template.yml +1 -1
  25. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/ruff-template.toml +1 -1
  26. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/myst.py +36 -19
  27. afterpython-0.4.0/src/afterpython/templates/pdoc/frame.html.jinja2 +0 -7
  28. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/__init__.py +0 -0
  29. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/_io/toml.py +0 -0
  30. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/_io/yaml.py +0 -0
  31. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/_paths.py +0 -0
  32. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/_typing.py +0 -0
  33. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/content_json.py +0 -0
  34. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/faq_json.py +0 -0
  35. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/index_md.py +0 -0
  36. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/llms_txt.py +0 -0
  37. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/markdown.py +0 -0
  38. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/metadata.py +0 -0
  39. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/builders/url_md.py +0 -0
  40. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/__init__.py +0 -0
  41. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/add.py +0 -0
  42. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/bump.py +0 -0
  43. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/changelog.py +0 -0
  44. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/check.py +0 -0
  45. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/commit_msg.py +0 -0
  46. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/format.py +0 -0
  47. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/init_branch_rules.py +0 -0
  48. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/install.py +0 -0
  49. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/lint.py +0 -0
  50. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/lock.py +0 -0
  51. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/prek.py +0 -0
  52. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/preview.py +0 -0
  53. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/release.py +0 -0
  54. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/remove.py +0 -0
  55. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/serve.py +0 -0
  56. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/start.py +0 -0
  57. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/sync.py +0 -0
  58. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/cli/commands/update.py +0 -0
  59. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/main.py +0 -0
  60. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/pcu.py +0 -0
  61. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/py.typed +0 -0
  62. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/cliff-template.toml +0 -0
  63. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/dependabot-template.yml +0 -0
  64. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/pre-commit-config-template.yaml +0 -0
  65. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/templates/ty-template.toml +0 -0
  66. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/_afterpython.py +0 -0
  67. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/_git.py +0 -0
  68. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/branch_rules.py +0 -0
  69. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/github_actions.py +0 -0
  70. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/prek.py +0 -0
  71. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/tools/pyproject.py +0 -0
  72. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/utils.py +0 -0
  73. {afterpython-0.4.0 → afterpython-0.4.2}/src/afterpython/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afterpython
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: CLI tool to streamline Python package maintenance and generate a dedicated project website
5
5
  Keywords: package-maintenance,project-website,static-site-generator,developer-tools,documentation,cli-tool,ci/cd,mystmd,semantic-release,python-packaging
6
6
  Author: Stephen Yau
@@ -11,26 +11,27 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: Topic :: Software Development :: Build Tools
12
12
  Classifier: Topic :: Software Development :: Documentation
13
13
  Classifier: Typing :: Typed
14
- Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
16
15
  Classifier: Programming Language :: Python :: 3.14
17
- Requires-Dist: click>=8.5.0
16
+ Classifier: Programming Language :: Python :: 3.15
17
+ Requires-Dist: click>=8.5.1
18
18
  Requires-Dist: trogon>=0.6.0
19
- Requires-Dist: httpx2>=2.13.1
20
- Requires-Dist: tomlkit>=0.15.1
19
+ Requires-Dist: httpx2>=2.16.0
20
+ Requires-Dist: tomlkit>=0.15.2
21
21
  Requires-Dist: ruamel-yaml>=0.19.1
22
- Requires-Dist: gitpython>=3.2.0
23
- Requires-Dist: python-dotenv>=1.2.3
22
+ Requires-Dist: gitpython>=3.2.1
23
+ Requires-Dist: python-dotenv>=1.2.4
24
24
  Requires-Dist: pyproject-metadata>=0.12.1
25
- Requires-Dist: jupytext>=1.19.5
26
- Requires-Dist: mystmd>=1.11.0
25
+ Requires-Dist: jupytext>=1.19.6
26
+ Requires-Dist: mystmd>=1.12.0
27
27
  Requires-Dist: pdoc>=16.0.0
28
- Requires-Dist: ruff>=0.16.9
29
- Requires-Dist: prek>=0.5.4
30
- Requires-Dist: ty>=0.0.84
28
+ Requires-Dist: ruff>=0.17.4
29
+ Requires-Dist: prek>=0.5.10
30
+ Requires-Dist: ty>=0.0.99
31
31
  Requires-Dist: git-cliff>=2.14.2
32
- Requires-Dist: marimo[recommended,lsp]>=0.25.0 ; extra == 'marimo'
33
- Requires-Python: >=3.12
32
+ Requires-Dist: questionary>=2.1.1
33
+ Requires-Dist: marimo[recommended,lsp]>=0.25.2 ; extra == 'marimo'
34
+ Requires-Python: >=3.13
34
35
  Project-URL: homepage, https://afterpython.afterpython.org
35
36
  Project-URL: repository, https://github.com/AfterPythonOrg/afterpython
36
37
  Project-URL: documentation, https://afterpython.afterpython.org/doc
@@ -44,8 +45,8 @@ Description-Content-Type: text/markdown
44
45
  [![PyPI](https://img.shields.io/pypi/v/afterpython.svg?cacheSeconds=300)](https://pypi.org/project/afterpython)
45
46
  ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/afterpython?cacheSeconds=300)
46
47
  [![Discussions](https://img.shields.io/badge/Discussions-Let's%20Chat-green)](https://github.com/AfterPythonOrg/afterpython/discussions)
47
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)
48
48
  [![View Code Wiki](https://assets.codewiki.google/readme-badge/static.svg)](https://codewiki.google/github.com/afterpythonorg/afterpython?utm_source=badge&utm_medium=github&utm_campaign=github.com/afterpythonorg/afterpython)
49
+ <!--[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)-->
49
50
  <!-- ![PyPI downloads](https://img.shields.io/pypi/dm/afterpython) -->
50
51
  <!-- [![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh) -->
51
52
 
@@ -53,6 +54,7 @@ Description-Content-Type: text/markdown
53
54
  [MyST]: https://mystmd.org
54
55
  [MyST Markdown]: https://mystmd.org/spec/
55
56
  [Jupyter Notebook]: https://jupyter.org
57
+ [marimo]: https://marimo.io
56
58
  [pre-commit]: https://pre-commit.com
57
59
  [pagefind]: https://pagefind.app
58
60
  [SemVer]: https://semver.org
@@ -80,7 +82,7 @@ First-time maintainers must learn multiple tools and concepts, e.g. [MyST], [Sem
80
82
 
81
83
 
82
84
  ## Core Features
83
- - [x] Write content directly in [MyST Markdown] or [Jupyter Notebook]
85
+ - [x] Write content directly in [MyST Markdown], [Jupyter Notebook] or [marimo] notebooks, with reproducible outputs for marimo
84
86
  - [x] Go from writing to **website deployment in minutes** — no need to learn any of the underlying tools
85
87
  - [x] Centralize all your content in a modern, **unified project website** — from documentation to blog posts
86
88
  - [x] Zero-config orchestration — Pre-configured modern tooling with sane defaults (see [Tech Stack](#tech-stack)), so you can start maintaining packages immediately **without learning each tool**
@@ -5,8 +5,8 @@
5
5
  [![PyPI](https://img.shields.io/pypi/v/afterpython.svg?cacheSeconds=300)](https://pypi.org/project/afterpython)
6
6
  ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/afterpython?cacheSeconds=300)
7
7
  [![Discussions](https://img.shields.io/badge/Discussions-Let's%20Chat-green)](https://github.com/AfterPythonOrg/afterpython/discussions)
8
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)
9
8
  [![View Code Wiki](https://assets.codewiki.google/readme-badge/static.svg)](https://codewiki.google/github.com/afterpythonorg/afterpython?utm_source=badge&utm_medium=github&utm_campaign=github.com/afterpythonorg/afterpython)
9
+ <!--[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)-->
10
10
  <!-- ![PyPI downloads](https://img.shields.io/pypi/dm/afterpython) -->
11
11
  <!-- [![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh) -->
12
12
 
@@ -14,6 +14,7 @@
14
14
  [MyST]: https://mystmd.org
15
15
  [MyST Markdown]: https://mystmd.org/spec/
16
16
  [Jupyter Notebook]: https://jupyter.org
17
+ [marimo]: https://marimo.io
17
18
  [pre-commit]: https://pre-commit.com
18
19
  [pagefind]: https://pagefind.app
19
20
  [SemVer]: https://semver.org
@@ -41,7 +42,7 @@ First-time maintainers must learn multiple tools and concepts, e.g. [MyST], [Sem
41
42
 
42
43
 
43
44
  ## Core Features
44
- - [x] Write content directly in [MyST Markdown] or [Jupyter Notebook]
45
+ - [x] Write content directly in [MyST Markdown], [Jupyter Notebook] or [marimo] notebooks, with reproducible outputs for marimo
45
46
  - [x] Go from writing to **website deployment in minutes** — no need to learn any of the underlying tools
46
47
  - [x] Centralize all your content in a modern, **unified project website** — from documentation to blog posts
47
48
  - [x] Zero-config orchestration — Pre-configured modern tooling with sane defaults (see [Tech Stack](#tech-stack)), so you can start maintaining packages immediately **without learning each tool**
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "afterpython"
3
- version = "0.4.0"
3
+ version = "0.4.2"
4
4
  description = "CLI tool to streamline Python package maintenance and generate a dedicated project website"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
@@ -22,27 +22,28 @@ classifiers = [
22
22
  "Topic :: Software Development :: Build Tools",
23
23
  "Topic :: Software Development :: Documentation",
24
24
  "Typing :: Typed",
25
- "Programming Language :: Python :: 3.12",
26
25
  "Programming Language :: Python :: 3.13",
27
26
  "Programming Language :: Python :: 3.14",
27
+ "Programming Language :: Python :: 3.15",
28
28
  ]
29
- requires-python = ">=3.12"
29
+ requires-python = ">=3.13"
30
30
  dependencies = [
31
- "click>=8.5.0",
31
+ "click>=8.5.1",
32
32
  "trogon>=0.6.0",
33
- "httpx2>=2.13.1",
34
- "tomlkit>=0.15.1",
33
+ "httpx2>=2.16.0",
34
+ "tomlkit>=0.15.2",
35
35
  "ruamel-yaml>=0.19.1",
36
- "gitpython>=3.2.0",
37
- "python-dotenv>=1.2.3",
36
+ "gitpython>=3.2.1",
37
+ "python-dotenv>=1.2.4",
38
38
  "pyproject-metadata>=0.12.1",
39
- "jupytext>=1.19.5",
40
- "mystmd>=1.11.0",
39
+ "jupytext>=1.19.6",
40
+ "mystmd>=1.12.0",
41
41
  "pdoc>=16.0.0",
42
- "ruff>=0.16.9",
43
- "prek>=0.5.4",
44
- "ty>=0.0.84",
42
+ "ruff>=0.17.4",
43
+ "prek>=0.5.10",
44
+ "ty>=0.0.99",
45
45
  "git-cliff>=2.14.2",
46
+ "questionary>=2.1.1",
46
47
  ]
47
48
 
48
49
  [[project.authors]]
@@ -50,7 +51,7 @@ name = "Stephen Yau"
50
51
  email = "softwareentrepreneer+afterpython@gmail.com"
51
52
 
52
53
  [project.optional-dependencies]
53
- marimo = ["marimo[recommended,lsp]>=0.25.0"]
54
+ marimo = ["marimo[recommended,lsp]>=0.25.2"]
54
55
 
55
56
  [project.urls]
56
57
  homepage = "https://afterpython.afterpython.org"
@@ -62,7 +63,7 @@ ap = "afterpython.main:run_cli"
62
63
  pcu = "afterpython.main:run_pcu"
63
64
 
64
65
  [build-system]
65
- requires = ["uv_build>=0.12.21,<0.13.0"]
66
+ requires = ["uv_build>=0.13.7,<0.14.0"]
66
67
  build-backend = "uv_build"
67
68
 
68
69
  [tool.uv.build-backend]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "afterpython"
3
- version = "0.4.0"
3
+ version = "0.4.2"
4
4
  description = "CLI tool to streamline Python package maintenance and generate a dedicated project website"
5
5
  license = "Apache-2.0"
6
6
  authors = [
@@ -25,32 +25,32 @@ classifiers = [
25
25
  "Topic :: Software Development :: Build Tools",
26
26
  "Topic :: Software Development :: Documentation",
27
27
  "Typing :: Typed",
28
- "Programming Language :: Python :: 3.12",
29
28
  "Programming Language :: Python :: 3.13",
30
29
  "Programming Language :: Python :: 3.14",
31
- # "Programming Language :: Python :: 3.15",
30
+ "Programming Language :: Python :: 3.15",
32
31
  ]
33
- requires-python = ">=3.12"
32
+ requires-python = ">=3.13"
34
33
  dependencies = [
35
- "click>=8.5.0",
34
+ "click>=8.5.1",
36
35
  "trogon>=0.6.0",
37
- "httpx2>=2.13.1",
38
- "tomlkit>=0.15.1",
36
+ "httpx2>=2.16.0",
37
+ "tomlkit>=0.15.2",
39
38
  "ruamel-yaml>=0.19.1",
40
- "gitpython>=3.2.0",
41
- "python-dotenv>=1.2.3",
39
+ "gitpython>=3.2.1",
40
+ "python-dotenv>=1.2.4",
42
41
  "pyproject-metadata>=0.12.1",
43
- "jupytext>=1.19.5",
44
- "mystmd>=1.11.0",
42
+ "jupytext>=1.19.6",
43
+ "mystmd>=1.12.0",
45
44
  "pdoc>=16.0.0",
46
- "ruff>=0.16.9",
47
- "prek>=0.5.4",
48
- "ty>=0.0.84",
45
+ "ruff>=0.17.4",
46
+ "prek>=0.5.10",
47
+ "ty>=0.0.99",
49
48
  "git-cliff>=2.14.2",
49
+ "questionary>=2.1.1",
50
50
  ]
51
51
 
52
52
  [project.optional-dependencies]
53
- marimo = ["marimo[recommended,lsp]>=0.25.0"]
53
+ marimo = ["marimo[recommended,lsp]>=0.25.2"]
54
54
 
55
55
  [project.urls]
56
56
  homepage = "https://afterpython.afterpython.org"
@@ -62,7 +62,7 @@ ap = "afterpython.main:run_cli"
62
62
  pcu = "afterpython.main:run_pcu"
63
63
 
64
64
  [build-system]
65
- requires = ["uv_build>=0.12.21,<0.13.0"]
65
+ requires = ["uv_build>=0.13.7,<0.14.0"]
66
66
  build-backend = "uv_build"
67
67
 
68
68
  [tool.uv]
@@ -1,4 +1,5 @@
1
1
  from afterpython.builders.build_pdoc import build_pdoc
2
+ from afterpython.builders.comments_json import build_comments_json
2
3
  from afterpython.builders.content_json import build_content_json
3
4
  from afterpython.builders.faq_json import build_faq_json
4
5
  from afterpython.builders.index_md import (
@@ -7,16 +8,19 @@ from afterpython.builders.index_md import (
7
8
  )
8
9
  from afterpython.builders.jupyter_notebook import build_jupyter_notebooks
9
10
  from afterpython.builders.llms_txt import build_llms_txt
11
+ from afterpython.builders.marimo_content import build_marimo_content
10
12
  from afterpython.builders.marimo_notebook import build_marimo_readme
11
13
  from afterpython.builders.markdown import build_markdown
12
14
  from afterpython.builders.metadata import build_metadata
13
15
  from afterpython.builders.url_md import build_url_md
14
16
 
15
17
  __all__ = (
18
+ "build_comments_json",
16
19
  "build_content_json",
17
20
  "build_faq_json",
18
21
  "build_jupyter_notebooks",
19
22
  "build_llms_txt",
23
+ "build_marimo_content",
20
24
  "build_marimo_readme",
21
25
  "build_markdown",
22
26
  "build_metadata",
@@ -19,9 +19,15 @@ def build_pdoc(base_path: str) -> None:
19
19
  project logo appears in the top-left of every API page and links
20
20
  back to the project website root (matches MyST's UX).
21
21
  """
22
+ from afterpython._io.toml import _from_tomlkit
23
+ from afterpython.tools._afterpython import read_afterpython
24
+
22
25
  package_dir = find_package_directory()
23
26
  output_dir = ap.paths.build_path / "api_reference"
24
27
  template_dir = ap.paths.templates_path / "pdoc"
28
+ # e.g. "google"; unset leaves it to pdoc (a module's __docformat__, else restructuredtext)
29
+ website = _from_tomlkit(read_afterpython().get("website", {}))
30
+ docformat = website.get("api_reference_docformat")
25
31
 
26
32
  click.echo("Building api_reference/...")
27
33
 
@@ -45,6 +51,7 @@ def build_pdoc(base_path: str) -> None:
45
51
  f"{base_path}/",
46
52
  "--template-directory",
47
53
  str(template_dir),
54
+ *(["--docformat", str(docformat)] if docformat else []),
48
55
  str(package_dir),
49
56
  ],
50
57
  env=env,
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+
6
+ import click
7
+
8
+ import afterpython as ap
9
+
10
+ GISCUS_API = "https://giscus.app/api/discussions/categories"
11
+ DEFAULT_CATEGORY = "Announcements"
12
+
13
+
14
+ def _read_comments_config() -> dict:
15
+ from afterpython._io.toml import _from_tomlkit
16
+ from afterpython.tools._afterpython import read_afterpython
17
+
18
+ website = _from_tomlkit(read_afterpython().get("website", {}))
19
+ return dict(website.get("comments", {}))
20
+
21
+
22
+ def _get_github_repo() -> str | None:
23
+ """Return "owner/repo" from pyproject.toml's repository URL, or the git remote."""
24
+ from afterpython.tools._git import get_github_url
25
+ from afterpython.tools.pyproject import read_metadata
26
+
27
+ url = str(read_metadata().urls.get("repository", "")) or get_github_url() or ""
28
+ match = re.search(r"github\.com[/:]([^/]+)/([^/]+?)(?:\.git)?/?$", url)
29
+ return f"{match[1]}/{match[2]}" if match else None
30
+
31
+
32
+ def _fetch_giscus_categories(repo: str) -> dict:
33
+ """Fetch the repo and category IDs giscus needs. Raises click.ClickException on failure."""
34
+ import httpx2
35
+
36
+ try:
37
+ response = httpx2.get(GISCUS_API, params={"repo": repo}, timeout=10.0)
38
+ except httpx2.HTTPError as e:
39
+ raise click.ClickException(f"could not reach giscus.app ({e})") from e
40
+ try:
41
+ data = response.json()
42
+ except ValueError:
43
+ data = {}
44
+ if response.status_code != 200 or "repositoryId" not in data:
45
+ error = data.get("error") or f"HTTP {response.status_code}"
46
+ raise click.ClickException(error)
47
+ return data
48
+
49
+
50
+ def build_comments_json():
51
+ """Build comments.json for the giscus comment section on content pages.
52
+
53
+ Reads [website.comments] in afterpython.toml. The website template only
54
+ shows comments when comments.json exists, so a disabled or failed setup
55
+ just leaves it out. Never fails the build.
56
+ """
57
+ stale = ap.paths.website_path / "static" / "comments.json"
58
+ stale.unlink(missing_ok=True)
59
+
60
+ config = _read_comments_config()
61
+ if not config.get("enabled", False):
62
+ click.echo("Comments are disabled, skip building comments.json")
63
+ return
64
+
65
+ def warn(reason: str):
66
+ click.secho(f"Warning: skip comments, {reason}", fg="yellow")
67
+
68
+ repo = _get_github_repo()
69
+ if not repo:
70
+ warn(
71
+ "no GitHub repository found "
72
+ "(set [project.urls] repository in pyproject.toml)"
73
+ )
74
+ return
75
+
76
+ category = str(config.get("category") or DEFAULT_CATEGORY)
77
+ try:
78
+ data = _fetch_giscus_categories(repo)
79
+ except click.ClickException as e:
80
+ warn(
81
+ f"giscus is not ready for {repo}: {e.message}\n"
82
+ f" 1. Enable Discussions: https://github.com/{repo}/settings\n"
83
+ " 2. Install the giscus app: https://github.com/apps/giscus\n"
84
+ " Or set [website.comments] enabled = false in afterpython.toml"
85
+ )
86
+ return
87
+
88
+ category_id = next(
89
+ (c["id"] for c in data.get("categories", []) if c["name"] == category), None
90
+ )
91
+ if not category_id:
92
+ names = ", ".join(c["name"] for c in data.get("categories", []))
93
+ warn(
94
+ f'Discussions category "{category}" not found in {repo} '
95
+ f"(available: {names or 'none'})"
96
+ )
97
+ return
98
+
99
+ click.echo(f"Building comments.json ({repo}, {category})...")
100
+ comments = {
101
+ "repo": repo,
102
+ "repoId": data["repositoryId"],
103
+ "category": category,
104
+ "categoryId": category_id,
105
+ }
106
+ with open(ap.paths.build_path / "comments.json", "w") as f:
107
+ json.dump(comments, f, indent=2)
@@ -17,7 +17,7 @@ def _read_notebook(notebook_path: Path, content_path: Path) -> dict | None:
17
17
  Notebook dict if valid, None if invalid or error occurred
18
18
  """
19
19
  try:
20
- with open(notebook_path) as f:
20
+ with open(notebook_path, encoding="utf-8") as f:
21
21
  notebook = json.load(f)
22
22
  except json.JSONDecodeError:
23
23
  click.echo(f"✗ Skipping {content_path} - invalid or empty JSON")
@@ -36,8 +36,11 @@ def _read_notebook(notebook_path: Path, content_path: Path) -> dict | None:
36
36
 
37
37
  def _write_notebook(notebook: dict, notebook_path: Path):
38
38
  """Write a notebook dict to file."""
39
- with open(notebook_path, "w") as f:
40
- json.dump(notebook, f, indent=1)
39
+ # match Jupyter's own formatting (unescaped unicode, trailing newline) so
40
+ # rewriting a committed notebook doesn't produce a noisy diff
41
+ with open(notebook_path, "w", encoding="utf-8") as f:
42
+ json.dump(notebook, f, indent=1, ensure_ascii=False)
43
+ f.write("\n")
41
44
 
42
45
 
43
46
  def _has_frontmatter(notebook: dict) -> bool:
@@ -243,8 +246,10 @@ def build_jupyter_notebooks():
243
246
 
244
247
  # Iterate over all .ipynb files once
245
248
  for notebook_path in path.rglob("*.ipynb"):
246
- # Skip files in _build directory
247
- if "_build" in notebook_path.parts:
249
+ # Skip MyST output and hidden tool dirs (.ipynb_checkpoints/,
250
+ # .virtual_documents/), which aren't content
251
+ rel_parts = notebook_path.relative_to(path).parts
252
+ if "_build" in rel_parts or any(p.startswith(".") for p in rel_parts):
248
253
  continue
249
254
 
250
255
  # Get path relative to afterpython/
@@ -264,9 +269,20 @@ def build_jupyter_notebooks():
264
269
  # Track if notebook was modified
265
270
  modified = False
266
271
 
267
- # Add molab badge if enabled
272
+ # Add molab badge if enabled. A notebook generated from a marimo .py
273
+ # isn't committed, so the badge must open the .py instead.
268
274
  if add_molab_badge and github_url:
269
- _add_molab_badge_to_notebook(notebook, github_url, content_path)
275
+ generated_from = (
276
+ notebook.get("metadata", {})
277
+ .get("afterpython", {})
278
+ .get("generated_from")
279
+ )
280
+ molab_path = (
281
+ content_path.with_name(generated_from)
282
+ if generated_from
283
+ else content_path
284
+ )
285
+ _add_molab_badge_to_notebook(notebook, github_url, molab_path)
270
286
  modified = True
271
287
 
272
288
  # Add thumbnail to frontmatter