afterpython 0.3.19__tar.gz → 0.4.1__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 (83) hide show
  1. {afterpython-0.3.19 → afterpython-0.4.1}/PKG-INFO +24 -20
  2. {afterpython-0.3.19 → afterpython-0.4.1}/README.md +5 -5
  3. {afterpython-0.3.19 → afterpython-0.4.1}/pyproject.toml +23 -21
  4. {afterpython-0.3.19 → afterpython-0.4.1}/pyproject.toml.orig +24 -29
  5. afterpython-0.4.1/src/afterpython/_typing.py +4 -0
  6. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/__init__.py +2 -0
  7. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/build_pdoc.py +7 -0
  8. afterpython-0.4.1/src/afterpython/builders/comments_json.py +107 -0
  9. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/marimo_notebook.py +7 -0
  10. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/build.py +5 -3
  11. afterpython-0.4.1/src/afterpython/cli/commands/bump.py +333 -0
  12. afterpython-0.4.1/src/afterpython/cli/commands/changelog.py +45 -0
  13. afterpython-0.4.1/src/afterpython/cli/commands/check.py +43 -0
  14. afterpython-0.4.1/src/afterpython/cli/commands/commit_msg.py +115 -0
  15. afterpython-0.4.1/src/afterpython/cli/commands/init.py +406 -0
  16. afterpython-0.3.19/src/afterpython/cli/commands/commitizen.py → afterpython-0.4.1/src/afterpython/cli/commands/lint.py +7 -10
  17. afterpython-0.3.19/src/afterpython/cli/commands/pre_commit.py → afterpython-0.4.1/src/afterpython/cli/commands/prek.py +8 -8
  18. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/release.py +39 -10
  19. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/start.py +6 -4
  20. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/update.py +8 -7
  21. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/main.py +9 -9
  22. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/const.py +1 -1
  23. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/pcu.py +45 -40
  24. afterpython-0.4.1/src/afterpython/templates/afterpython-template.toml +39 -0
  25. afterpython-0.4.1/src/afterpython/templates/ci-workflow-template.yml +251 -0
  26. afterpython-0.4.1/src/afterpython/templates/cliff-template.toml +111 -0
  27. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/templates/deploy-workflow-template.yml +14 -18
  28. afterpython-0.4.1/src/afterpython/templates/pdoc/custom.css +37 -0
  29. afterpython-0.4.1/src/afterpython/templates/pdoc/frame.html.jinja2 +21 -0
  30. afterpython-0.4.1/src/afterpython/templates/pdoc/syntax-highlighting.css +155 -0
  31. afterpython-0.4.1/src/afterpython/templates/pdoc/theme.css +44 -0
  32. afterpython-0.4.1/src/afterpython/templates/pre-commit-config-template.yaml +68 -0
  33. afterpython-0.4.1/src/afterpython/templates/release-workflow-template.yml +107 -0
  34. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/templates/ruff-template.toml +8 -1
  35. afterpython-0.4.1/src/afterpython/templates/ty-template.toml +19 -0
  36. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/_afterpython.py +31 -21
  37. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/_git.py +56 -0
  38. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/branch_rules.py +3 -1
  39. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/myst.py +36 -19
  40. afterpython-0.4.1/src/afterpython/tools/prek.py +94 -0
  41. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/utils.py +1 -1
  42. afterpython-0.3.19/src/afterpython/_typing.py +0 -4
  43. afterpython-0.3.19/src/afterpython/cli/commands/bump.py +0 -118
  44. afterpython-0.3.19/src/afterpython/cli/commands/check.py +0 -36
  45. afterpython-0.3.19/src/afterpython/cli/commands/commit.py +0 -52
  46. afterpython-0.3.19/src/afterpython/cli/commands/init.py +0 -161
  47. afterpython-0.3.19/src/afterpython/templates/ci-workflow-template.yml +0 -220
  48. afterpython-0.3.19/src/afterpython/templates/cz-template.toml +0 -7
  49. afterpython-0.3.19/src/afterpython/templates/pdoc/frame.html.jinja2 +0 -7
  50. afterpython-0.3.19/src/afterpython/templates/pre-commit-config-template.yaml +0 -49
  51. afterpython-0.3.19/src/afterpython/templates/release-workflow-template.yml +0 -56
  52. afterpython-0.3.19/src/afterpython/tools/commitizen.py +0 -13
  53. afterpython-0.3.19/src/afterpython/tools/pre_commit.py +0 -37
  54. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/__init__.py +0 -0
  55. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/_io/toml.py +0 -0
  56. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/_io/yaml.py +0 -0
  57. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/_paths.py +0 -0
  58. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/content_json.py +0 -0
  59. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/faq_json.py +0 -0
  60. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/index_md.py +0 -0
  61. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/jupyter_notebook.py +0 -0
  62. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/llms_txt.py +0 -0
  63. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/markdown.py +0 -0
  64. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/metadata.py +0 -0
  65. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/builders/url_md.py +0 -0
  66. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/__init__.py +0 -0
  67. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/add.py +0 -0
  68. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/clean.py +0 -0
  69. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/dev.py +0 -0
  70. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/format.py +0 -0
  71. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/init_branch_rules.py +0 -0
  72. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/install.py +0 -0
  73. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/lock.py +0 -0
  74. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/preview.py +0 -0
  75. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/remove.py +0 -0
  76. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/serve.py +0 -0
  77. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/cli/commands/sync.py +0 -0
  78. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/main.py +0 -0
  79. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/py.typed +0 -0
  80. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/templates/dependabot-template.yml +0 -0
  81. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/github_actions.py +0 -0
  82. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/tools/pyproject.py +0 -0
  83. {afterpython-0.3.19 → afterpython-0.4.1}/src/afterpython/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afterpython
