afterpython 0.4.0__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.
- {afterpython-0.4.0 → afterpython-0.4.1}/PKG-INFO +16 -15
- {afterpython-0.4.0 → afterpython-0.4.1}/README.md +1 -1
- {afterpython-0.4.0 → afterpython-0.4.1}/pyproject.toml +16 -15
- {afterpython-0.4.0 → afterpython-0.4.1}/pyproject.toml.orig +16 -16
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/__init__.py +2 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/build_pdoc.py +7 -0
- afterpython-0.4.1/src/afterpython/builders/comments_json.py +107 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/build.py +2 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/init.py +113 -51
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/const.py +1 -1
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/afterpython-template.toml +6 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/ci-workflow-template.yml +10 -10
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/deploy-workflow-template.yml +2 -2
- afterpython-0.4.1/src/afterpython/templates/pdoc/custom.css +37 -0
- afterpython-0.4.1/src/afterpython/templates/pdoc/frame.html.jinja2 +21 -0
- afterpython-0.4.1/src/afterpython/templates/pdoc/syntax-highlighting.css +155 -0
- afterpython-0.4.1/src/afterpython/templates/pdoc/theme.css +44 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/release-workflow-template.yml +1 -1
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/ruff-template.toml +1 -1
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/myst.py +36 -19
- afterpython-0.4.0/src/afterpython/templates/pdoc/frame.html.jinja2 +0 -7
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/__init__.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/_io/toml.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/_io/yaml.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/_paths.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/_typing.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/content_json.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/faq_json.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/index_md.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/jupyter_notebook.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/llms_txt.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/marimo_notebook.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/markdown.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/metadata.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/builders/url_md.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/__init__.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/add.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/bump.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/changelog.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/check.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/clean.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/commit_msg.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/dev.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/format.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/init_branch_rules.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/install.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/lint.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/lock.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/prek.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/preview.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/release.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/remove.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/serve.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/start.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/sync.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/commands/update.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/cli/main.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/main.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/pcu.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/py.typed +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/cliff-template.toml +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/dependabot-template.yml +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/pre-commit-config-template.yaml +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/templates/ty-template.toml +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/_afterpython.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/_git.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/branch_rules.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/github_actions.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/prek.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/tools/pyproject.py +0 -0
- {afterpython-0.4.0 → afterpython-0.4.1}/src/afterpython/utils.py +0 -0
- {afterpython-0.4.0 → 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.4.
|
|
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,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
|
-
|
|
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.
|
|
20
|
-
Requires-Dist: tomlkit>=0.15.
|
|
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.
|
|
23
|
-
Requires-Dist: python-dotenv>=1.2.
|
|
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.
|
|
26
|
-
Requires-Dist: mystmd>=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.
|
|
29
|
-
Requires-Dist: prek>=0.5.
|
|
30
|
-
Requires-Dist: ty>=0.0.
|
|
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:
|
|
33
|
-
Requires-
|
|
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
|
[](https://pypi.org/project/afterpython)
|
|
45
46
|

|
|
46
47
|
[](https://github.com/AfterPythonOrg/afterpython/discussions)
|
|
47
|
-
[](https://deepwiki.com/AfterPythonOrg/afterpython)
|
|
48
48
|
[](https://codewiki.google/github.com/afterpythonorg/afterpython?utm_source=badge&utm_medium=github&utm_campaign=github.com/afterpythonorg/afterpython)
|
|
49
|
+
<!--[](https://deepwiki.com/AfterPythonOrg/afterpython)-->
|
|
49
50
|
<!--  -->
|
|
50
51
|
<!-- [](https://pixi.sh) -->
|
|
51
52
|
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
[](https://pypi.org/project/afterpython)
|
|
6
6
|

|
|
7
7
|
[](https://github.com/AfterPythonOrg/afterpython/discussions)
|
|
8
|
-
[](https://deepwiki.com/AfterPythonOrg/afterpython)
|
|
9
8
|
[](https://codewiki.google/github.com/afterpythonorg/afterpython?utm_source=badge&utm_medium=github&utm_campaign=github.com/afterpythonorg/afterpython)
|
|
9
|
+
<!--[](https://deepwiki.com/AfterPythonOrg/afterpython)-->
|
|
10
10
|
<!--  -->
|
|
11
11
|
<!-- [](https://pixi.sh) -->
|
|
12
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "afterpython"
|
|
3
|
-
version = "0.4.
|
|
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,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.
|
|
29
|
+
requires-python = ">=3.13"
|
|
30
30
|
dependencies = [
|
|
31
|
-
"click>=8.5.
|
|
31
|
+
"click>=8.5.1",
|
|
32
32
|
"trogon>=0.6.0",
|
|
33
|
-
"httpx2>=2.
|
|
34
|
-
"tomlkit>=0.15.
|
|
33
|
+
"httpx2>=2.16.0",
|
|
34
|
+
"tomlkit>=0.15.2",
|
|
35
35
|
"ruamel-yaml>=0.19.1",
|
|
36
|
-
"gitpython>=3.2.
|
|
37
|
-
"python-dotenv>=1.2.
|
|
36
|
+
"gitpython>=3.2.1",
|
|
37
|
+
"python-dotenv>=1.2.4",
|
|
38
38
|
"pyproject-metadata>=0.12.1",
|
|
39
|
-
"jupytext>=1.19.
|
|
40
|
-
"mystmd>=1.
|
|
39
|
+
"jupytext>=1.19.6",
|
|
40
|
+
"mystmd>=1.12.0",
|
|
41
41
|
"pdoc>=16.0.0",
|
|
42
|
-
"ruff>=0.
|
|
43
|
-
"prek>=0.5.
|
|
44
|
-
"ty>=0.0.
|
|
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.
|
|
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.
|
|
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.
|
|
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,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
|
-
|
|
30
|
+
"Programming Language :: Python :: 3.15",
|
|
32
31
|
]
|
|
33
|
-
requires-python = ">=3.
|
|
32
|
+
requires-python = ">=3.13"
|
|
34
33
|
dependencies = [
|
|
35
|
-
"click>=8.5.
|
|
34
|
+
"click>=8.5.1",
|
|
36
35
|
"trogon>=0.6.0",
|
|
37
|
-
"httpx2>=2.
|
|
38
|
-
"tomlkit>=0.15.
|
|
36
|
+
"httpx2>=2.16.0",
|
|
37
|
+
"tomlkit>=0.15.2",
|
|
39
38
|
"ruamel-yaml>=0.19.1",
|
|
40
|
-
"gitpython>=3.2.
|
|
41
|
-
"python-dotenv>=1.2.
|
|
39
|
+
"gitpython>=3.2.1",
|
|
40
|
+
"python-dotenv>=1.2.4",
|
|
42
41
|
"pyproject-metadata>=0.12.1",
|
|
43
|
-
"jupytext>=1.19.
|
|
44
|
-
"mystmd>=1.
|
|
42
|
+
"jupytext>=1.19.6",
|
|
43
|
+
"mystmd>=1.12.0",
|
|
45
44
|
"pdoc>=16.0.0",
|
|
46
|
-
"ruff>=0.
|
|
47
|
-
"prek>=0.5.
|
|
48
|
-
"ty>=0.0.
|
|
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.
|
|
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.
|
|
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 (
|
|
@@ -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)
|
|
@@ -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()
|
|
@@ -169,14 +169,62 @@ def init_py_typed():
|
|
|
169
169
|
click.echo(f"Created {py_typed_path}")
|
|
170
170
|
|
|
171
171
|
|
|
172
|
-
def _preflight_init(
|
|
172
|
+
def _preflight_init(website: bool) -> None:
|
|
173
173
|
"""Validate external prerequisites before any filesystem writes, so a
|
|
174
174
|
failure aborts cleanly instead of leaving a half-initialized project."""
|
|
175
|
-
if
|
|
176
|
-
from afterpython.tools.myst import
|
|
175
|
+
if website:
|
|
176
|
+
from afterpython.tools.myst import ensure_pnpm_12
|
|
177
177
|
from afterpython.utils import find_node_env
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
ensure_pnpm_12(find_node_env())
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _select_steps(yes: bool, skip_website: bool, ask_commit_types: bool) -> set[str]:
|
|
183
|
+
"""Ask which optional setup steps to run, as one checkbox list (all checked by default).
|
|
184
|
+
|
|
185
|
+
Without a terminal (CI, piped input) or with --yes, nothing is asked and the defaults are used.
|
|
186
|
+
"""
|
|
187
|
+
import sys
|
|
188
|
+
|
|
189
|
+
import questionary
|
|
190
|
+
|
|
191
|
+
# (step, label, checked by default)
|
|
192
|
+
options = [
|
|
193
|
+
(
|
|
194
|
+
"website",
|
|
195
|
+
"Project website (MyST, template, deploy workflow)",
|
|
196
|
+
not skip_website,
|
|
197
|
+
)
|
|
198
|
+
]
|
|
199
|
+
# only relevant when afterpython.toml is created, it's ignored otherwise
|
|
200
|
+
if ask_commit_types:
|
|
201
|
+
options.append(
|
|
202
|
+
(
|
|
203
|
+
"commit_types",
|
|
204
|
+
"Commit message check (conventional commit types, e.g. 'feat: add X')",
|
|
205
|
+
True,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
options += [
|
|
209
|
+
("prek", "Pre-commit hooks (prek)", True),
|
|
210
|
+
("ruff", "ruff: lint & format", True),
|
|
211
|
+
("ty", "ty: type checking (`ap check`)", True),
|
|
212
|
+
("cliff", "git-cliff: changelog", True),
|
|
213
|
+
("release", "Release workflow (.github/workflows/release.yml)", True),
|
|
214
|
+
("dependabot", "Dependabot (auto-update GitHub Actions versions)", True),
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
if yes or not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
218
|
+
return {step for step, _, checked in options if checked}
|
|
219
|
+
|
|
220
|
+
choices = [
|
|
221
|
+
questionary.Choice(label, value=step, checked=checked)
|
|
222
|
+
for step, label, checked in options
|
|
223
|
+
]
|
|
224
|
+
selected = questionary.checkbox("Select what to set up", choices=choices).ask()
|
|
225
|
+
if selected is None: # Ctrl-C, before anything was written
|
|
226
|
+
raise click.Abort()
|
|
227
|
+
return set(selected)
|
|
180
228
|
|
|
181
229
|
|
|
182
230
|
@click.group(invoke_without_command=True)
|
|
@@ -184,7 +232,7 @@ def _preflight_init(skip_website: bool) -> None:
|
|
|
184
232
|
"--yes",
|
|
185
233
|
"-y",
|
|
186
234
|
is_flag=True,
|
|
187
|
-
help="
|
|
235
|
+
help="Set up everything without asking",
|
|
188
236
|
)
|
|
189
237
|
@click.option(
|
|
190
238
|
"--skip-website",
|
|
@@ -205,32 +253,27 @@ def init(ctx, yes, skip_website: bool):
|
|
|
205
253
|
from afterpython.tools.prek import init_prek, sync_hooks
|
|
206
254
|
from afterpython.tools.pyproject import init_pyproject
|
|
207
255
|
|
|
208
|
-
_preflight_init(skip_website)
|
|
209
|
-
|
|
210
256
|
paths = ctx.obj["paths"]
|
|
211
|
-
click.echo("Initializing afterpython...")
|
|
212
257
|
afterpython_path = paths.afterpython_path
|
|
213
258
|
static_path = paths.static_path
|
|
214
259
|
|
|
260
|
+
steps = _select_steps(
|
|
261
|
+
yes,
|
|
262
|
+
skip_website,
|
|
263
|
+
ask_commit_types=not (afterpython_path / "afterpython.toml").exists(),
|
|
264
|
+
)
|
|
265
|
+
_preflight_init(website="website" in steps)
|
|
266
|
+
|
|
267
|
+
click.echo("Initializing afterpython...")
|
|
215
268
|
try:
|
|
216
269
|
afterpython_path.mkdir(parents=True, exist_ok=True)
|
|
217
270
|
static_path.mkdir(parents=True, exist_ok=True)
|
|
218
271
|
|
|
219
272
|
init_pyproject()
|
|
220
273
|
|
|
221
|
-
|
|
222
|
-
commit_types = (
|
|
223
|
-
yes
|
|
224
|
-
or (afterpython_path / "afterpython.toml").exists()
|
|
225
|
-
or click.confirm(
|
|
226
|
-
"\nCheck commit messages against conventional commit types "
|
|
227
|
-
"(e.g. 'feat: add X', types listed in afterpython.toml)?",
|
|
228
|
-
default=True,
|
|
229
|
-
)
|
|
230
|
-
)
|
|
231
|
-
init_afterpython(commit_types=commit_types)
|
|
274
|
+
init_afterpython(commit_types="commit_types" in steps)
|
|
232
275
|
|
|
233
|
-
if
|
|
276
|
+
if "website" in steps:
|
|
234
277
|
# check=True so a failure inside `ap init website` aborts the parent
|
|
235
278
|
# run instead of silently continuing to write more files.
|
|
236
279
|
subprocess.run(["ap", "init", "website"], check=True)
|
|
@@ -239,42 +282,20 @@ def init(ctx, yes, skip_website: bool):
|
|
|
239
282
|
|
|
240
283
|
create_workflow("ci")
|
|
241
284
|
|
|
242
|
-
if
|
|
243
|
-
f"\nCreate .pre-commit-config.yaml in {afterpython_path}?", default=True
|
|
244
|
-
):
|
|
285
|
+
if "prek" in steps:
|
|
245
286
|
init_prek()
|
|
246
|
-
|
|
247
|
-
if yes or click.confirm(
|
|
248
|
-
f"\nCreate ruff.toml in {afterpython_path} (and point pyproject.toml's [tool.ruff] to it)?",
|
|
249
|
-
default=True,
|
|
250
|
-
):
|
|
287
|
+
if "ruff" in steps:
|
|
251
288
|
init_ruff()
|
|
252
|
-
|
|
253
|
-
if yes or click.confirm(
|
|
254
|
-
f"\nCreate ty.toml in {afterpython_path} (type checking with `ap check`)?",
|
|
255
|
-
default=True,
|
|
256
|
-
):
|
|
289
|
+
if "ty" in steps:
|
|
257
290
|
init_ty()
|
|
258
|
-
# whatever
|
|
291
|
+
# whatever was selected: keep ruff/ty hooks only for the tools that were set up
|
|
259
292
|
sync_hooks()
|
|
260
293
|
|
|
261
|
-
if
|
|
262
|
-
f"\nCreate cliff.toml in {afterpython_path} (changelog generation with git-cliff)?",
|
|
263
|
-
default=True,
|
|
264
|
-
):
|
|
294
|
+
if "cliff" in steps:
|
|
265
295
|
init_cliff()
|
|
266
|
-
|
|
267
|
-
if yes or click.confirm(
|
|
268
|
-
"\nCreate release workflow in .github/workflows/release.yml?",
|
|
269
|
-
default=True,
|
|
270
|
-
):
|
|
296
|
+
if "release" in steps:
|
|
271
297
|
create_workflow("release")
|
|
272
|
-
|
|
273
|
-
if yes or click.confirm(
|
|
274
|
-
"\nCreate Dependabot configuration (.github/dependabot.yml) "
|
|
275
|
-
"to auto-update GitHub Actions versions?",
|
|
276
|
-
default=True,
|
|
277
|
-
):
|
|
298
|
+
if "dependabot" in steps:
|
|
278
299
|
create_dependabot()
|
|
279
300
|
except BaseException:
|
|
280
301
|
if afterpython_path.exists():
|
|
@@ -327,12 +348,12 @@ def init_website_subcommand():
|
|
|
327
348
|
the website to an existing AfterPython project.
|
|
328
349
|
"""
|
|
329
350
|
from afterpython.tools.github_actions import create_workflow
|
|
330
|
-
from afterpython.tools.myst import
|
|
351
|
+
from afterpython.tools.myst import ensure_pnpm_12, init_myst
|
|
331
352
|
from afterpython.utils import find_node_env
|
|
332
353
|
|
|
333
354
|
# Pre-flight: same rationale as `ap init` — guard the standalone entry too,
|
|
334
355
|
# since this subcommand can be invoked directly via `ap init website`.
|
|
335
|
-
|
|
356
|
+
ensure_pnpm_12(find_node_env())
|
|
336
357
|
|
|
337
358
|
init_faq()
|
|
338
359
|
init_myst()
|
|
@@ -342,3 +363,44 @@ def init_website_subcommand():
|
|
|
342
363
|
# of a broken website install.
|
|
343
364
|
subprocess.run(["ap", "update", "website"], check=True)
|
|
344
365
|
create_workflow("deploy")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@init.command("content")
|
|
369
|
+
@click.option("--all", is_flag=True, help="Initialize all content types")
|
|
370
|
+
@click.option("--doc", is_flag=True, help="Initialize afterpython/doc/")
|
|
371
|
+
@click.option("--blog", is_flag=True, help="Initialize afterpython/blog/")
|
|
372
|
+
@click.option("--tutorial", is_flag=True, help="Initialize afterpython/tutorial/")
|
|
373
|
+
@click.option("--example", is_flag=True, help="Initialize afterpython/example/")
|
|
374
|
+
@click.option("--guide", is_flag=True, help="Initialize afterpython/guide/")
|
|
375
|
+
def init_content_subcommand(
|
|
376
|
+
all: bool, doc: bool, blog: bool, tutorial: bool, example: bool, guide: bool
|
|
377
|
+
):
|
|
378
|
+
"""Initialize content directories (MyST config) without touching the website
|
|
379
|
+
|
|
380
|
+
Content types that already have a myst.yml are skipped.
|
|
381
|
+
|
|
382
|
+
Examples:
|
|
383
|
+
ap init content --blog # afterpython/blog/
|
|
384
|
+
ap init content --blog --tutorial # afterpython/blog/ and afterpython/tutorial/
|
|
385
|
+
ap init content --all # every content type that is missing
|
|
386
|
+
"""
|
|
387
|
+
from afterpython._typing import tContentType
|
|
388
|
+
from afterpython.const import CONTENT_TYPES
|
|
389
|
+
from afterpython.tools.myst import init_myst
|
|
390
|
+
|
|
391
|
+
flags: dict[tContentType, bool] = {
|
|
392
|
+
"doc": doc,
|
|
393
|
+
"blog": blog,
|
|
394
|
+
"tutorial": tutorial,
|
|
395
|
+
"example": example,
|
|
396
|
+
"guide": guide,
|
|
397
|
+
}
|
|
398
|
+
if all:
|
|
399
|
+
content_types = CONTENT_TYPES
|
|
400
|
+
else:
|
|
401
|
+
content_types = {content_type for content_type, flag in flags.items() if flag}
|
|
402
|
+
if not content_types:
|
|
403
|
+
raise click.UsageError(
|
|
404
|
+
"Specify one or more of --doc/--blog/--tutorial/--example/--guide, or --all"
|
|
405
|
+
)
|
|
406
|
+
init_myst(content_types)
|
|
@@ -13,6 +13,12 @@ announcement = ""
|
|
|
13
13
|
readme_py = "wasm" # marimo html export format: "wasm" or "static"
|
|
14
14
|
execute_readme_py = false # execute README.py cells at build time and embed outputs as a preview (wasm mode only)
|
|
15
15
|
api_reference = false # set to true to enable the pdoc-generated API reference
|
|
16
|
+
# api_reference_docformat = "google" # docstring format for pdoc: "google", "numpy", "restructuredtext" or "markdown"
|
|
17
|
+
|
|
18
|
+
[website.comments] # giscus comment section on blog posts, tutorials, examples and guides
|
|
19
|
+
enabled = true
|
|
20
|
+
# Discussions category for comments. Announcement format means only maintainers can start discussions there; visitors can only reply.
|
|
21
|
+
category = "Announcements"
|
|
16
22
|
|
|
17
23
|
# [website.blog]
|
|
18
24
|
# thumbnail = "blog_default_thumbnail.png" # inside blog/static/ folder, default thumbnail for blog posts
|