Skip to content

Add distilled package standards page and review skill - #41

Open
ecomodeller wants to merge 2 commits into
mainfrom
package-standards
Open

Add distilled package standards page and review skill#41
ecomodeller wants to merge 2 commits into
mainfrom
package-standards

Conversation

@ecomodeller

Copy link
Copy Markdown
Member

The course is seven slide decks, which makes it awkward to point someone at a specific rule mid-conversation. standards.md distils it into ~45 normative rules, one linkable heading each, published at standards.html:

…/standards.html#mutable-default-arguments
…/standards.html#composition-over-inheritance
…/standards.html#every-dependency-is-a-decision

Only rules you'd assert at someone — the exposition stays in the slides. That includes rules no tool can check (keep PRs small, composition over inheritance), since the point is lecturing, not just linting.

Issues folded in

Seven that never made it into the slides:

Left open deliberately: #39 (great-docs is still "worth evaluating"), #31 and #32 (pedagogy, belongs in the slides), #25 and #24 (questions you haven't settled).

Review skill

.claude/skills/review-python-package/ audits a package repo against the page and links every finding to the rule it breaks, so the report is something you can paste into a PR and the reader gets the explanation without you there. It runs ruff for ground truth but deliberately won't run pytest/mypy/uv sync unprompted on an unfamiliar repo.

The finding URLs 404 until this merges and Pages rebuilds.

The course content is spread across seven slide decks, which makes it hard
to point someone at a specific rule. standards.md distils it into ~45
normative rules, one linkable heading each, published at standards.html.

Exposition is deliberately left in the slides: the page holds only rules
you would assert at someone, including ones no tool can check.

Also folds in seven issues that never made it into the slides: #40 (the
underscore convention, with the public-API-only amendment to breaking
changes), #37 (cost of dependencies), #36 (long signatures), #35
(inappropriate intimacy), #34 (comments say why), #33/#26 (deprecation),
#22 (changelog).

The review-python-package skill audits a repository against the page and
links each finding to the rule it breaks.
Comment thread standards.md
`FutureWarning` is for end users (always visible). `mypy --enable-error-code=deprecated`
catches uses of `@deprecated` at type-check time.

### Changelog

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice to have, but is tedious to curate manually. Think twice before spending time on this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — demoted to Nice in 0055f53, with the cost stated plainly ("a stale changelog is worse than none; if nobody reads it, skip it"). Rather than drop the rule, it now points at towncrier / git-cliff so the work lands on the author of each PR instead of on you at release time.

Comment thread standards.md Outdated
success — a non-empty string is truthy.

```python
if is_operable(height=12.0, period=5.0): # returns "No way!" — and this runs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns None. Fix it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0055f53 — the example only had the call site, so nothing showed the mixed return. It now defines is_operable, with the str / None / bool paths visible, plus a line making the implicit-None case explicit as the same bug.

I read this as being about the One return type example. If you meant the clip above it, that one returns None on purpose — it is the bad variant showing mutation — so say the word and I will handle it differently.

Comment thread standards.md Outdated
```

### Keyword-only arguments
*Nice.* `def f(*, x, y)` forces callers to be explicit and lets you reorder parameters later

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be more nuanced. One or two positional arguments are fine, that is usually the data the function is operating upon. More than two positional arguments are a strong code smell.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adopted your wording in 0055f53 and promoted the rule to Recommended. It now says one or two positional parameters is fine (the data being operated on), everything after belongs behind the *, and three or more positionals is a strong smell — resample(df, 3, 0, True) as the counter-example.

This collided with When a long signature is a smell, which said "the smell is not the count". Scoped that one to the total count and cross-linked the two so the page does not argue with itself.

Comment thread standards.md Outdated
```

### Examples that are tested
*Nice.* `doctest` runs the examples in your docstrings. Documentation that is wrong is worse

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Quarto for documentation, where the snippets are run as part of the build is another option.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0055f53doctest for docstring examples, Quarto for prose pages where the build executes every snippet and fails first.

Comment thread standards.md
```