3
- Version: 0.3.19
3
+ Version: 0.4.1
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,27 +11,31 @@ 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.11
15
- Classifier: Programming Language :: Python :: 3.12
16
14
  Classifier: Programming Language :: Python :: 3.13
17
- Requires-Dist: click>=8.1.8
15
+ Classifier: Programming Language :: Python :: 3.14
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.5.0
20
- Requires-Dist: tomlkit>=0.15.0
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.1.50
23
- Requires-Dist: python-dotenv>=1.2.2
24
- Requires-Dist: pyproject-metadata>=0.11.0
25
- Requires-Dist: jupytext>=1.19.4
26
- Requires-Dist: mystmd>=1.10.1
22
+ Requires-Dist: gitpython>=3.2.1
23
+ Requires-Dist: python-dotenv>=1.2.4
24
+ Requires-Dist: pyproject-metadata>=0.12.1
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.15.21
29
- Requires-Dist: pre-commit>=4.6.0
30
- Requires-Dist: commitizen>=4.16.4
31
- Requires-Python: >=3.11
28
+ Requires-Dist: ruff>=0.17.4
29
+ Requires-Dist: prek>=0.5.10
30
+ Requires-Dist: ty>=0.0.99
31
+ Requires-Dist: git-cliff>=2.14.2
32
+ Requires-Dist: questionary>=2.1.1
33
+ Requires-Dist: marimo[recommended,lsp]>=0.25.2 ; extra == 'marimo'
34
+ Requires-Python: >=3.13
32
35
  Project-URL: homepage, https://afterpython.afterpython.org
33
36
  Project-URL: repository, https://github.com/AfterPythonOrg/afterpython
34
37
  Project-URL: documentation, https://afterpython.afterpython.org/doc
38
+ Provides-Extra: marimo
35
39
  Description-Content-Type: text/markdown
36
40
 
37
41
  # AfterPython: Python Package Maintenance Toolkit and Project Website Generator
