@aacombarro89/praxis 0.1.9 → 0.1.11

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.
@@ -12,26 +12,22 @@ Layer 1 rules (think before coding, simplicity first, surgical changes,
12
12
  goal-driven execution) apply unchanged — this file adds only what is
13
13
  Python-specific.
14
14
 
15
- - **Mirror the nearest module.** Before adding a feature, open the closest
16
- existing file that already does the same kind of thing (a sibling in the same
17
- directory, an adjacent route/handler/service). Copy its structure — imports,
18
- class/function layout, error handling, naming — rather than inventing a new
19
- shape. The prior art is the template; matching it is verifiable, following
20
- prose is not.
21
- - **One module, one responsibility.** A new `.py` file exports the smallest
22
- coherent unit. Keep pure logic separate from I/O (filesystem, network, database)
23
- so the logic is testable without mocks — mirror how the existing modules here
24
- already split the two.
25
- - **Use the stdlib and installed deps before adding one.** Python stdlib
26
- (`pathlib`, `dataclasses`, `typing`, `json`, `os`) and a dependency already in
27
- `pyproject.toml` / `requirements*.txt` cover most needs. A new dependency for
28
- what a few lines do is debt; justify it against what is already imported in
29
- neighbouring files.
30
- - **Type hints are the contract.** Annotate inputs and outputs at module
31
- boundaries; validate external/untrusted input where it enters (a schema
32
- validator if the repo already uses one, e.g. pydantic). Don't re-validate data
33
- already typed and checked upstream.
34
15
  - **Follow the repo's packaging conventions.** Match the module layout, import
35
- style (absolute vs relative), and packaging already in use — don't introduce a
36
- second convention. If the project uses `src/` layout, honour it; if it uses
37
- flat packages, honour that.
16
+ style (absolute vs relative), and `src/` vs flat packaging already in use —
17
+ don't introduce a second convention.
18
+ - **No mutable default arguments.** A `[]` or `{}` default is shared across
19
+ every call; use a `None` sentinel and build the value inside the function
20
+ body.
21
+ - **Never swallow exceptions.** No bare `except:`; catch the narrowest
22
+ exception type and either handle it or re-raise with `raise ... from e` to
23
+ keep the chain.
24
+ - **Timezone-aware datetimes.** Use `datetime.now(timezone.utc)`, not naive
25
+ `datetime.now()` — a naive datetime is a latent bug at every boundary it
26
+ crosses.
27
+ - **No blocking calls inside `async def`.** `requests`, `time.sleep`, and sync
28
+ DB drivers stall the event loop; use the async equivalent or run it in an
29
+ executor.
30
+ - **Parameterized queries only.** Pass values as query parameters; never build
31
+ SQL with f-strings or `%` interpolation.
32
+ - **Context managers for resources.** Open files, connections, and locks with
33
+ `with` so they close on every path, including exceptions.
@@ -4,34 +4,28 @@ paths: ["**/*.py", "**/test_*.py", "**/*_test.py"]
4
4
 
5
5
  ## Python test-layer recipe (Layer 2)
6
6
 
7
- Choosing *where* a test belongs matters as much as writing it. Pick the layer
8
- that fails for the right reason, and mirror the repo's existing tests of that
9
- kind for structure and fixtures.
7
+ Choosing *where* a test belongs matters as much as writing it: pick unit,
8
+ integration, or end-to-end by what could actually break unit for a single
9
+ module's logic, integration for real seams (filesystem, a spawned process, a
10
+ database), end-to-end for an invariant the whole application must honour.
11
+ Then mirror the nearest existing test of that same layer for its location
12
+ (`tests/` or alongside), `test_*.py` naming, and `conftest.py` fixtures rather
13
+ than inventing a new shape.
10
14
 
11
15
  Layer 1 rules (think before coding, simplicity first, surgical changes,
12
16
  goal-driven execution) apply unchanged — this file adds only what is
13
17
  Python-testing-specific.
14
18
 
