Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## 2025-07-17 - Efficiency of str.isprintable()
**Learning:** Checking for printable characters via `not value.isprintable()` is ~15-18x faster than manual character-by-character iteration with `any(not c.isprintable() for c in value)` in Python.
**Action:** Always prefer the built-in `str.isprintable()` method over manual loops or generators for character validation.

## 2025-07-17 - Grouped I/O in scripts/init.py
**Learning:** Performing multiple consecutive file reads/writes on the same files (`pyproject.toml`, `mkdocs.yml`) causes redundant disk I/O overhead.
**Action:** Group file modifications by file path to perform exactly one read and one write operation per file.
134 changes: 108 additions & 26 deletions scripts/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@

from click import ClickException, UsageError, command, confirm, echo, option, secho

# Static global paths for template file replacements
APP_MD_PATH = Path("docs/reference/app.md")
MKDOCS_PATH = Path("mkdocs.yml")
PYPROJECT_PATH = Path("pyproject.toml")
README_PATH = Path("docs/README.md")
CODEOWNERS_PATH = Path(".github/CODEOWNERS")
FUNDING_PATH = Path(".github/FUNDING.yml")


def _get_git_config(key: str) -> str:
try:
Expand Down Expand Up @@ -47,7 +55,7 @@
]:
if len(value) > 100:
raise UsageError(f"Invalid {label}: maximum length is 100 characters.")
if any(not c.isprintable() for c in value):
if not value.isprintable():
raise UsageError(f"Invalid {label}: control characters are not allowed.")
if label != "description" and '"' in value:
raise UsageError(f"Invalid {label}: double quotes are not allowed.")
Expand All @@ -64,7 +72,7 @@
raise UsageError(f"Invalid email address '{email}'.")


def _perform_replacements(source: str, github: str, name: str, description: str, author: str, email: str):

Check failure on line 75 in scripts/init.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 24 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=cur8d_python&issues=AZ9uUVwmjx1_oJFvwIju&open=AZ9uUVwmjx1_oJFvwIju&pullRequest=117
# Sanitize for TOML double-quoted strings (escape backslashes and double quotes)
def toml_escape(s: str) -> str:
return s.replace("\\", "\\\\").replace('"', '\\"')
Expand All @@ -73,31 +81,105 @@
escaped_author = toml_escape(author)
escaped_email = toml_escape(email)

replacements = [
("docs/reference/app.md", r"^::: project\.app", f"::: {source}.app"),
("mkdocs.yml", r"^repo_name: .*", f"repo_name: {github}/{name}"),
("mkdocs.yml", r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"),
("pyproject.toml", r"^source = \[.*\]", f'source = ["{source}"]'),
("pyproject.toml", r'^app = "project\.app:main"', f'app = "{source}.app:main"'),
("pyproject.toml", r'^name = ".*"', f'name = "{source}"'),
("pyproject.toml", r'^description = ".*"', f'description = "{escaped_description}"'),
("pyproject.toml", r"^authors = \[.*\]", f'authors = ["{escaped_author} <{escaped_email}>"]'),
("docs/README.md", r"^# .*", f"# {description}"),
(".github/CODEOWNERS", r"@.*", f"@{github}"),
(".github/FUNDING.yml", r"^github: \[.*\]", f"github: [{github}]"),
]

for filepath, pattern, replacement in replacements:
path = Path(filepath)
if not path.exists():
secho(f" Warning: File {filepath} not found, skipping. ⚠️", fg="yellow")
continue

content = path.read_text()
# Use a lambda for replacement to avoid regex backreference injection
new_content = re.sub(pattern, lambda _, r=replacement: r, content, flags=re.MULTILINE)
path.write_text(new_content)
secho(f" Updated {filepath} ✅", fg="blue")
# 1. Update docs/reference/app.md
if APP_MD_PATH.exists():
content = APP_MD_PATH.read_text()
new_content = re.sub(r"^::: project\.app", lambda _, r=f"::: {source}.app": r, content, flags=re.MULTILINE)
if new_content != content:
APP_MD_PATH.write_text(new_content)
secho(" Updated docs/reference/app.md ✅", fg="blue")
else:
secho(" Warning: File docs/reference/app.md not found, skipping. ⚠️", fg="yellow")

# 2. Update mkdocs.yml
if MKDOCS_PATH.exists():
content = MKDOCS_PATH.read_text()
new_content = re.sub(
r"^repo_name: .*",
lambda _, r=f"repo_name: {github}/{name}": r,
content,
flags=re.MULTILINE,
)
new_content = re.sub(
r"^repo_url: .*",
lambda _, r=f"repo_url: https://github.com/{github}/{name}": r,
new_content,
flags=re.MULTILINE,
)
if new_content != content:
MKDOCS_PATH.write_text(new_content)
secho(" Updated mkdocs.yml ✅", fg="blue")
else:
secho(" Warning: File mkdocs.yml not found, skipping. ⚠️", fg="yellow")

# 3. Update pyproject.toml
if PYPROJECT_PATH.exists():
content = PYPROJECT_PATH.read_text()
new_content = re.sub(
r"^source = \[.*\]",
lambda _, r=f'source = ["{source}"]': r,
content,
flags=re.MULTILINE,
)
new_content = re.sub(
r'^app = "project\.app:main"',
lambda _, r=f'app = "{source}.app:main"': r,
new_content,
flags=re.MULTILINE,
)
new_content = re.sub(
r'^name = ".*"',
lambda _, r=f'name = "{source}"': r,
new_content,
flags=re.MULTILINE,
)
new_content = re.sub(
r'^description = ".*"',
lambda _, r=f'description = "{escaped_description}"': r,
new_content,
flags=re.MULTILINE,
)
new_content = re.sub(
r"^authors = \[.*\]",
lambda _, r=f'authors = ["{escaped_author} <{escaped_email}>"]': r,
new_content,
flags=re.MULTILINE,
)
if new_content != content:
PYPROJECT_PATH.write_text(new_content)
secho(" Updated pyproject.toml ✅", fg="blue")
else:
secho(" Warning: File pyproject.toml not found, skipping. ⚠️", fg="yellow")

# 4. Update docs/README.md
if README_PATH.exists():
content = README_PATH.read_text()
new_content = re.sub(r"^# .*", lambda _, r=f"# {description}": r, content, flags=re.MULTILINE)
if new_content != content:
README_PATH.write_text(new_content)
secho(" Updated docs/README.md ✅", fg="blue")
else:
secho(" Warning: File docs/README.md not found, skipping. ⚠️", fg="yellow")

# 5. Update .github/CODEOWNERS
if CODEOWNERS_PATH.exists():
content = CODEOWNERS_PATH.read_text()
new_content = re.sub(r"@.*", lambda _, r=f"@{github}": r, content, flags=re.MULTILINE)
if new_content != content:
CODEOWNERS_PATH.write_text(new_content)
secho(" Updated .github/CODEOWNERS ✅", fg="blue")
else:
secho(" Warning: File .github/CODEOWNERS not found, skipping. ⚠️", fg="yellow")

# 6. Update .github/FUNDING.yml
if FUNDING_PATH.exists():
content = FUNDING_PATH.read_text()
new_content = re.sub(r"^github: \[.*\]", lambda _, r=f"github: [{github}]": r, content, flags=re.MULTILINE)
if new_content != content:
FUNDING_PATH.write_text(new_content)
secho(" Updated .github/FUNDING.yml ✅", fg="blue")
else:
secho(" Warning: File .github/FUNDING.yml not found, skipping. ⚠️", fg="yellow")


@command(context_settings={"help_option_names": ["-h", "--help"]})
Expand Down