@@ -41,8 +45,8 @@ Description-Content-Type: text/markdown
41
45
  [![PyPI](https://img.shields.io/pypi/v/afterpython.svg?cacheSeconds=300)](https://pypi.org/project/afterpython)
42
46
  ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/afterpython?cacheSeconds=300)
43
47
  [![Discussions](https://img.shields.io/badge/Discussions-Let's%20Chat-green)](https://github.com/AfterPythonOrg/afterpython/discussions)
44
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)
45
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)-->
46
50
  <!-- ![PyPI downloads](https://img.shields.io/pypi/dm/afterpython) -->
47
51
  <!-- [![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh) -->
48
52
 
@@ -59,10 +63,10 @@ Description-Content-Type: text/markdown
59
63
  [Scikit-Learn]: https://scikit-learn.org
60
64
  [WebLLM]: https://webllm.mlc.ai/
61
65
  [project-website-template]: https://github.com/AfterPythonOrg/project-website-template
62
- [commitizen]: https://github.com/commitizen-tools/commitizen
63
66
  [uv]: https://docs.astral.sh/uv/
64
67
  [ruff]: https://docs.astral.sh/ruff/
65
-
68
+ [ty]: https://docs.astral.sh/ty/
69
+ [git-cliff]: https://git-cliff.org/
66
70
 
67
71
  ## Problem
68
72
  Going from **writing Python code to publishing and maintaining a package** is **time-consuming**.
@@ -125,10 +129,10 @@ ap tui
125
129
  - [pre-commit]
126
130
  - [GitHub Actions]
127
131
  - [pdoc]
128
- - [commitizen]
129
132
  - [uv]
130
133
  - [ruff]
134
+ - [ty]
131
135
  - [pagefind]
132
136
  - [WebLLM]
133
- <!-- - ty -->
137
+ - [git-cliff]
134
138
  <!-- - [pixi] -->
@@ -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
 
@@ -23,10 +23,10 @@
23
23
  [Scikit-Learn]: https://scikit-learn.org
24
24
  [WebLLM]: https://webllm.mlc.ai/
25
25
  [project-website-template]: https://github.com/AfterPythonOrg/project-website-template
26
- [commitizen]: https://github.com/commitizen-tools/commitizen
27
26
  [uv]: https://docs.astral.sh/uv/
28
27
  [ruff]: https://docs.astral.sh/ruff/
29
-
28
+ [ty]: https://docs.astral.sh/ty/
29
+ [git-cliff]: https://git-cliff.org/
30
30
 
31
31
  ## Problem
32
32
  Going from **writing Python code to publishing and maintaining a package** is **time-consuming**.
@@ -89,10 +89,10 @@ ap tui
89
89
  - [pre-commit]
90
90
  - [GitHub Actions]
91
91
  - [pdoc]
92
- - [commitizen]
93
92
  - [uv]
94
93
  - [ruff]
94
+ - [ty]
95
95
  - [pagefind]
96
96
  - [WebLLM]
97
- <!-- - ty -->
97
+ - [git-cliff]
98
98
  <!-- - [pixi] -->
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "afterpython"
3
- version = "0.3.19"
3
+ version = "0.4.1"
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,32 +22,37 @@ classifiers = [
22
22
  "Topic :: Software Development :: Build Tools",
23
23
  "Topic :: Software Development :: Documentation",
24
24
  "Typing :: Typed",
25
- "Programming Language :: Python :: 3.11",
26
- "Programming Language :: Python :: 3.12",
27
25
  "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Programming Language :: Python :: 3.15",
28
28
  ]
29
- requires-python = ">=3.11"
29
+ requires-python = ">=3.13"
30
30
  dependencies = [
31
- "click>=8.1.8",
31
+ "click>=8.5.1",
32
32
  "trogon>=0.6.0",
33
- "httpx2>=2.5.0",
34
- "tomlkit>=0.15.0",
33
+ "httpx2>=2.16.0",
34
+ "tomlkit>=0.15.2",
35
35
  "ruamel-yaml>=0.19.1",
36
- "gitpython>=3.1.50",
37
- "python-dotenv>=1.2.2",
38
- "pyproject-metadata>=0.11.0",
39
- "jupytext>=1.19.4",
40
- "mystmd>=1.10.1",
36
+ "gitpython>=3.2.1",
37
+ "python-dotenv>=1.2.4",
38
+ "pyproject-metadata>=0.12.1",
39
+ "jupytext>=1.19.6",
40
+ "mystmd>=1.12.0",
41
41
  "pdoc>=16.0.0",
42
- "ruff>=0.15.21",
43
- "pre-commit>=4.6.0",
44
- "commitizen>=4.16.4",
42
+ "ruff>=0.17.4",
43
+ "prek>=0.5.10",
44
+ "ty>=0.0.99",
45
+ "git-cliff>=2.14.2",
46
+ "questionary>=2.1.1",
45
47
  ]
46
48
 
47
49
  [[project.authors]]
48
50
  name = "Stephen Yau"
49
51
  email = "softwareentrepreneer+afterpython@gmail.com"
50
52
 
53
+ [project.optional-dependencies]
54
+ marimo = ["marimo[recommended,lsp]>=0.25.2"]
55
+
51
56
  [project.urls]
52
57
  homepage = "https://afterpython.afterpython.org"
53
58
  repository = "https://github.com/AfterPythonOrg/afterpython"
@@ -58,15 +63,12 @@ ap = "afterpython.main:run_cli"
58
63
  pcu = "afterpython.main:run_pcu"
59
64
 
60
65
  [build-system]
61
- requires = ["uv_build>=0.11.28,<0.12.0"]
66
+ requires = ["uv_build>=0.13.7,<0.14.0"]
62
67
  build-backend = "uv_build"
63
68
 
64
- [tool.uv]
65
- exclude-newer = "14 days"
66
-
67
69
  [tool.uv.build-backend]
68
70
  module-name = "afterpython"
69
71
  module-root = "src"
70
72
 
71
- [tool.pyright]
72
- typeCheckingMode = "basic"
73
+ [tool.ruff]
74
+ extend = "afterpython/ruff.toml"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "afterpython"
3
- version = "0.3.19"
3
+ version = "0.4.1"
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,33 @@ classifiers = [
25
25
  "Topic :: Software Development :: Build Tools",
26
26
  "Topic :: Software Development :: Documentation",
27
27
  "Typing :: Typed",
28
- "Programming Language :: Python :: 3.11",
29
- "Programming Language :: Python :: 3.12",
30
28
  "Programming Language :: Python :: 3.13",
31
- # "Programming Language :: Python :: 3.14",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Programming Language :: Python :: 3.15",
32
31
  ]
33
- requires-python = ">=3.11"
32
+ requires-python = ">=3.13"
34
33
  dependencies = [
35
- "click>=8.1.8",
34
+ "click>=8.5.1",
36
35
  "trogon>=0.6.0",
37
- "httpx2>=2.5.0",
38
- "tomlkit>=0.15.0",
36
+ "httpx2>=2.16.0",
37
+ "tomlkit>=0.15.2",
39
38
  "ruamel-yaml>=0.19.1",
40
- "gitpython>=3.1.50",
41
- # "pygithub>=2.8.1",
42
- "python-dotenv>=1.2.2",
43
- "pyproject-metadata>=0.11.0",
44
- "jupytext>=1.19.4",
45
- # "jinja2>=3.1.6",
46
- "mystmd>=1.10.1",
39
+ "gitpython>=3.2.1",
40
+ "python-dotenv>=1.2.4",
41
+ "pyproject-metadata>=0.12.1",
42
+ "jupytext>=1.19.6",
43
+ "mystmd>=1.12.0",
47
44
  "pdoc>=16.0.0",
48
- "ruff>=0.15.21",
49
- "pre-commit>=4.6.0",
50
- "commitizen>=4.16.4",
51
- # "python-semantic-release>=10.5.2",
45
+ "ruff>=0.17.4",
46
+ "prek>=0.5.10",
47
+ "ty>=0.0.99",
48
+ "git-cliff>=2.14.2",
49
+ "questionary>=2.1.1",
52
50
  ]
53
51
 
52
+ [project.optional-dependencies]
53
+ marimo = ["marimo[recommended,lsp]>=0.25.2"]
54
+
54
55
  [project.urls]
55
56
  homepage = "https://afterpython.afterpython.org"
56
57
  repository = "https://github.com/AfterPythonOrg/afterpython"
@@ -60,22 +61,16 @@ documentation = "https://afterpython.afterpython.org/doc"
60
61
  ap = "afterpython.main:run_cli"
61
62
  pcu = "afterpython.main:run_pcu"
62
63
 
63
- # [tool.uv]
64
- # override-dependencies = ["platformdirs>=4.5.0", "nodeenv>=1.9.1"]
65
-
66
64
  [build-system]
67
- requires = ["uv_build>=0.11.28,<0.12.0"]
65
+ requires = ["uv_build>=0.13.7,<0.14.0"]
68
66
  build-backend = "uv_build"
69
67
 
70
68
  [tool.uv]
71
- exclude-newer = "14 days"
69
+ # exclude-newer = "14 days"
72
70
 
73
71
  [tool.uv.build-backend]
74
72
  module-name = "afterpython"
75
73
  module-root = "src"
76
74
 
77
- # [tool.uv.sources]
78
- # afterpython = { path = ".", editable = true }
79
-
80
- [tool.pyright]
81
- typeCheckingMode = "basic"
75
+ [tool.ruff]
76
+ extend = "afterpython/ruff.toml"
@@ -0,0 +1,4 @@
1
+ from typing import Literal
2
+
3
+ type NodeEnv = dict[str, str]
4
+ tContentType = Literal["doc", "blog", "tutorial", "example", "guide"]
@@ -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 (
@@ -13,6 +14,7 @@ from afterpython.builders.metadata import build_metadata
13
14
  from afterpython.builders.url_md import build_url_md
14
15
 
15
16
  __all__ = (
17
+ "build_comments_json",
16
18
  "build_content_json",
17
19
  "build_faq_json",
18
20
  "build_jupyter_notebooks",
@@ -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)
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import re
5
+ import shutil
5
6
  import subprocess
6
7
  from pathlib import Path
7
8
  from typing import Literal
@@ -48,6 +49,12 @@ def _export_marimo(
48
49
  execute: bool = False,
49
50
  ):
50
51
  """Run `marimo export` to produce HTML at `output_html`."""
52
+ # marimo is an optional dependency (the `marimo` extra)
53
+ if shutil.which("marimo") is None:
54
+ raise click.ClickException(
55
+ f"marimo is required to build {source.name} (a marimo notebook), "
56
+ 'install it with: uv add --dev "afterpython[marimo]"'
57
+ )
51
58
  output_html.parent.mkdir(parents=True, exist_ok=True)
52
59
  subcommand = "html-wasm" if mode == "wasm" else "html"
53
60
  cmd = [
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Callable
3
+ from collections.abc import Callable, Iterable
4
4
  from typing import TYPE_CHECKING
5
5
 
6
6
  if TYPE_CHECKING:
@@ -17,6 +17,7 @@ from click.exceptions import Exit
17
17
 
18
18
  import afterpython as ap
19
19
  from afterpython.builders import (
20
+ build_comments_json,
20
21
  build_content_json,
21
22
  build_faq_json,
22
23
  build_jupyter_notebooks,
@@ -137,6 +138,7 @@ def prebuild():
137
138
  create_placeholder_index_md_files()
138
139
  build_metadata()
139
140
  build_faq_json()
141
+ build_comments_json()
140
142
  build_marimo_readme(mode=_get_readme_py_mode())
141
143
  build_markdown()
142
144
  build_jupyter_notebooks()
@@ -147,7 +149,7 @@ def postbuild(dev_build: bool = False):
147
149
  source: Path,
148
150
  destination: Path,
149
151
  is_copy: bool = True,
150
- ignore_copy: Callable[[str, list[str]], set[str]] | None = None,
152
+ ignore_copy: Callable[[str, list[str]], Iterable[str]] | None = None,
151
153
  ):
152
154
  """
153
155
  Move or copy files from source to destination.
@@ -156,7 +158,7 @@ def postbuild(dev_build: bool = False):
156
158
  source: Source path
157
159
  destination: Destination path
158
160
  is_copy: If True, copy files (merge with existing). If False, move files (replace destination)
159
- ignore_copy: Optional ignore function for copytree, takes (directory, contents) and returns set of names to ignore
161
+ ignore_copy: Optional ignore function for copytree, takes (directory, contents) and returns the names to ignore
160
162
  """
161
163
  if not source.exists():
162
164
  return