afterpython 0.1.0__py3-none-any.whl → 0.1.0.dev4__py3-none-any.whl

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 (55) hide show
  1. afterpython/{templates/pre-commit-config-template.yaml → .pre-commit-config.yaml} +1 -1
  2. afterpython/_io/toml.py +3 -5
  3. afterpython/_paths.py +1 -1
  4. afterpython/_typing.py +2 -1
  5. afterpython/builders/__init__.py +3 -12
  6. afterpython/builders/metadata.py +3 -7
  7. afterpython/builders/molab.py +99 -0
  8. afterpython/cli/__init__.py +1 -0
  9. afterpython/cli/commands/build.py +63 -136
  10. afterpython/cli/commands/bump.py +0 -30
  11. afterpython/cli/commands/check.py +1 -1
  12. afterpython/cli/commands/clean.py +3 -4
  13. afterpython/cli/commands/commit.py +3 -21
  14. afterpython/cli/commands/commitizen.py +1 -1
  15. afterpython/cli/commands/dev.py +13 -80
  16. afterpython/cli/commands/format.py +1 -1
  17. afterpython/cli/commands/init.py +39 -48
  18. afterpython/cli/commands/install.py +2 -8
  19. afterpython/cli/commands/pre_commit.py +1 -1
  20. afterpython/cli/commands/preview.py +1 -1
  21. afterpython/cli/commands/start.py +4 -8
  22. afterpython/cli/commands/sync.py +21 -65
  23. afterpython/cli/commands/update.py +8 -31
  24. afterpython/cli/main.py +14 -75
  25. afterpython/const.py +2 -1
  26. afterpython/cz.toml +27 -0
  27. afterpython/pcu.py +1 -3
  28. afterpython/templates/deploy-workflow-template.yml +5 -22
  29. afterpython/templates/release-workflow-template.yml +3 -3
  30. afterpython/templates/ruff-template.toml +1 -1
  31. afterpython/tools/_afterpython.py +10 -73
  32. afterpython/tools/_git.py +2 -28
  33. afterpython/tools/commitizen.py +45 -5
  34. afterpython/tools/github_actions.py +53 -23
  35. afterpython/tools/myst.py +38 -82
  36. afterpython/tools/pre_commit.py +31 -6
  37. afterpython/tools/pyproject.py +2 -40
  38. afterpython/utils.py +3 -87
  39. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.dist-info}/METADATA +13 -39
  40. afterpython-0.1.0.dev4.dist-info/RECORD +47 -0
  41. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.dist-info}/WHEEL +1 -1
  42. afterpython/builders/content_json.py +0 -131
  43. afterpython/builders/index_md.py +0 -94
  44. afterpython/builders/jupyter_notebook.py +0 -293
  45. afterpython/builders/markdown.py +0 -271
  46. afterpython/cli/commands/add.py +0 -67
  47. afterpython/cli/commands/init_branch_rules.py +0 -9
  48. afterpython/cli/commands/lock.py +0 -23
  49. afterpython/cli/commands/remove.py +0 -67
  50. afterpython/templates/ci-workflow-template.yml +0 -197
  51. afterpython/templates/cz-template.toml +0 -7
  52. afterpython/templates/dependabot-template.yml +0 -30
  53. afterpython/tools/branch_rules.py +0 -106
  54. afterpython-0.1.0.dist-info/RECORD +0 -57
  55. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.dist-info}/entry_points.txt +0 -0
afterpython/tools/myst.py CHANGED
@@ -1,17 +1,15 @@
1
1
  from __future__ import annotations
2
-
3
2
  from typing import TYPE_CHECKING
4
3
 
5
4
  if TYPE_CHECKING:
6
5
  from pathlib import Path
7
-
8
- from afterpython._typing import NodeEnv, tContentType
6
+ from afterpython._typing import tContentType, NodeEnv
9
7
 
10
8
  import subprocess
11
9
 
12
10
  import afterpython as ap
13
- from afterpython._io.yaml import read_yaml, write_yaml
14
11
  from afterpython.utils import deep_merge
12
+ from afterpython._io.yaml import write_yaml, read_yaml
15
13
 
16
14
 
17
15
  def update_authors_yml(data_update: dict):
@@ -50,7 +48,7 @@ def update_authors_yml(data_update: dict):
50
48
  write_yaml(file_path, existing_data)
51
49
 
52
50
 
53
- def update_myst_yml(data_update: dict, path: Path, add_comments: bool = False):
51
+ def update_myst_yml(data_update: dict, path: Path):
54
52
  """Update myst.yml while preserving order and formatting
55
53
 
56
54
  Args:
@@ -66,83 +64,38 @@ def update_myst_yml(data_update: dict, path: Path, add_comments: bool = False):
66
64
  )
67
65
 
68
66
  existing_data = read_yaml(file_path) or {}
69
- existing_data = deep_merge(existing_data, data_update, extend_lists=False)
70
-
71
- # NOTE: this checking existing_data["key1"].ca.items.get("key2") is buggy
72
- # only add_comments=True during init to avoid adding duplicate comments
73
- if add_comments:
74
- # set comments for project section for convenience
75
- if "project" in existing_data:
76
- if not existing_data["project"].ca.items.get("id"):
77
- existing_data["project"].yaml_set_comment_before_after_key(
78
- "id",
79
- before="See how to create Table of Contents at: https://mystmd.org/guide/table-of-contents",
80
- )
81
- if not existing_data["project"].ca.items.get("authors"):
82
- existing_data["project"].yaml_set_comment_before_after_key(
83
- "authors",
84
- before="See more authors' fields at: https://mystmd.org/guide/frontmatter#frontmatter-authors",
85
- )
86
- if not existing_data["project"].ca.items.get("venue"):
87
- existing_data["project"].yaml_set_comment_before_after_key(
88
- "venue",
89
- before="See more venue's fields at: https://mystmd.org/guide/frontmatter#venue",
90
- )
91
- if "site" in existing_data:
92
- if not existing_data["site"].ca.items.get("options"):
93
- existing_data["site"].yaml_set_comment_before_after_key(
94
- "options",
95
- before="See options at: https://mystmd.org/guide/website-templates#site-options",
96
- )
97
- if not existing_data["site"].ca.items.get("actions"):
98
- existing_data["site"].yaml_set_comment_before_after_key(
99
- "actions",
100
- before="See web layout at: https://mystmd.org/guide/website-navigation",
101
- )
102
-
103
- write_yaml(file_path, existing_data)
104
-
105
-
106
- def _write_index_file(content_type: tContentType):
107
- """Create a placeholder index.md file for MyST to prevent first file from becoming index.
108
-
109
- MyST treats the first file in TOC as index. This placeholder ensures all actual
110
- content files get proper slugs. The generated index.html is deleted post-build
111
- since SvelteKit owns the landing page route.
67
+ existing_data = deep_merge(existing_data, data_update)
112
68
 
113
- Note: This only creates the file. TOC modification is handled by the build process.
114
-
115
- Raises:
116
- FileExistsError: If index.md already exists (users shouldn't create this file).
117
- """
118
- if content_type == "doc":
119
- return # Doc doesn't need a placeholder index.md
120
-
121
- content_path = ap.paths.afterpython_path / content_type
122
- index_file = content_path / "index.md"
123
-
124
- # Check if user created an index.md file
125
- if index_file.exists():
126
- raise FileExistsError(
127
- f"\n"
128
- f"Found existing 'index.md' in afterpython/{content_type}/\n"
129
- f"\n"
130
- f"The 'index.md' file is reserved for internal use by AfterPython.\n"
131
- f"MyST treats the first file in TOC as index, which would conflict with\n"
132
- f"SvelteKit (project-website-template)'s landing page route (/{content_type}).\n"
133
- f"\n"
134
- f"Please rename your file to something else (e.g., '{content_type}_intro.md')\n"
135
- f"and update the reference in afterpython/{content_type}/myst.yml"
136
- )
137
-
138
- index_content = f"""---
139
- title: ← {content_type.capitalize()}
140
- ---
69
+ # set comments for project section for convenience
70
+ if "project" in existing_data:
71
+ if not existing_data["project"].ca.items.get("id"):
72
+ existing_data["project"].yaml_set_comment_before_after_key(
73
+ "id",
74
+ before="See how to create Table of Contents at: https://mystmd.org/guide/table-of-contents",
75
+ )
76
+ if not existing_data["project"].ca.items.get("authors"):
77
+ existing_data["project"].yaml_set_comment_before_after_key(
78
+ "authors",
79
+ before="See more authors' fields at: https://mystmd.org/guide/frontmatter#frontmatter-authors",
80
+ )
81
+ if not existing_data["project"].ca.items.get("venue"):
82
+ existing_data["project"].yaml_set_comment_before_after_key(
83
+ "venue",
84
+ before="See more venue's fields at: https://mystmd.org/guide/frontmatter#venue",
85
+ )
86
+ if "site" in existing_data:
87
+ if not existing_data["site"].ca.items.get("options"):
88
+ existing_data["site"].yaml_set_comment_before_after_key(
89
+ "options",
90
+ before="See options at: https://mystmd.org/guide/website-templates#site-options",
91
+ )
92
+ if not existing_data["site"].ca.items.get("actions"):
93
+ existing_data["site"].yaml_set_comment_before_after_key(
94
+ "actions",
95
+ before="See web layout at: https://mystmd.org/guide/website-navigation",
96
+ )
141
97
 
142
- This is a placeholder index page. The actual {content_type} landing page is rendered by SvelteKit.
143
- """
144
- index_file.write_text(index_content)
145
- return index_file
98
+ write_yaml(file_path, existing_data)
146
99
 
147
100
 
148
101
  def _write_welcome_file(content_type: tContentType):
@@ -163,7 +116,7 @@ Replace this placeholder content with your own. Here's what you can do:
163
116
 
164
117
  ## Resources
165
118
 
166
- - [AfterPython's Project Website](https://afterpython.afterpython.org)
119
+ - [AfterPython's Project Website](https://ap.afterpython.org)
167
120
  - [MyST Markdown Guide](https://mystmd.org)
168
121
 
169
122
  Start building your amazing project! 🚀
@@ -198,11 +151,14 @@ def init_myst():
198
151
  },
199
152
  "site": {
200
153
  "options": {
154
+ "favicon": "../static/favicon.ico",
155
+ "logo": "../static/logo.svg",
156
+ "logo_dark": "../static/logo.svg",
201
157
  "analytics_google": "{{ GOOGLE_ANALYTICS_ID }}",
202
158
  # "twitter": "",
203
159
  },
204
160
  },
205
161
  }
206
- update_myst_yml(myst_yml_defaults, path, add_comments=True)
162
+ update_myst_yml(myst_yml_defaults, path)
207
163
  _write_welcome_file(content_type)
208
164
  subprocess.run(["ap", "sync"])
@@ -1,8 +1,36 @@
1
- import shutil
2
1
  import subprocess
3
2
 
4
3
  import afterpython as ap
5
- from afterpython._io.yaml import read_yaml, write_yaml
4
+
5
+ from afterpython._io.yaml import write_yaml, read_yaml
6
+
7
+
8
+ pre_commit_default = {
9
+ # default is only ["pre-commit"], without changing the default,
10
+ # we need to explicitly pass in --hook-type whenever we run "pre-commit install --hook-type ..."
11
+ "default_install_hook_types": ["pre-commit", "commit-msg", "pre-push"],
12
+ "exclude": "^afterpython/_website/", # do not check the project website template
13
+ "repos": [
14
+ {
15
+ "repo": "https://github.com/pre-commit/pre-commit-hooks",
16
+ "rev": "v6.0.0",
17
+ "hooks": [
18
+ {"id": "trailing-whitespace", "stages": ["pre-commit"]},
19
+ {"id": "end-of-file-fixer", "stages": ["pre-commit"]},
20
+ {"id": "check-yaml", "stages": ["pre-commit"]},
21
+ {"id": "check-toml", "stages": ["pre-commit"]},
22
+ {"id": "check-added-large-files", "stages": ["pre-commit"]},
23
+ ],
24
+ },
25
+ # {
26
+ # "repo": "https://github.com/astral-sh/uv-pre-commit",
27
+ # "rev": "0.9.8",
28
+ # "hooks": [
29
+ # {"id": "uv-lock"}, # Update the uv lockfile
30
+ # ]
31
+ # },
32
+ ],
33
+ }
6
34
 
7
35
 
8
36
  def install_pre_commit():
@@ -29,9 +57,6 @@ def init_pre_commit():
29
57
  if pre_commit_path.exists():
30
58
  print(f".pre-commit-config.yaml already exists at {pre_commit_path}")
31
59
  return
32
- pre_commit_template_path = (
33
- ap.paths.templates_path / "pre-commit-config-template.yaml"
34
- )
35
- shutil.copy(pre_commit_template_path, pre_commit_path)
60
+ write_yaml(pre_commit_path, pre_commit_default)
36
61
  print(f"Created {pre_commit_path}")
37
62
  install_pre_commit()
@@ -1,17 +1,15 @@
1
1
  from __future__ import annotations
2
-
3
2
  from typing import TYPE_CHECKING
4
3
 
5
4
  if TYPE_CHECKING:
6
5
  from tomlkit.toml_document import TOMLDocument
7
6
 
8
7
  import asyncio
9
- from pathlib import Path
10
8
 
11
9
  from pyproject_metadata import StandardMetadata
12
10
 
13
11
  import afterpython as ap
14
- from afterpython._io.toml import _to_tomlkit, read_toml, write_toml
12
+ from afterpython._io.toml import read_toml, write_toml, _to_tomlkit
15
13
 
16
14
 
17
15
  def read_pyproject() -> TOMLDocument:
@@ -28,50 +26,14 @@ def read_metadata() -> StandardMetadata:
28
26
  return StandardMetadata.from_pyproject(read_pyproject())
29
27
 
30
28
 
31
- def find_package_directory() -> Path:
32
- """Find the user's package directory.
33
-
34
- Supports both common Python project layouts:
35
- - src layout: user_project/src/package_name/__init__.py
36
- - flat layout: user_project/package_name/__init__.py
37
-
38
- Returns:
39
- Path to the package directory containing __init__.py
40
-
41
- Raises:
42
- FileNotFoundError: If package directory cannot be found in either layout
43
- """
44
- metadata = read_metadata()
45
- package_name = metadata.name
46
- project_root = ap.paths.user_path
47
-
48
- # Try src layout first (recommended layout)
49
- src_layout_dir = project_root / "src" / package_name
50
- if (src_layout_dir / "__init__.py").exists():
51
- return src_layout_dir
52
-
53
- # Try flat layout
54
- flat_layout_dir = project_root / package_name
55
- if (flat_layout_dir / "__init__.py").exists():
56
- return flat_layout_dir
57
-
58
- # Package not found in either location
59
- raise FileNotFoundError(
60
- f"Could not find package '{package_name}' in either src layout "
61
- f"({src_layout_dir}) or flat layout ({flat_layout_dir}). "
62
- f"Expected to find __init__.py in one of these locations."
63
- )
64
-
65
-
66
29
  def init_pyproject():
67
30
  """Initialize pyproject.toml with sensible defaults
68
31
  - add [build-system] section with uv build backend (same as `uv init --package`)
69
32
  - add [project.urls] section with homepage, repository, and documentation URLs
70
33
  """
71
34
  import httpx
72
-
73
- from afterpython.tools._git import get_git_user_config, get_github_url
74
35
  from afterpython.utils import fetch_pypi_json
36
+ from afterpython.tools._git import get_git_user_config, get_github_url
75
37
 
76
38
  build_backend = "uv_build"
77
39
 
afterpython/utils.py CHANGED
@@ -1,12 +1,9 @@
1
1
  from __future__ import annotations
2
-
3
2
  from typing import TYPE_CHECKING
4
3
 
5
4
  if TYPE_CHECKING:
6
5
  from pathlib import Path
7
-
8
6
  from httpx import AsyncClient
9
-
10
7
  from afterpython._typing import NodeEnv
11
8
 
12
9
  import os
@@ -23,7 +20,6 @@ def find_node_env() -> NodeEnv:
23
20
  If no, install the Node.js version specified in NODEENV_VERSION
24
21
  """
25
22
  from mystmd_py.nodeenv import find_any_node
26
-
27
23
  from afterpython.const import NODEENV_VERSION
28
24
 
29
25
  # from mystmd_py.main import ensure_valid_version
@@ -39,11 +35,6 @@ def has_uv() -> bool:
39
35
  return shutil.which("uv") is not None
40
36
 
41
37
 
42
- def has_pixi() -> bool:
43
- """Check if pixi is installed"""
44
- return shutil.which("pixi") is not None
45
-
46
-
47
38
  def has_gh() -> bool:
48
39
  """Check if gh is installed"""
49
40
  return shutil.which("gh") is not None
@@ -91,7 +82,7 @@ def detect_license_from_file(file_path: str) -> str:
91
82
  return ""
92
83
 
93
84
 
94
- def deep_merge(base: dict, updates: dict, extend_lists: bool = True) -> dict:
85
+ def deep_merge(base: dict, updates: dict) -> dict:
95
86
  """Deep merge updates into base, preserving structure and metadata.
96
87
 
97
88
  Works with:
@@ -115,10 +106,8 @@ def deep_merge(base: dict, updates: dict, extend_lists: bool = True) -> dict:
115
106
  if key in base:
116
107
  if isinstance(base[key], dict) and isinstance(value, dict):
117
108
  # Recursively merge dicts
118
- deep_merge(base[key], value, extend_lists=extend_lists)
119
- elif (
120
- isinstance(base[key], list) and isinstance(value, list) and extend_lists
121
- ):
109
+ deep_merge(base[key], value)
110
+ elif isinstance(base[key], list) and isinstance(value, list):
122
111
  # Extend lists, but only add items that don't already exist
123
112
  for item in value:
124
113
  if item not in base[key]:
@@ -224,76 +213,3 @@ def handle_passthrough_help(
224
213
  subprocess.run([*underlying_command, "--help"])
225
214
 
226
215
  ctx.exit(0)
227
-
228
-
229
- def normalize_static_path(path: str) -> str:
230
- """
231
- Normalize paths for static assets.
232
-
233
- Only allows two formats:
234
- 1. Pure filename/path without ./ or ../ (e.g., "logo.svg", "somewhere/logo.svg")
235
- 2. Absolute path starting with / (e.g., "/somewhere/logo.svg")
236
-
237
- Args:
238
- path: User-provided path from afterpython.toml
239
-
240
- Returns:
241
- Normalized path
242
-
243
- Raises:
244
- ValueError: If path contains ./ or ../ or other invalid patterns
245
-
246
- Examples:
247
- >>> normalize_static_path("logo.svg")
248
- 'logo.svg'
249
- >>> normalize_static_path("somewhere/logo.svg")
250
- '/somewhere/logo.svg'
251
- >>> normalize_static_path("/somewhere/logo.svg")
252
- '/somewhere/logo.svg'
253
- >>> normalize_static_path("./logo.svg") # Raises ValueError
254
- >>> normalize_static_path("../logo.svg") # Raises ValueError
255
- >>> normalize_static_path("static/logo.svg") # Raises ValueError
256
- """
257
- if not path:
258
- return ""
259
-
260
- # Reject paths with ./ or ../
261
- if "./" in path or "../" in path or path.startswith(".") or path.startswith(".."):
262
- raise ValueError(
263
- f"Invalid path '{path}': paths cannot contain './' or '../'. "
264
- f"Use either a pure filename (e.g., 'logo.svg') or absolute path (e.g., '/somewhere/logo.svg')"
265
- )
266
-
267
- # Reject paths with 'static' in them
268
- if "static" in path.lower():
269
- raise ValueError(
270
- f"Invalid path '{path}': do not include 'static' in the path. "
271
- f"Files are assumed to be in the static/ directory."
272
- )
273
-
274
- # If it starts with /, it's absolute - keep as is
275
- if not path.startswith("/"):
276
- return "/" + path
277
- else:
278
- return path
279
-
280
-
281
- def get_relative_static_prefix(file_path: Path, content_type_path: Path) -> str:
282
- """Calculate the relative path prefix from a file to its content type's static/ folder.
283
-
284
- Args:
285
- file_path: Path to the content file (e.g., blog/layer/blog5.ipynb)
286
- content_type_path: Path to the content type folder (e.g., blog/)
287
-
288
- Returns:
289
- Relative prefix to reach static/ (e.g., "./" or "../" or "../../")
290
- """
291
- # Get the depth: how many directories deep the file is from content_type_path
292
- relative = file_path.parent.relative_to(content_type_path)
293
- depth = len(relative.parts)
294
-
295
- # Build the correct prefix
296
- if depth == 0:
297
- return "./" # File is directly in content_type_path
298
- else:
299
- return "../" * depth
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afterpython
3
- Version: 0.1.0
4
- Summary: CLI tool to streamline Python package maintenance and generate a dedicated project website
3
+ Version: 0.1.0.dev4
4
+ Summary: CLI tool for AfterPython
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
7
7
  Author-email: Stephen Yau <softwareentrepreneer+afterpython@gmail.com>
@@ -10,38 +10,39 @@ Classifier: Operating System :: OS Independent
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Topic :: Software Development :: Build Tools
12
12
  Classifier: Topic :: Software Development :: Documentation
13
- Classifier: Typing :: Typed
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
16
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
17
  Requires-Dist: click>=8.3.1
18
18
  Requires-Dist: trogon>=0.6.0
19
19
  Requires-Dist: httpx>=0.28.1
20
20
  Requires-Dist: tomlkit>=0.13.3
21
21
  Requires-Dist: ruamel-yaml>=0.18.16
22
22
  Requires-Dist: gitpython>=3.1.45
23
+ Requires-Dist: pygithub>=2.8.1
23
24
  Requires-Dist: python-dotenv>=1.2.1
24
- Requires-Dist: pyproject-metadata>=0.10.0
25
- Requires-Dist: mystmd>=1.7.0
25
+ Requires-Dist: pyproject-metadata>=0.9.1
26
+ Requires-Dist: mystmd>=1.6.6
26
27
  Requires-Dist: pdoc>=16.0.0
27
- Requires-Dist: ruff>=0.14.8
28
- Requires-Dist: pre-commit>=4.5.0
28
+ Requires-Dist: ruff>=0.14.5
29
+ Requires-Dist: pre-commit>=4.4.0
29
30
  Requires-Dist: commitizen>=4.10.0
30
31
  Requires-Python: >=3.11
31
- Project-URL: documentation, https://afterpython.afterpython.org/doc
32
- Project-URL: homepage, https://afterpython.afterpython.org
32
+ Project-URL: documentation, https://docs.afterpython.org
33
+ Project-URL: homepage, https://afterpython.org
33
34
  Project-URL: repository, https://github.com/AfterPythonOrg/afterpython
34
35
  Description-Content-Type: text/markdown
35
36
 
36
37
  # AfterPython: Python Package Maintenance Toolkit and Project Website Generator
37
38
 
38
39
  [![afterpython](https://afterpython.org/shield.svg)](https://afterpython.org)
40
+ [![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh)
39
41
  ![PyPI downloads](https://img.shields.io/pypi/dm/afterpython)
40
42
  [![PyPI](https://img.shields.io/pypi/v/afterpython.svg)](https://pypi.org/project/afterpython)
41
43
  ![PyPI - Support Python Versions](https://img.shields.io/pypi/pyversions/afterpython)
42
44
  [![Discussions](https://img.shields.io/badge/Discussions-Let's%20Chat-green)](https://github.com/AfterPythonOrg/afterpython/discussions)
43
45
  [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/AfterPythonOrg/afterpython)
44
- <!-- [![Pixi Badge](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/prefix-dev/pixi/main/assets/badge/v0.json)](https://pixi.sh) -->
45
46
 
46
47
 
47
48
  [MyST]: https://mystmd.org
@@ -56,7 +57,6 @@ Description-Content-Type: text/markdown
56
57
  [Scikit-Learn]: https://scikit-learn.org
57
58
  [WebLLM]: https://webllm.mlc.ai/
58
59
  [project-website-template]: https://github.com/AfterPythonOrg/project-website-template
59
- [commitizen]: https://github.com/commitizen-tools/commitizen
60
60
  [uv]: https://docs.astral.sh/uv/
61
61
  [ruff]: https://docs.astral.sh/ruff/
62
62
 
@@ -75,54 +75,28 @@ First-time maintainers must learn multiple tools and concepts, e.g. [MyST], [Sem
75
75
 
76
76
  ## Core Features
77
77
  - [x] Write content directly in [MyST Markdown] or [Jupyter Notebook]
78
- - [x] Go from writing to **website deployment in minutes** — no need to learn any of the underlying tools
79
- - [x] Centralize all your content in a modern, **unified project website** — from documentation to blog posts
80
- - [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**
78
+ - [x] Go from writing to website deployment in minutes — no need to learn any of the underlying tools
79
+ - [x] Centralize all your content in a modern, unified project website — from documentation to blog posts
81
80
  - [ ] Export content as PDF — for example, combine all blog posts into a single PDF file
82
81
  - [ ] **⚡ Full-text search** across **ALL** your content in your website — docs, blogs, tutorials, everything
83
82
  - [ ] **🤖 Embedded AI Chatbot** that answers questions directly using an in-browser LLM — at no cost
84
83
 
85
- ---
86
- ## Project Website
87
- > The project website for `afterpython` is created using `afterpython` itself. See the [**website**](https://afterpython.afterpython.org).
88
- <picture>
89
- <source media="(prefers-color-scheme: dark)" srcset="afterpython/static/website-dark.png">
90
- <source media="(prefers-color-scheme: light)" srcset="afterpython/static/website-light.png">
91
- <img alt="Project Website" src="afterpython/static/website-light.png">
92
- </picture>
93
-
94
- You can create your own website too and deploy it to GitHub Pages in **less than a minute**! See [Quickstart](https://afterpython.afterpython.org/doc/quickstart).
95
84
 
96
- ---
97
85
  ## Installation
98
86
  ```bash
99
87
  # install afterpython as a dev dependency
100
88
  uv add --dev afterpython
101
-
102
89
  # initialize afterpython
103
90
  ap init
104
91
  ```
105
92
 
106
93
 
107
- ---
108
- ## CLI Commands
109
- ```bash
110
- # show all commands
111
- ap --help
112
-
113
- # or use terminal UI (TUI)
114
- ap tui
115
- ```
116
-
117
-
118
- ---
119
94
  ## Tech Stack
120
95
  - [MyST]
121
96
  - [project-website-template]
122
97
  - [pre-commit]
123
98
  - [GitHub Actions]
124
99
  - [pdoc]
125
- - [commitizen]
126
100
  - [uv]
127
101
  - [ruff]
128
102
  - [pagefind]
@@ -0,0 +1,47 @@
1
+ afterpython/.pre-commit-config.yaml,sha256=7mpSRHaFcG2M6SOY-5Sbs9WxN8UISnZ2g0NwCGsLNCA,915
2
+ afterpython/__init__.py,sha256=aNochs72gU2NR1cBzIkk3NxSsdJ7ceHluI5gdYwaFpI,289
3
+ afterpython/_io/toml.py,sha256=nrx0CFPONgmkz5WDCSMyr4caic6nzu0uCd33qOmhhF0,3408
4
+ afterpython/_io/yaml.py,sha256=raksve_gaIFJim8lQrq-kQHkRjTloed7CT2FY0oUJSg,654
5
+ afterpython/_paths.py,sha256=1NrPQ4erjA_rIvDggx7QRY4_01zyU-yht9s95Q-4YgM,1584
6
+ afterpython/_typing.py,sha256=kmxBRbhft85C8Ycm5BFBP258Jozrw9tsl7KHOVPcF_o,146
7
+ afterpython/builders/__init__.py,sha256=MOv3spIh-ABwyUtvQAHhPZ2horWb0D93WNmOPNAzKYo,215
8
+ afterpython/builders/metadata.py,sha256=IL-fe3GJYbAaqtMvw5HhpZJ7O57h5GX2GJtKKdWVab8,1275
9
+ afterpython/builders/molab.py,sha256=Qji8piWJB1tbzhgtld0pQWIsUA7d7RsGCfnoLVKd5EA,3714
10
+ afterpython/cli/__init__.py,sha256=1vLZylRMnGaV7cJ-kHO96dChp33_P4fBIJ_Vi-yyZg4,85
11
+ afterpython/cli/commands/build.py,sha256=RWO-4s8V0Ubdm21royqB2zDoZA1QfeLr-0YZQqAKKjc,6444
12
+ afterpython/cli/commands/bump.py,sha256=Wg-q3UQIkIRGT3umZCUBPpwIQTAHwQZVs1yHSZhmzKE,3088
13
+ afterpython/cli/commands/check.py,sha256=cLP4Vle3ENQHCXBv1okDlWK-L1XK-fv80m66xW3LQkg,1097
14
+ afterpython/cli/commands/clean.py,sha256=dCEkkWOTn93758wGTrWJG9V6c-lv2Z59CYfoP3i3rJE,2531
15
+ afterpython/cli/commands/commit.py,sha256=yRLOBs6JetZSGkFy9_uLzgR8yAX9djeUF291gabtuUo,880
16
+ afterpython/cli/commands/commitizen.py,sha256=naI8j4pfXIMsiOLmPMeZTGh831I3665_1_Sbn7yoKyE,811
17
+ afterpython/cli/commands/dev.py,sha256=QJmV9sARdxXTMX6oedeoJg3QAr7PYC0n3gcFkppd1X0,5106
18
+ afterpython/cli/commands/format.py,sha256=cXvuc-6XwULM1VJjS2B2bJBvnV1TVHFl1CeSs0SYQ24,738
19
+ afterpython/cli/commands/init.py,sha256=Sc0B-Gv1bpYHzD8hYoVTXY2QsOV36kWcKH5vgHBmp1g,2987
20
+ afterpython/cli/commands/install.py,sha256=wgH9DEuN7hsA6BslNJASqOBn4VHJrYHBvowXDHi4smE,573
21
+ afterpython/cli/commands/pre_commit.py,sha256=DdmbkcZgJ7QArAY1y5vODYZmHUXWEIuXRyp69AS-pn0,1981
22
+ afterpython/cli/commands/preview.py,sha256=EbuB-dFcgonmqumTb2aHlLpXRiXkGE4DI3ddJdHwcaM,619
23
+ afterpython/cli/commands/release.py,sha256=6SDEnPtuCL0_XUqWUlvRSKxAv011QqOD0kc5pKu7378,2479
24
+ afterpython/cli/commands/start.py,sha256=_ikrxPErYSGYtn8MAlbWD0CDEhYdoKvZ9e7XvrUZfog,3932
25
+ afterpython/cli/commands/sync.py,sha256=Cy9M52YmEGOZkhI8tX8RgM8SNad7q_LRlaNrkFEUYfM,4057
26
+ afterpython/cli/commands/update.py,sha256=f6dzoJIiEod7j44dgnaFe-v7INVdUbopVoWPxv7-Mpo,5491
27
+ afterpython/cli/main.py,sha256=GMcP7bmOA1glKGPsCuOXcd-9hv2k3BGkk_ofdo8oatw,2660
28
+ afterpython/const.py,sha256=lNmmyObrjSxxMkVpIsEqtd9Nms_ndrW_omIP79BWvPc,159
29
+ afterpython/cz.toml,sha256=JlenGROat8jvxvqRH6KluDs6f7w0JAy8Skd3ak7sGLY,1506
30
+ afterpython/main.py,sha256=w0z6mTib3u4p2HxMrk-tIJlZtur7uRaqsbE0ewlYlSs,490
31
+ afterpython/pcu.py,sha256=RWgPcdAFyNBVhWbQPuL1seji9uyT8Pqz-zE6jCf_Qb8,6091
32
+ afterpython/templates/deploy-workflow-template.yml,sha256=mYshdvVCz2COFKBkUoHApXng45ZUIOPeg5O9VzkroXg,1928
33
+ afterpython/templates/release-workflow-template.yml,sha256=DpuFLo3_43rqwBjDf3XrWbYJNRdlhy7-wJFL_TxdRNE,1606
34
+ afterpython/templates/ruff-template.toml,sha256=_yTtZ0YPsJiCxy66ZDfYaStTJ9aXXW-JXy-KW6l2bBE,1010
35
+ afterpython/tools/_afterpython.py,sha256=W88GhtQADB09DV4A24GOYBeYrHsnVVzllLdWFiKgS1A,1904
36
+ afterpython/tools/_git.py,sha256=pOcbP_hb6db3qRlbB4Hlp7MkShkTmUefnIBU2kqoq4I,3211
37
+ afterpython/tools/commitizen.py,sha256=9aGXsO4tTyDs3s8qS_fiskdSabHqc9XjHwePThybJU0,1469
38
+ afterpython/tools/github_actions.py,sha256=M3uGLJKOCml1fQBc8W2lkZWsR5p-0UVUolFWvqf5mDo,2866
39
+ afterpython/tools/myst.py,sha256=m-1W_Tv3VrP6GQIoRaCJZuiy3AuedJ42UV7jHcrg56c,6238
40
+ afterpython/tools/pre_commit.py,sha256=stXUQfR0A6nPMCSuuKO-Icjc5F2z9VsZu2mKkIW1vWI,2193
41
+ afterpython/tools/pyproject.py,sha256=YwJJAB3lIqYK9wt8UtL4dfCli9UN7uzi3e-vtycB0HE,2325
42
+ afterpython/utils.py,sha256=7r4jsxn-Rr2gvCh7MJqxVIjzZH7Jnjt1z_sl95cFaEk,6831
43
+ afterpython/version.py,sha256=hihRV1S-gc1m46OaXD32ikkEkqzs-Gtq7AcAqOQV-wY,1603
44
+ afterpython-0.1.0.dev4.dist-info/WHEEL,sha256=YUH1mBqsx8Dh2cQG2rlcuRYUhJddG9iClegy4IgnHik,79
45
+ afterpython-0.1.0.dev4.dist-info/entry_points.txt,sha256=roa0PDgBueM1JPbRG0G2OSxzaN394OUL_oS9iEwVLBc,80
46
+ afterpython-0.1.0.dev4.dist-info/METADATA,sha256=WZLaLUbAfqmrAYYfyGMN6kFvOzmZwmhV-brUIwWwUHo,4740
47
+ afterpython-0.1.0.dev4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.16
2
+ Generator: uv 0.9.11
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any