15
- - **Harness before features.** A rule that matters has a test. Write (or extend)
16
- the test that pins the new behaviour *before or with* the code that satisfies
17
- it — never land logic whose only check is "it looked right". Make the failing
18
- test the prompt: it states what is missing.
19
- - **Pick the layer by what could break.**
20
- - *Unit* pure logic, branches, parsing, a single module's contract. Fast, no
21
- I/O, no fixtures. Default here; most logic belongs at this layer.
22
- - *Integration* two or more modules composed, or real I/O (filesystem, a
23
- spawned process, a database). Use when the bug would live in the seam between
24
- units, not inside one.
25
- - *End-to-end* an invariant the whole application must honour, or a contract
26
- a future change must not break. Use for the rails, not for every feature.
27
- - **Mirror the existing suite.** Find the nearest test of the same layer and copy
28
- its setup — `pytest` is the default, its file location (`tests/` or alongside
29
- source), naming convention (`test_*.py`), how it builds fixtures (`conftest.py`,
30
- `@pytest.fixture`) and asserts. Don't introduce a second runner or a new fixture
31
- style.
32
- - **Assert behaviour, not implementation.** Test observable inputs→outputs and
33
- error cases at the boundary; avoid asserting private internals that refactors
34
- will churn. One clear failing assertion beats five brittle ones.
35
- - **No new framework for one test.** The repo's installed runner and plain
36
- `assert` statements cover almost everything. Reach for a new testing dependency
37
- only when the existing one demonstrably cannot express the check.
19
+ - **Plain `assert` with pytest.** Let pytest's assertion rewriting report the
20
+ values; don't wrap checks in `unittest` assert methods or add a second
21
+ runner.
22
+ - **Prefer builtin fixtures.** Reach for `tmp_path`, `monkeypatch`, `capsys`
23
+ over hand-rolled setup/teardown code they clean up after themselves.
24
+ - **Parametrize instead of copy-paste.** Use `@pytest.mark.parametrize` for
25
+ the same body run over many inputs, not duplicated test functions.
26
+ - **Default fixtures to function scope.** Widen to `module`/`session` scope
27
+ only for an expensive read-only resource; a wider scope shares mutable
28
+ state across tests that expect isolation.
29
+ - **Assert error paths with `pytest.raises`.** Use
30
+ `with pytest.raises(Err, match=...)` and assert on the message so the wrong
31
+ error can't pass silently.
@@ -13,32 +13,37 @@ Layer 1 rules (think before coding, simplicity first, surgical changes,
13
13
  goal-driven execution) apply unchanged — this file adds only what is
14
14
  React-specific.
15
15
 
16
- - **Mirror the nearest component.** Before adding a component, open the closest
17
- existing one that does the same kind of thing (a sibling in the same directory
18
- or feature folder). Copy its structure — file layout, how it exports, how it
19
- receives and passes props, where state lives — rather than inventing a new
20
- shape. The prior art is the template; matching it is verifiable, following
21
- prose is not.
22
16
  - **Props typed at the boundary.** Every component declares an explicit props
23
- interface or type at the top of its file. Accept the minimum props needed; no
24
- catch-all `[key: string]: unknown`. Children are explicit when the component
25
- uses them.
17
+ interface or type at the top of its file. Accept the minimum props needed;
18
+ no catch-all `[key: string]: unknown`.
26
19
  - **Composition over configuration.** Prefer a small focused component used in
27
20
  multiple arrangements over a large one with a boolean-flag API. When a
28
- component's props grow into a switch statement, split into two components that
29
- share a common primitive.
30
- - **Native HTML elements before a component library.** A `<button>`, `<label>`,
31
- or `<ul>` is always the first option. Reach for a library component only when
32
- the native element demonstrably cannot satisfy the requirement — and then use
33
- whichever library the repo already imports, not a new one.
34
- - **Accessibility is non-negotiable.** Every interactive element is reachable by
35
- keyboard and has a visible focus style. Form inputs have associated `<label>`
36
- elements (via `htmlFor`/`id` or wrapping). Non-decorative images have
37
- descriptive `alt` text. Custom interactive elements carry the correct ARIA
38
- `role` and `aria-*` attributes. These are not optional polish; ship them with
39
- the component.
40
- - **Follow the repo's hooks and state conventions.** Use `useState`/`useReducer`
41
- at the level state is first needed, and lift only when a sibling needs it.
42
- Match whatever custom-hook patterns the repo already uses; don't introduce a
43
- new global-state solution for a single component's needs. Mirror how the
44
- existing components here manage side effects and data fetching.
21
+ component's props grow into a switch statement, split into two components
22
+ that share a common primitive.
23
+ - **Native HTML elements before a component library.** A `<button>`,
24
+ `<label>`, or `<ul>` is always the first option. Reach for a library
25
+ component only when the native element demonstrably cannot satisfy the
26
+ requirement — and then use whichever library the repo already imports.
27
+ - **Accessibility is non-negotiable.** Every interactive element is reachable
28
+ by keyboard and has a visible focus style. Form inputs have associated
29
+ `<label>` elements. Non-decorative images have descriptive `alt` text.
30
+ Custom interactive elements carry the correct ARIA `role` and `aria-*`
31
+ attributes.
32
+ - **Follow the repo's hooks and state conventions.** Use
33
+ `useState`/`useReducer` at the level state is first needed, and lift only
34
+ when a sibling needs it. Don't introduce a new global-state library for a
35
+ single component's needs.
36
+ - **You might not need an effect.** Derive values during render instead of
37
+ mirroring props/state into state; an effect is for synchronizing with an
38
+ external system, not for computing from existing state.
39
+ - **Name the effect callback.** `useEffect(function syncScrollPosition() { … },
40
+ [deps])` — a named function makes the effect's intent legible and gives it a
41
+ real name in stack traces and React DevTools instead of an anonymous arrow.
42
+ - **Every subscription effect returns its cleanup.** A listener, timer, or
43
+ subscription set up in an effect is torn down in the function the effect
44
+ returns.
45
+ - **`key` is stable identity, not the array index.** Keys must survive
46
+ reordering; index keys corrupt state on insert or remove.
47
+ - **No `memo`/`useMemo`/`useCallback` by default.** Add memoization only
48
+ against a measured problem; premature memoization is complexity with no
49
+ payoff.
@@ -4,36 +4,27 @@ paths: ["**/*.test.tsx", "**/*.test.jsx", "**/*.spec.tsx", "**/*.spec.jsx"]
4
4
 