### Published API documentation
*Recommended.* `mkdocs` + `mkdocstrings` + GitHub Pages, at

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quarto or the new framework Great Docs (wrapping Quarto), or zensical are all viable options.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0055f53mkdocs + mkdocstrings stays the default, with Quarto, Great Docs and zensical named as viable alternatives (all linked).

Worth deciding whether #39 stays open now that the page names Great Docs.

Comment thread standards.md Outdated
### Published API documentation
*Recommended.* `mkdocs` + `mkdocstrings` + GitHub Pages, at
`https://dhi.github.io/<repository>/`. Note that a private repository can still have a public
website — `robots.txt` hides it from search engines but is not security.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer relevant. With GitHub Enterprise, we can have private pages.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in 0055f53. The robots.txt sentence is gone; the rule now says a private repository can have access-controlled Pages on GitHub Enterprise, and to use that rather than relying on the URL not being found.

Comment thread standards.md Outdated
```

### Lint and format with ruff
*Recommended.* `ruff check` finds unused imports, undefined names and dead variables — usually

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason not use ruff for linting and formatting.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your sentence is now the opening of the rule in 0055f53, with the reason spelled out: one binary, no configuration required, replaces flake8 + black + isort.

I left the severity at Recommended rather than promoting it. Blocker is defined at the top of the page as "not fit to share", and an unlinted package is still shareable — it would be the first rule to stretch that definition. Happy to promote it if you would rather the legend bend.

Comment thread standards.md
```

### Makefile
*Nice.* One source of truth for how to run the project's tools, and the fastest onboarding

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider just instead of make, more modern and better cross platform support.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 0055f53 — the heading is now A task runner: Makefile if everyone is on Linux or macOS, just if anyone is on Windows, where make is absent by default and just is a single binary (uv tool install rust-just). Both recipe files are shown, both using uv run. Anchor changed to #a-task-runner; nothing linked to the old one.

Comment thread standards.md
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ["3.10", "3.13"]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matrix testing is powerful, but don't go overboard. If your package is actually an application, targeting a single Python version might be ok. CI is great, but it is not free.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0055f53 — "test what you claim and no more", CI is not free, every cell costs minutes on every push. Uses the library/application split the page already makes, cross-linked: an application has one deployment target, so one cell is the honest matrix.

Comment thread standards.md Outdated
## Release

### Publish from a GitHub release
*Recommended.* Tag a release, let a workflow build and publish. Use Trusted Publishers so

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using git tags to track released versions makes your life easy. Just do it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promoted to its own rule in 0055f53Tag every release, Recommended, annotated vX.Y.Z pushed, framed as what makes "which commit is 1.2.0?" answerable a year later. Two commands, so "just do it" is in there.

Comment thread standards.md Outdated

```yaml
on:
release:

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A viable alternative is to trigger automated releases via git tags.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0055f53 — the rule is now Publish from a tag or a release and shows on: push: tags: ["v*"], with the release trigger as the inline alternative and one clause on the tradeoff: the release gives you somewhere for release notes, the tag is one step fewer. Trusted Publishers works with either.

standards.md:
- Keyword-only arguments: rewritten around positional count — one or two
  for the data, config after the `*`, three+ is the smell. Now Recommended.
- Changelog demoted to Nice; hand curation is real work, so point at
  towncrier/git-cliff instead of prescribing it.
- One return type: show the function, so the None path is visible.
- New rules: Tag every release, Type checking in CI.
- Publish from a tag or a release, showing the `on: push: tags` trigger.
- Test the matrix: CI is not free; test only what you claim to support.
- Docs: name Quarto, Great Docs and zensical as alternatives; replace the
  robots.txt note with access-controlled Pages on GitHub Enterprise.
- Makefile becomes A task runner, covering `just` for Windows.
- Fix checkout@v3 -> v4 and a backwards `min`/`max` in the clip example.

review-python-package skill:
- Use Glob/Read/Grep instead of `ls`, `cat` and piped `grep` — the reviewer
  may be on Windows with no POSIX shell. Table-escaped pipes had also made
  the mutable-default pattern match nothing.
- `uvx ruff`, and say so when the repo has no ruff config.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant