afterpython 0.3.15__py3-none-any.whl → 0.3.16__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.
@@ -222,7 +222,7 @@ def build_jupyter_notebooks():
222
222
  from afterpython.tools._afterpython import get_default_thumbnail
223
223
 
224
224
  # Check if molab badge feature is enabled
225
- add_molab_badge = os.getenv("AP_MOLAB_BADGE", "1") == "1"
225
+ add_molab_badge = os.getenv("AP_MOLAB_BADGE", "0") == "1"
226
226
 
227
227
  # Get GitHub URL once if needed for molab badges
228
228
  github_url = None
@@ -130,7 +130,7 @@ def build_marimo_notebook(
130
130
  if mode == "wasm":
131
131
  return
132
132
 
133
- add_molab_badge = os.getenv("AP_MOLAB_BADGE", "1") == "1"
133
+ add_molab_badge = os.getenv("AP_MOLAB_BADGE", "0") == "1"
134
134
  if not add_molab_badge:
135
135
  return
136
136
 
@@ -74,7 +74,12 @@ def _publish_for_type(content_type: tContentType) -> int:
74
74
 
75
75
  count = 0
76
76
  for source in sorted(source_dir.rglob("*")):
77
- if "_build" in source.parts:
77
+ # Skip MyST build output and any hidden tool directory. The latter is
78
+ # essential: jupyterlab-lsp writes `.virtual_documents/*.ipynb` and
79
+ # jupyter writes `.ipynb_checkpoints/*.ipynb` — these carry an `.ipynb`
80
+ # extension but are NOT notebooks (the virtual-documents ones are raw
81
+ # Python source), so jupytext.read() chokes on them.
82
+ if "_build" in source.parts or any(p.startswith(".") for p in source.parts):
78
83
  continue
79
84
  if not source.is_file():
80
85
  continue
@@ -6,7 +6,11 @@ from pyproject_metadata import StandardMetadata
6
6
  from tomlkit.toml_document import TOMLDocument
7
7
 
8
8
  import afterpython as ap
9
- from afterpython.utils import convert_author_name_to_id, normalize_static_path
9
+ from afterpython.utils import (
10
+ build_author_name,
11
+ convert_author_name_to_id,
12
+ normalize_static_path,
13
+ )
10
14
 
11
15
 
12
16
  def _myst_favicon_path(path: str) -> str:
@@ -23,20 +27,21 @@ def _sync_authors_yml(authors: list[tuple[str, str | None]]):
23
27
 
24
28
  # read myst.yml from docs path to get "version"
25
29
  doc_myst_yml = read_yaml(ap.paths.afterpython_path / "doc" / "myst.yml")
30
+ entries = []
31
+ for author in authors:
32
+ # author is a tuple of (name, email)
33
+ name = str(author[0]).strip()
34
+ entries.append(
35
+ {
36
+ "id": convert_author_name_to_id(name),
37
+ "name": build_author_name(name),
38
+ "email": str(author[1]),
39
+ # "github": ...
40
+ }
41
+ )
26
42
  data = {
27
43
  "version": doc_myst_yml["version"],
28
- "project": {
29
- "contributors": [
30
- # NOTE: author is a tuple of (name, email), so author[0] is the name and author[1] is the email
31
- {
32
- "id": convert_author_name_to_id(str(author[0])),
33
- "name": str(author[0]),
34
- "email": str(author[1]),
35
- # "github": ...
36
- }
37
- for author in authors
38
- ]
39
- },
44
+ "project": {"authors": entries},
40
45
  }
41
46
  update_authors_yml(data)
42
47
  click.echo("✓ Synced authors.yml with pyproject.toml")
@@ -114,7 +119,8 @@ def sync():
114
119
  # nav_bar_per_content_type = [
115
120
  # item for item in nav_bar if item["title"] != content_type.capitalize() + "s"
116
121
  # ]
117
- title = project_name + f"'s {content_type.capitalize()}"
122
+ project_title = content_type.capitalize()
123
+ site_title = project_name + f"'s {content_type.capitalize()}"
118
124
  data = {
119
125
  "project": {
120
126
  # using author ids defined in authors.yml
@@ -127,7 +133,7 @@ def sync():
127
133
  "url": company_url,
128
134
  },
129
135
  "copyright": f"© {company_name or project_name} {datetime.now().year}. All rights reserved.",
130
- "title": "",
136
+ "title": project_title,
131
137
  "description": str(pyproject.description),
132
138
  "keywords": list(map(str, pyproject.keywords)),
133
139
  "github": github_url,
@@ -136,7 +142,7 @@ def sync():
136
142
  else "",
137
143
  },
138
144
  "site": {
139
- "title": title,
145
+ "title": site_title,
140
146
  "options": {
141
147
  "favicon": "../static" + myst_favicon if myst_favicon else "",
142
148
  "logo": "../static" + website_logo if website_logo else "",
afterpython/tools/myst.py CHANGED
@@ -30,8 +30,7 @@ def update_authors_yml(data_update: dict):
30
30
  # merge authors, keep the author with more fields (e.g. github, x, etc.) to avoid duplication
31
31
  merged_authors = {}
32
32
  for author in (
33
- existing_data["project"]["contributors"]
34
- + data_update["project"]["contributors"]
33
+ existing_data["project"].get("authors", []) + data_update["project"]["authors"]
35
34
  ):
36
35
  author_id = author["id"]
37
36
  if author_id not in merged_authors:
@@ -40,14 +39,14 @@ def update_authors_yml(data_update: dict):
40
39
  # keep the author with more fields (e.g. github, x, etc.)
41
40
  if len(author.keys()) > len(merged_authors[author_id].keys()):
42
41
  merged_authors[author_id] = author
43
- existing_data["project"]["contributors"] = list(merged_authors.values())
42
+ existing_data["project"]["authors"] = list(merged_authors.values())
44
43
 
45
44
  # set comments for project section for convenience
46
45
  if "project" in existing_data and not existing_data["project"].ca.items.get(
47
- "contributors"
46
+ "authors"
48
47
  ):
49
48
  existing_data["project"].yaml_set_comment_before_after_key(
50
- "contributors",
49
+ "authors",
51
50
  before="See more at: https://mystmd.org/guide/frontmatter#frontmatter-authors",
52
51
  )
53
52
  write_yaml(file_path, existing_data)
afterpython/utils.py CHANGED
@@ -149,6 +149,25 @@ def convert_author_name_to_id(name: str) -> str:
149
149
  return name.replace(" ", "_").lower()
150
150
 
151
151
 
152
+ def build_author_name(name: str) -> dict:
153
+ """Build a MyST name object from a display name.
154
+
155
+ A multi-word name ("Stephen Yau") is split into ``given``/``surname`` so MyST
156
+ can render and cite it properly. A single-token name (e.g. a username like
157
+ "softwareentrepreneer") uses ``literal`` so MyST treats it as the full name
158
+ verbatim instead of warning about a missing given name.
159
+
160
+ Examples:
161
+ - "Stephen Yau" -> {"given": "Stephen", "surname": "Yau"}
162
+ - "softwareentrepreneer" -> {"literal": "softwareentrepreneer"}
163
+ """
164
+ name = name.strip()
165
+ if " " in name:
166
+ given, surname = name.split(" ", 1)
167
+ return {"given": given, "surname": surname}
168
+ return {"literal": name}
169
+
170
+
152
171
  def find_available_port(
153
172
  start_port: int = 3000, max_port: int = 3100, host: str = "localhost"
154
173
  ) -> int:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afterpython
3
- Version: 0.3.15
3
+ Version: 0.3.16
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
@@ -8,12 +8,12 @@ afterpython/builders/build_pdoc.py,sha256=rasjCtTah5GsZYokqU--Fp3bvTEf464ievENfO
8
8
  afterpython/builders/content_json.py,sha256=yem8qhVjy5-aTnmUIING5ttNZFsuTQ-r5M_dYKGRyTk,5362
9
9
  afterpython/builders/faq_json.py,sha256=mMfFepRBL_1QYO0tQ6orf5ESo2LVntk55nfFzencViM,1193
10
10
  afterpython/builders/index_md.py,sha256=ZpBA1FIHgw9MXD8kRaq2OBaEAKnf94_sJmB2cG68iag,6021
11
- afterpython/builders/jupyter_notebook.py,sha256=8yjgwWBaHfEzvN90j4biR6-RaTUHeIQ59h4NKethHOk,9693
11
+ afterpython/builders/jupyter_notebook.py,sha256=9rCOrHWYbPmLllRcp5v9p2odasqQxFZN280oK8yFFd8,9693
12
12
  afterpython/builders/llms_txt.py,sha256=9x-B1i1buTDJt4zGs8nugyto_pI5QIp7WUBcv-zpcso,9226
13
- afterpython/builders/marimo_notebook.py,sha256=dwSQl9sVMUKC1zPwBtGo-K8fKCU7g7qz9qM62jzbu3U,6033
13
+ afterpython/builders/marimo_notebook.py,sha256=J7bXcmt492vWZX_4L1x8ij77UlzRIkrU3Br0-LioUvU,6033
14
14
  afterpython/builders/markdown.py,sha256=0ExfRqvOTWsgUkAVaGGNkiNDtNedRr7JR1yTGxpbsmA,8736
15
15
  afterpython/builders/metadata.py,sha256=EueKoYWM__Hb9MwX0KYejxJvouoaAxD1oz9AP-Na-ZI,3830
16
- afterpython/builders/url_md.py,sha256=uuW3YCyphd-Qvl9Yopv48utu6y1tyfJHNc25sS-O6Yw,3526
16
+ afterpython/builders/url_md.py,sha256=MfXSy0f44J72OlHASir-Xscqq1onA10vtqUO_L4b1xM,3949
17
17
  afterpython/cli/__init__.py,sha256=G4AB-rllhmr7tURFc5cXJfkjZZ2bXtMBg3oy4zUGEFw,84
18
18
  afterpython/cli/commands/add.py,sha256=Ppp4msdvOwN9_-fdG5-b0-IV9p0Eef9CiIEU3srUjYk,1792
19
19
  afterpython/cli/commands/build.py,sha256=h0AaZuhjuxD_2Iu_CHD-3-OMBbi-ncAYuMUIR2pha1k,11798
@@ -34,7 +34,7 @@ afterpython/cli/commands/release.py,sha256=6SDEnPtuCL0_XUqWUlvRSKxAv011QqOD0kc5p
34
34
  afterpython/cli/commands/remove.py,sha256=Fl79JSI9IdHyzYflocXxcxgBkBPkNFAW98Yuijly26g,1812
35
35
  afterpython/cli/commands/serve.py,sha256=L0LIhMGByQA_mNTOAGHQTQ8TcBfi5sgmO1_iGI24QgI,822
36
36
  afterpython/cli/commands/start.py,sha256=q4rUkIllOO8wJQ9ABtsx-dqkW1yC0vltAtSkiHN_GH4,4002
37
- afterpython/cli/commands/sync.py,sha256=cNR0XeSPxAegETSXQa1bMlpKZoybBuhYzR2xd0e34zw,6409
37
+ afterpython/cli/commands/sync.py,sha256=Rx_Wmf8toys3FjENtcKVCw22ejEvOzyuqblWKA04dl0,6467
38
38
  afterpython/cli/commands/update.py,sha256=s84yzIBsG1lQQ61gWkLLP-KzxZ3GIfwosb0Ge2mR7AI,9685
39
39
  afterpython/cli/main.py,sha256=-9eQiIrUViSvof-Ic2RHF3AVZharPSQh9sJ7tkhvf6Q,5042
40
40
  afterpython/const.py,sha256=pZERFcKA60HsTob9kFCh34M_bZhyphHgLKUHab9pNFg,225
@@ -54,12 +54,12 @@ afterpython/tools/_git.py,sha256=aO-qDEQ4OevD_B74DYKK4nQKYeiaS1BXmYc8paJRfbQ,377
54
54
  afterpython/tools/branch_rules.py,sha256=ciQ2H2YBnoCZu_plyVeIHzf2FcAILRFhpjzOR2_IfJg,3159
55
55
  afterpython/tools/commitizen.py,sha256=ZO1qHmzUU0myd85tncv76WtIR7DB_AoQGSUvaf78Ohg,438
56
56
  afterpython/tools/github_actions.py,sha256=0xzOc41QF1PKagD1CW7BAt_Rugy0KCEx_KfcrPKVl3c,3168
57
- afterpython/tools/myst.py,sha256=c8M7QohKfdIQ0ckUZEhvrs0rQHTikIxvM190nFqYY9o,9399
57
+ afterpython/tools/myst.py,sha256=zlZcrofF8OAtg6UymF3b8xirV0KG1MXoMnpNaat5MAQ,9374
58
58
  afterpython/tools/pre_commit.py,sha256=oNsrUkPI13rSSBSyhyTTKPnQGhDxLIPYhxu6kL0fDmo,1206
59
59
  afterpython/tools/pyproject.py,sha256=hz-DOf-pkGBF2m_RqEZipAaO-NDbsLxrrc65LWAIwLA,3698
60
- afterpython/utils.py,sha256=4JlUuKdfIzdRpHhmlSsDEUQibxinkIREoEcY_FwsbpU,11673
60
+ afterpython/utils.py,sha256=VN6NBO2TKTFvqEvcxiHZrcmNuhl6wDlb0xFnlgqq9xk,12402
61
61
  afterpython/version.py,sha256=hihRV1S-gc1m46OaXD32ikkEkqzs-Gtq7AcAqOQV-wY,1603
62
- afterpython-0.3.15.dist-info/WHEEL,sha256=Q9FtwzuR2QE37l-JIkuyklGnJJiCBHKnsPVQ9vzCMzQ,81
63
- afterpython-0.3.15.dist-info/entry_points.txt,sha256=roa0PDgBueM1JPbRG0G2OSxzaN394OUL_oS9iEwVLBc,80
64
- afterpython-0.3.15.dist-info/METADATA,sha256=pUeIoJmSQkZRAstZOzIoTIdsyOU3pHZ2l8LyhvjpdGM,6181
65
- afterpython-0.3.15.dist-info/RECORD,,
62
+ afterpython-0.3.16.dist-info/WHEEL,sha256=wXwAVsgVaOZ_pwDFqQm5Rd6PID-Fc74nkLc8X8gHiDo,81
63
+ afterpython-0.3.16.dist-info/entry_points.txt,sha256=roa0PDgBueM1JPbRG0G2OSxzaN394OUL_oS9iEwVLBc,80
64
+ afterpython-0.3.16.dist-info/METADATA,sha256=ESzURVA2UGHO-opFKKprnc8nGFb-VqlPzr4sIIerVcE,6181
65
+ afterpython-0.3.16.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.11.17
2
+ Generator: uv 0.11.19
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any