5
5
  ## React test-layer recipe (Layer 2)
6
6
 
7
- Choosing *where* a test belongs matters as much as writing it. Pick the layer
8
- that fails for the right reason, and mirror the repo's existing tests of that
9
- kind for structure and setup.
7
+ Choosing *where* a test belongs matters as much as writing it: pick unit
8
+ render, integration, or end-to-end by what could actually break a single
9
+ component in isolation, two or more components (or a component wired to a
10
+ real hook/context/store), or a full user journey in a real browser reserved
11
+ for critical paths. Then mirror the nearest existing test's RTL setup,
12
+ provider wrapping, runner, and naming rather than inventing a new shape.
10
13
 
11
14
  Layer 1 rules (think before coding, simplicity first, surgical changes,
12
15
  goal-driven execution) apply unchanged — this file adds only what is
13
16
  React-testing-specific.
14
17
 
15
- - **Test behaviour, not implementation.** Query by roles and user-visible text
16
- (`getByRole`, `getByLabelText`, `getByText`)not by CSS class, component
17
- display name, or internal state. A test that breaks on a classname rename
18
- without catching a real regression is noise; a test that catches a missing
19
- label is signal.
20
- - **Pick the layer by what could break.**
21
- - *Unit render* — a single component in isolation: does it render the right
22
- output for a given set of props, show/hide the right elements, respond to
23
- user events? Default here for most component logic.
24
- - *Integration* two or more components composed, or a component wired to a
25
- real hook/context/store. Use when the bug would live in the seam between
26
- components, not inside one.
27
- - *End-to-end* — a full user journey in a real browser. Use only for the
28
- critical paths that no unit or integration test can protect; reserve for the
29
- rails, not for every feature.
30
- - **Use React Testing Library.** The repo's RTL setup (`render`, `screen`,
31
- `userEvent`, `fireEvent`) covers almost everything. Mirror the nearest
32
- existing test file for setup, cleanup, and how it wraps providers. Don't
33
- introduce a second render helper or a separate component-testing framework.
34
- - **Mirror the existing suite.** Copy the nearest test file's structure —
35
- runner, file naming, directory location, how it imports the component under
36
- test, how it sets up providers or mocks. Consistency beats cleverness.
37
- - **No new framework for one test.** RTL and the repo's installed runner (e.g.
38
- vitest/jest) cover almost every component test need. Reach for a new dependency
39
- only when the existing tooling demonstrably cannot express the check.
18
+ - **Query by role, label, or text.** Use `getByRole`, `getByLabelText`,
19
+ `getByText` — never CSS class, a test id where a role exists, or internal
20
+ component state. A test that breaks on a classname rename without catching
21
+ a regression is noise.
22
+ - **Interactions go through `userEvent`.** Await it
23
+ (`await userEvent.click(...)`); prefer it over `fireEvent`, which skips the
24
+ events real users trigger.
25
+ - **Await async UI with `findBy*`.** Use `findByRole`/`findByText` (or
26
+ `waitFor`) for content that appears after an effect; never put a
27
+ side-effecting call inside `waitFor`.
28
+ - **Wrap renders with the repo's provider helper.** Mirror how the nearest
29
+ test supplies context, store, or router rather than rendering the bare
30
+ component.