elecdef 0.1.0__tar.gz

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 (162) hide show
  1. elecdef-0.1.0/.gitattributes +1 -0
  2. elecdef-0.1.0/.github/workflows/ci.yml +31 -0
  3. elecdef-0.1.0/.github/workflows/docs.yml +35 -0
  4. elecdef-0.1.0/.github/workflows/publish.yml +39 -0
  5. elecdef-0.1.0/.gitignore +12 -0
  6. elecdef-0.1.0/.python-version +1 -0
  7. elecdef-0.1.0/AGENTS.md +12 -0
  8. elecdef-0.1.0/CLAUDE.md +48 -0
  9. elecdef-0.1.0/DESIGN.md +209 -0
  10. elecdef-0.1.0/LICENSE +21 -0
  11. elecdef-0.1.0/PKG-INFO +34 -0
  12. elecdef-0.1.0/README.md +23 -0
  13. elecdef-0.1.0/RULES.md +195 -0
  14. elecdef-0.1.0/data/tables/awg.toml +49 -0
  15. elecdef-0.1.0/data/tables/breaker_currents.toml +21 -0
  16. elecdef-0.1.0/data/tables/breaker_curves.toml +11 -0
  17. elecdef-0.1.0/data/tables/breaker_icn.toml +12 -0
  18. elecdef-0.1.0/data/tables/cable_voltages.toml +11 -0
  19. elecdef-0.1.0/data/tables/core_colours.toml +50 -0
  20. elecdef-0.1.0/data/tables/cross_sections.toml +39 -0
  21. elecdef-0.1.0/data/tables/e_series.toml +54 -0
  22. elecdef-0.1.0/data/tables/held.toml +12 -0
  23. elecdef-0.1.0/data/tables/overvoltage_impulse.toml +10 -0
  24. elecdef-0.1.0/data/tables/trip_times.toml +10 -0
  25. elecdef-0.1.0/docs/CHANGELOG.md +23 -0
  26. elecdef-0.1.0/docs/api-snapshot.txt +544 -0
  27. elecdef-0.1.0/docs/guide/blocks.md +203 -0
  28. elecdef-0.1.0/docs/guide/cables.md +40 -0
  29. elecdef-0.1.0/docs/guide/connections.md +18 -0
  30. elecdef-0.1.0/docs/guide/examples.md +155 -0
  31. elecdef-0.1.0/docs/guide/index.md +11 -0
  32. elecdef-0.1.0/docs/guide/lookup.md +57 -0
  33. elecdef-0.1.0/docs/guide/pins.md +39 -0
  34. elecdef-0.1.0/docs/guide/ranges.md +73 -0
  35. elecdef-0.1.0/docs/guide/tables.md +66 -0
  36. elecdef-0.1.0/docs/guide/values.md +84 -0
  37. elecdef-0.1.0/docs/guide/vocab.md +525 -0
  38. elecdef-0.1.0/docs/index.md +6 -0
  39. elecdef-0.1.0/docs/llms.txt +178 -0
  40. elecdef-0.1.0/justfile +61 -0
  41. elecdef-0.1.0/mkdocs.yml +35 -0
  42. elecdef-0.1.0/pyproject.toml +180 -0
  43. elecdef-0.1.0/scripts/build_site.py +142 -0
  44. elecdef-0.1.0/scripts/design_guard.py +91 -0
  45. elecdef-0.1.0/scripts/family_compare.py +55 -0
  46. elecdef-0.1.0/scripts/family_files.txt +18 -0
  47. elecdef-0.1.0/scripts/gen_tables.py +271 -0
  48. elecdef-0.1.0/scripts/release_guard.py +93 -0
  49. elecdef-0.1.0/src/elecdef/__init__.py +51 -0
  50. elecdef-0.1.0/src/elecdef/__main__.py +18 -0
  51. elecdef-0.1.0/src/elecdef/_api.py +56 -0
  52. elecdef-0.1.0/src/elecdef/_block.py +56 -0
  53. elecdef-0.1.0/src/elecdef/_body.py +25 -0
  54. elecdef-0.1.0/src/elecdef/_breaker.py +62 -0
  55. elecdef-0.1.0/src/elecdef/_check.py +62 -0
  56. elecdef-0.1.0/src/elecdef/_checkpins.py +84 -0
  57. elecdef-0.1.0/src/elecdef/_checkwalk.py +26 -0
  58. elecdef-0.1.0/src/elecdef/_coil.py +52 -0
  59. elecdef-0.1.0/src/elecdef/_connection.py +28 -0
  60. elecdef-0.1.0/src/elecdef/_contact.py +69 -0
  61. elecdef-0.1.0/src/elecdef/_device.py +36 -0
  62. elecdef-0.1.0/src/elecdef/_issue.py +26 -0
  63. elecdef-0.1.0/src/elecdef/_numbering.py +29 -0
  64. elecdef-0.1.0/src/elecdef/_overload.py +76 -0
  65. elecdef-0.1.0/src/elecdef/_plccard.py +71 -0
  66. elecdef-0.1.0/src/elecdef/_product.py +54 -0
  67. elecdef-0.1.0/src/elecdef/_psu.py +74 -0
  68. elecdef-0.1.0/src/elecdef/_quantity.py +160 -0
  69. elecdef-0.1.0/src/elecdef/_range.py +176 -0
  70. elecdef-0.1.0/src/elecdef/_rating.py +26 -0
  71. elecdef-0.1.0/src/elecdef/_relay.py +121 -0
  72. elecdef-0.1.0/src/elecdef/cable.py +102 -0
  73. elecdef-0.1.0/src/elecdef/computer.py +61 -0
  74. elecdef-0.1.0/src/elecdef/conductor.py +30 -0
  75. elecdef-0.1.0/src/elecdef/connections.py +34 -0
  76. elecdef-0.1.0/src/elecdef/connector.py +56 -0
  77. elecdef-0.1.0/src/elecdef/fuse.py +91 -0
  78. elecdef-0.1.0/src/elecdef/pins.py +158 -0
  79. elecdef-0.1.0/src/elecdef/plc.py +124 -0
  80. elecdef-0.1.0/src/elecdef/py.typed +0 -0
  81. elecdef-0.1.0/src/elecdef/supply.py +26 -0
  82. elecdef-0.1.0/src/elecdef/tables/HELD.txt +20 -0
  83. elecdef-0.1.0/src/elecdef/tables/__init__.py +27 -0
  84. elecdef-0.1.0/src/elecdef/tables/awg.py +98 -0
  85. elecdef-0.1.0/src/elecdef/tables/breaker_currents.py +22 -0
  86. elecdef-0.1.0/src/elecdef/tables/breaker_curves.py +10 -0
  87. elecdef-0.1.0/src/elecdef/tables/breaker_icn.py +13 -0
  88. elecdef-0.1.0/src/elecdef/tables/cable_voltages.py +12 -0
  89. elecdef-0.1.0/src/elecdef/tables/core_colours.py +51 -0
  90. elecdef-0.1.0/src/elecdef/tables/cross_sections.py +35 -0
  91. elecdef-0.1.0/src/elecdef/tables/e_series.py +50 -0
  92. elecdef-0.1.0/src/elecdef/tables/overvoltage_impulse.py +12 -0
  93. elecdef-0.1.0/src/elecdef/tables/trip_times.py +11 -0
  94. elecdef-0.1.0/src/elecdef/terminal.py +87 -0
  95. elecdef-0.1.0/src/elecdef/vocab/__init__.py +118 -0
  96. elecdef-0.1.0/src/elecdef/vocab/_base.py +17 -0
  97. elecdef-0.1.0/src/elecdef/vocab/_cable.py +92 -0
  98. elecdef-0.1.0/src/elecdef/vocab/_coil.py +53 -0
  99. elecdef-0.1.0/src/elecdef/vocab/_conditions.py +51 -0
  100. elecdef-0.1.0/src/elecdef/vocab/_connection.py +84 -0
  101. elecdef-0.1.0/src/elecdef/vocab/_contact.py +30 -0
  102. elecdef-0.1.0/src/elecdef/vocab/_misc.py +85 -0
  103. elecdef-0.1.0/src/elecdef/vocab/_plc.py +105 -0
  104. elecdef-0.1.0/src/elecdef/vocab/_protection.py +93 -0
  105. elecdef-0.1.0/tests/family.py +461 -0
  106. elecdef-0.1.0/tests/fixtures/issues/bad_marking.py +10 -0
  107. elecdef-0.1.0/tests/fixtures/issues/dangling_link.py +11 -0
  108. elecdef-0.1.0/tests/fixtures/issues/duplicate_marking.py +10 -0
  109. elecdef-0.1.0/tests/fixtures/issues/end_order.py +10 -0
  110. elecdef-0.1.0/tests/fixtures/issues/missing_figure.py +10 -0
  111. elecdef-0.1.0/tests/fixtures/issues/pick_ambiguous.py +14 -0
  112. elecdef-0.1.0/tests/fixtures/issues/pick_no_rating.py +13 -0
  113. elecdef-0.1.0/tests/fixtures/issues/unknown_class.py +16 -0
  114. elecdef-0.1.0/tests/fixtures/malformed/at_two_conditions.py +8 -0
  115. elecdef-0.1.0/tests/fixtures/malformed/link_one_marking.py +7 -0
  116. elecdef-0.1.0/tests/fixtures/malformed/link_three_markings.py +7 -0
  117. elecdef-0.1.0/tests/fixtures/malformed/quantity_bool.py +10 -0
  118. elecdef-0.1.0/tests/fixtures/malformed/range_no_end.py +7 -0
  119. elecdef-0.1.0/tests/test_agents.py +112 -0
  120. elecdef-0.1.0/tests/test_api.py +76 -0
  121. elecdef-0.1.0/tests/test_blocks.py +213 -0
  122. elecdef-0.1.0/tests/test_boundary.py +15 -0
  123. elecdef-0.1.0/tests/test_boundary_e.py +102 -0
  124. elecdef-0.1.0/tests/test_cable.py +126 -0
  125. elecdef-0.1.0/tests/test_check.py +7 -0
  126. elecdef-0.1.0/tests/test_check_e.py +300 -0
  127. elecdef-0.1.0/tests/test_connections.py +105 -0
  128. elecdef-0.1.0/tests/test_contact_coil.py +122 -0
  129. elecdef-0.1.0/tests/test_deps.py +7 -0
  130. elecdef-0.1.0/tests/test_design_guard.py +121 -0
  131. elecdef-0.1.0/tests/test_design_guard_e.py +17 -0
  132. elecdef-0.1.0/tests/test_docs.py +104 -0
  133. elecdef-0.1.0/tests/test_family_compare.py +58 -0
  134. elecdef-0.1.0/tests/test_family_rules.py +36 -0
  135. elecdef-0.1.0/tests/test_kinds.py +191 -0
  136. elecdef-0.1.0/tests/test_lean.py +98 -0
  137. elecdef-0.1.0/tests/test_licence.py +27 -0
  138. elecdef-0.1.0/tests/test_malformed.py +7 -0
  139. elecdef-0.1.0/tests/test_pick.py +90 -0
  140. elecdef-0.1.0/tests/test_pin_roles_e.py +11 -0
  141. elecdef-0.1.0/tests/test_pins.py +342 -0
  142. elecdef-0.1.0/tests/test_plc.py +118 -0
  143. elecdef-0.1.0/tests/test_product.py +48 -0
  144. elecdef-0.1.0/tests/test_protection.py +141 -0
  145. elecdef-0.1.0/tests/test_psu.py +75 -0
  146. elecdef-0.1.0/tests/test_pure.py +31 -0
  147. elecdef-0.1.0/tests/test_pyproject_pins.py +41 -0
  148. elecdef-0.1.0/tests/test_quantity.py +100 -0
  149. elecdef-0.1.0/tests/test_range.py +138 -0
  150. elecdef-0.1.0/tests/test_relay.py +131 -0
  151. elecdef-0.1.0/tests/test_release_guard.py +114 -0
  152. elecdef-0.1.0/tests/test_rules_lists.py +85 -0
  153. elecdef-0.1.0/tests/test_site.py +100 -0
  154. elecdef-0.1.0/tests/test_standalone.py +24 -0
  155. elecdef-0.1.0/tests/test_supply.py +57 -0
  156. elecdef-0.1.0/tests/test_surface.py +170 -0
  157. elecdef-0.1.0/tests/test_tables.py +155 -0
  158. elecdef-0.1.0/tests/test_terminal_connector.py +144 -0
  159. elecdef-0.1.0/tests/test_vocab.py +7 -0
  160. elecdef-0.1.0/tests/test_vocab_e.py +108 -0
  161. elecdef-0.1.0/tests/test_wording.py +131 -0
  162. elecdef-0.1.0/uv.lock +479 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,31 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ ci:
13
+ runs-on: ubuntu-24.04
14
+ steps:
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
+ with:
17
+ fetch-depth: 0
18
+ fetch-tags: true
19
+ - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
20
+ with:
21
+ version: "0.13.5"
22
+ - run: uv sync
23
+ - run: uv python install 3.14
24
+ - run: uvx --from rust-just==1.59.0 just ci
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
26
+ if: github.event.repository.private == false
27
+ with:
28
+ repository: OleJBondahl/refdef
29
+ path: sibling
30
+ - run: uv run python scripts/family_compare.py sibling
31
+ if: github.event.repository.private == false
@@ -0,0 +1,35 @@
1
+ name: docs
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*.*.*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-24.04
13
+ steps:
14
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
16
+ with:
17
+ version: "0.13.5"
18
+ - run: uv run python scripts/build_site.py
19
+ - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
20
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
21
+ with:
22
+ path: .site/
23
+
24
+ deploy:
25
+ needs: build
26
+ runs-on: ubuntu-24.04
27
+ environment:
28
+ name: github-pages
29
+ url: ${{ steps.deployment.outputs.page_url }}
30
+ permissions:
31
+ pages: write
32
+ id-token: write
33
+ steps:
34
+ - id: deployment
35
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
@@ -0,0 +1,39 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*.*.*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-24.04
13
+ steps:
14
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
16
+ with:
17
+ version: "0.13.5"
18
+ - name: The tag matches the package version
19
+ run: test "v$(uv version --short)" = "$GITHUB_REF_NAME"
20
+ - run: uv build
21
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+
26
+ pypi:
27
+ needs: build
28
+ runs-on: ubuntu-24.04
29
+ environment: pypi
30
+ permissions:
31
+ id-token: write # trusted publishing: no token secret
32
+ steps:
33
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
38
+ with:
39
+ attestations: true
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ .ruff_cache/
4
+ .pytest_cache/
5
+ .coverage
6
+ .superpowers/
7
+ claude-tools/
8
+ dist/
9
+ .site-src/
10
+ .site/
11
+ .elecdef/
12
+ .cache/
@@ -0,0 +1 @@
1
+ 3.15.0
@@ -0,0 +1,12 @@
1
+ # Agent guide
2
+
3
+ - Docs site: https://olejbondahl.github.io/elecdef/
4
+ - Surface: `python -m elecdef api`, snapshot in `docs/api-snapshot.txt`
5
+ - Guide: `docs/guide/`
6
+ - Design: `DESIGN.md`
7
+ - Rules: `RULES.md`
8
+ - A change follows RULES.md.
9
+ - Design-review paths: `DESIGN.md`, `RULES.md`, `docs/api-snapshot.txt`, `src/elecdef/tables/`.
10
+ A commit touching them needs a `Design-review: <ID>` trailer.
11
+ - A need the surface does not meet is a reviewed design change, never a workaround.
12
+ - Use `uv` only, with exact pins.
@@ -0,0 +1,48 @@
1
+ # elecdef: agent guide
2
+
3
+ elecdef is a standard-agnostic electrical engineering vocabulary: strict, typed, immutable Python
4
+ values with no runtime dependency. A sibling library follows the same family rules.
5
+ Docs: <https://olejbondahl.github.io/elecdef/>. Surface: `python -m elecdef api`.
6
+
7
+ ## Read first
8
+
9
+ Read `DESIGN.md`, then `RULES.md`. They bind: a change follows RULES.md, and code that disagrees
10
+ with them is wrong. Family rules F1-F17 are shared with the sibling library; E1-E19 are ours.
11
+
12
+ ## Gate
13
+
14
+ `just ci` runs locally (no hosted gate beyond one CI job): ruff, ty, vulture, pytest with a 95 %
15
+ coverage floor, the tests on Python 3.14, the two guards, and the strict site build. Its last
16
+ stdout line is `site built: .site`. Each shared test guards one family rule:
17
+
18
+ - `test_deps` (F1): no runtime dependency, stdlib and the package only.
19
+ - `test_pure` (F2), `test_check` and `test_malformed` (F3): frozen values, no I/O, issues as values.
20
+ - `test_vocab` (F4): a closed vocabulary is never a bare `str`.
21
+ - `test_boundary` (F5, F6, F13): no boundary names, no application default, no sibling import.
22
+ - `test_wording` (F7 and the em-dash rule): no standard cited by number, clause or year.
23
+ - `test_surface` (F8, F15): the snapshot equals `python -m elecdef api`; no operators on surface classes.
24
+ - `test_docs` (F9) and `test_site`: every name has a docstring and a runnable guide block.
25
+ - `test_lean` (F10): no nested def, 300 code lines, short docstrings, every noqa with a reason.
26
+ - `test_release_guard` (F11) and `test_design_guard` (F12): the guard scripts can fail.
27
+ - `test_licence` (F14), `test_agents` (F16), `test_standalone` (F17), `test_family_rules`, `test_pyproject_pins`.
28
+
29
+ ## Process
30
+
31
+ - F12: a commit that touches a review path (`DESIGN.md`, `RULES.md`, `docs/api-snapshot.txt`,
32
+ `src/elecdef/tables/`) carries a trailer `Design-review: <ID>`. The maintainer approves the
33
+ contract; the design reviewer approves surface and table changes.
34
+ - F13: a missing need is a reviewed extension of the surface, never a workaround or a private import.
35
+ - F17: name no application anywhere, in a tracked or an untracked file, or in a commit message.
36
+ - One source of truth for the surface: `docs/api-snapshot.txt`, regenerated with `just api-write`,
37
+ never edited by hand. A new surface name adds its door to `[tool.family].doors` and `_api.DOORS`
38
+ together, a docstring, a runnable guide block, a CHANGELOG line and its `docs/llms.txt` lines.
39
+ - The identical test set listed in `scripts/family_files.txt` is copied byte for byte between the
40
+ sibling repositories; `scripts/family_compare.py` checks it. Edit it in both or in neither.
41
+
42
+ ## Style
43
+
44
+ - `uv` only; no bare `python` or `pip`. Dependencies are exact pins (`name==X.Y.Z`).
45
+ - No em-dashes in any text file.
46
+ - Lean limits (F10): 15 statements, 12 branches, complexity 8, no nested `def`, 300 code lines per
47
+ module, docstrings of one summary line plus at most two of why, `# noqa: CODE - reason`.
48
+ - Stage named paths with git; never `git add -A`.
@@ -0,0 +1,209 @@
1
+ # elecdef: design
2
+
3
+ `elecdef` defines what electrical and electronic things are: a coil, a contact, a terminal, a
4
+ connection, a fuse, a power supply, a PLC channel, a conductor, a cable. Each is a block with
5
+ typed, exact, kind-checked values, its pin markings and its internal links. This file is the
6
+ current truth of the model; `RULES.md` is the rule set every change follows.
7
+
8
+ ## 1. Purpose
9
+
10
+ elecdef is for every electrical and electronic engineer working in Python: one typed, exact
11
+ definition of each basic thing in the electrical world, so that nobody types a coil, a contact or
12
+ a cable again. A parts library describes a real product by filling the blocks' values. An
13
+ application, a script or a notebook reads the values through one small typed surface. The
14
+ library judges nothing: it holds facts and looks them up. It has no runtime dependency and
15
+ runs with or without the rest of the family.
16
+
17
+ ## 2. The family
18
+
19
+ | Library | Owns | Never holds |
20
+ |---|---|---|
21
+ | `symdef` | how a thing is drawn: symbols, frames, ports, geometry | a rating, a unit, a marking's meaning |
22
+ | `elecdef` | what a thing is: blocks, values, markings, internal links | a coordinate, a page, a designation string, a drawing key's meaning |
23
+ | `refdef` | what a thing is called: designations, levels, letter codes, numbering, sort | a device fact, a marking's meaning |
24
+ | an application | how a project connects, lays out, checks and prints | a copy of any fact above |
25
+
26
+ A fact lives in exactly one library. A block names its symbol by a `Symbol(key, version)` and
27
+ reads nothing from `symdef`. A pin marking (`13`, `A1`, `95`) is an elecdef fact; the string
28
+ `-K1:13` is a refdef fact; which wire lands on it is the application's fact.
29
+
30
+ ## 3. Core model
31
+
32
+ ### 3.1 Values
33
+
34
+ A `Quantity` is a `Decimal` and a `Unit`. A `Unit` is an enum member carrying a symbol, a `Kind`
35
+ (voltage, current, power, apparent power, resistance, frequency, time, length, area, gauge,
36
+ torque, ratio, temperature, temperature difference) and an exact factor to the kind's base unit.
37
+ `U.V(24)`, `U.MM2("2.5")` is the one spelling; a `float` is refused. `==` compares as written,
38
+ `same()` and ordering compare in base units, `to(unit)` converts within a kind and refuses across
39
+ kinds. Percent is a unit of kind ratio. Degrees Celsius and kelvin are different kinds.
40
+
41
+ A `Range` is written the way a datasheet states it: a role and up to three ends.
42
+
43
+ ```python
44
+ Range(Role.RATED, nom=U.V(24), min=U.V("20.4"), max=U.V(30), at=At(Current.DC))
45
+ Range(Role.LIMITING, max=U.V(250))
46
+ Range(Role.REFERENCE, min=U.DEG_C(23), max=U.DEG_C(27))
47
+ ```
48
+
49
+ `role` is positional and required: `RATED` is what the maker states for the stated conditions,
50
+ `LIMITING` is an extreme the thing withstands, `REFERENCE` is the band other figures are stated
51
+ for. `nom` is the marked value; an optional `typ` holds a measured typical and is never confused
52
+ with it. `tolerance` is a property returning exact fractions derived from the ends; it is never
53
+ written. Every end has one kind. `contains(quantity)` and `covers(range)` are the two tests.
54
+
55
+ `At` is the set of conditions a figure holds under, one per kind: a `Quantity` (at 400 V), a
56
+ `Range` (up to 250 V), or an enum member (`Current.DC`, `Utilisation.AC_3`, `CoilState.HOLD`,
57
+ `ConductorKind.FINE`). A condition's kind is its unit's kind or its enum class. AC and DC are
58
+ conditions, not fields, so one `Ranges` field (a tuple of `Range`) holds the 50 Hz, the 60 Hz
59
+ and the DC figures side by side.
60
+
61
+ ### 3.2 Lookup
62
+
63
+ `pick(ranges, *ask) -> Pick(found, issue)` returns the single range whose conditions cover the
64
+ ask. A `Range` condition covers by inclusion, a `Quantity` by equality in base units, an enum by
65
+ identity. A row may state more kinds than the ask names; the ask may not name a kind the row
66
+ lacks. No covering row gives the issue `NO_RATING`; more than one gives `AMBIGUOUS_RATING`;
67
+ there is no silent first match. This is data access. Whether a load is acceptable against the
68
+ found range is the application's check, and elecdef stops at `found`.
69
+
70
+ ### 3.3 Blocks
71
+
72
+ A block is a frozen dataclass stating what one kind of thing is. Every property may be unfilled.
73
+ A property is a `Quantity` for a dimension or a count-like figure, a `Range` for an electrical
74
+ figure, `Ranges` when it varies with conditions, an enum for a closed vocabulary, `int` or `bool`
75
+ otherwise. Every quantity field declares its kind; a value of another kind is refused at
76
+ construction. A field that holds a closed set is an enum, never a string.
77
+
78
+ Blocks compose by typed fields, by value: `Relay.coil: Coil`, `Relay.contacts: tuple[Contact,
79
+ ...]`, `Coil.connection: Connection`. A leaf defined once is passed by reference into every block
80
+ that has it; equality is by value. `elecdef.connections` ships technology-only leaves (`SCREW`,
81
+ `PUSH_IN`, ...) with ranges unfilled; a parts library derives its vendor leaves from them with
82
+ `dataclasses.replace` and shares those. Family identity of a derived leaf is not tracked. A
83
+ `Connection` belongs to the pins a block owns itself: a block that owns pins directly has one
84
+ `connection` field (a terminal block has one per side in `sides`), and a block whose pins all
85
+ come from its sub-blocks has none. So `Coil`, `Contact`, `SupplyInput`, `ChannelGroup`, `Fuse`,
86
+ `FuseHolder`, `Breaker`, `Overload`, `PowerSupply` (its output pins), `Connector` and `Device`
87
+ carry one; `Relay`, `Contactor`, `ContactBlock`, `PlcCard` and `Computer` carry none.
88
+
89
+ There is one base class, `Block`, for mechanism only: it runs the kind check and normalises a
90
+ bare `Range` given to a `Ranges` field. An application never subclasses a block; `check` reports a
91
+ block whose class is not in the catalogue. A family template and a product are the same class:
92
+ the template holds ranges and gaps, the product holds values.
93
+
94
+ ### 3.4 Pins, markings and links
95
+
96
+ A `Pin` is `(marking, role, side, alternates)`; a `Link` is `(markings, kind, closed_at_rest)`, `markings` a pair, with
97
+ kind `CONDUCTIVE`, `SWITCHED` or `PROTECTIVE`. Markings follow the block: `elecdef.pins` derives
98
+ them by form, index and `Marking` scheme (`AUX` n3-n4, n1-n2, n1-n2-n4; `MAIN` 1/L1-2/T1 ...;
99
+ `OVERLOAD` 95-96, 97-98; `TIMER` n5-n6-n8; `PLAIN` 1-8) with alternates as data. A composite
100
+ numbers its unnumbered sub-blocks on every read: a relay's contacts 1..n, a contactor's main
101
+ poles then its auxiliaries, a contact block from `numbered_from`, a card's channels across its
102
+ groups. A product may give pins outright; a coil or contact stores only those and computes the rest, so `replace` never leaves a stale marking. A composite's `pins` and `links` are computed over its
103
+ sub-blocks and stored nowhere else. A marking is a non-empty string of letters, digits and
104
+ `/ + - . '`, so every marking reads back unchanged in a designation string such as `-K1:13`.
105
+
106
+ ### 3.5 Product
107
+
108
+ `Product(mpn, manufacturer, block, name)` is the one identity envelope: a block with its values
109
+ filled, under a maker's part number. An application wraps it in its own record (suppliers,
110
+ footprint, class letter) and never copies a block value out of it.
111
+
112
+ ### 3.6 Issues
113
+
114
+ `check(block | product) -> tuple[Issue, ...]` reports content problems as values: an end below
115
+ another, a marking that repeats, a marking outside the character set, a link naming no pin, a
116
+ block kind missing a figure it needs, a class outside the catalogue. An `Issue` is `(code:
117
+ IssueCode, message, subject)`. `IssueCode` has eight members: the six that `check` reports and the
118
+ two that `pick` returns (`NO_RATING`, `AMBIGUOUS_RATING`). A malformed call (a wrong type, a wrong
119
+ kind, two conditions of one kind in an `At`) raises `TypeError`; content never does.
120
+
121
+ ## 4. v1 catalogue
122
+
123
+ | Block | Reuses | States |
124
+ |---|---|---|
125
+ | `Body` | - | width, height, depth; ip; temperature (rated, storage); mounting |
126
+ | `Connection` | - | technology; conductor area per conductor kind; AWG as stated; conductors per clamp; strip length; torque; screw; pitch; material |
127
+ | `Rating` | - | operational voltage per current kind; insulation and impulse voltage; thermal current; frequency; overvoltage category; pollution degree |
128
+ | `SupplyInput` | Connection | what a powered thing draws: voltage and current per conditions; its supply pins |
129
+ | `Contact` | Connection | form (make, break, changeover) and features; switching current per voltage, current kind and utilisation code; limiting voltage; minimum load; material; life |
130
+ | `Coil` | Connection | kind; voltage per current kind and frequency; pick-up and drop-out as ratio; resistance; power and apparent power per coil state; times; suppression; A1-A2 |
131
+ | `Conductor` | - | material; class; area; insulation; temperature; colour; voltages |
132
+ | `Cable` | Conductor | cores (number, colour, pair, drain); screen; armour; diameter; bend radii; sheath; temperature; flags; opaque type code with scheme |
133
+ | `TerminalBlock` | Connection per side, Rating, Body | kind; levels with marking and potential; sides; cross-section; width; rail |
134
+ | `Relay` | Coil, Contact, Body | kind; contacts; latching; force-guided; timer function |
135
+ | `Contactor` | Coil, Contact, Rating, Body | main poles; auxiliaries (a mirror contact is an auxiliary with that feature) |
136
+ | `ContactBlock` | Contact, Body | a coil-less carrier: snap-on auxiliary block, button block, limit switch; actuator; first number |
137
+ | `Overload` | Contact, Connection, Body | poles; setting; trip class; reset; phase failure; auxiliaries |
138
+ | `Fuse`, `FuseHolder` | Connection, Rating, Body | system; class; current; voltage and breaking capacity per current kind; speed; indicator; poles; disconnect |
139
+ | `Breaker` | Connection, Rating, Contact, Body | poles; current; curve; breaking capacities; auxiliaries |
140
+ | `PowerSupply` | SupplyInput, Contact, Connection, Body | kind; input; output voltage and current; boost; hold-up; overload behaviour; circuit class; isolation; redundancy; efficiency; OK contact (`ok_contact`) |
141
+ | `Channel`, `ChannelGroup` | Connection | signal and number; digital input, digital output, analog or relay data; a group's markings, commons, isolation and connection, written once for many channels |
142
+ | `PlcCard` | SupplyInput, ChannelGroup, Body | form; role; bus; groups; safety levels |
143
+ | `Connector` | Rating, Connection, Body | family; coding; poles; gender; locking |
144
+ | `Computer` | SupplyInput, Connector, Body | supply; power; ports |
145
+ | `Device` | Rating, Connection, Body | pins and links given outright: the typed fallback for a thing with no block yet |
146
+
147
+ Vocabularies ship as enums in `elecdef.vocab`: industry codes are the member names
148
+ (`Utilisation.AC_3`, `FuseClass.gG`, `TripCurve.C`) with short descriptions in our own words.
149
+ Data tables ship as generated Python in `elecdef.tables`: the cross-section series, the AWG
150
+ formula, colour letter codes, the 44-core colour sequence, breaker curve bands, trip classes,
151
+ cable voltage digits, overvoltage categories, the E series. A table row ships only when its
152
+ value is confirmed; a contested row waits.
153
+
154
+ ## 5. Public surface
155
+
156
+ Top level (`elecdef.__all__`): `Unit`, `Quantity`, `Range`, `Ranges`, `Role`, `At`, `pick`,
157
+ `Pick`, `Issue`, `IssueCode`, `check`, `Symbol`, `Product`, `Body`, `Rating`, `Connection`,
158
+ `Contact`, `Coil`, `Relay`, `Contactor`, `ContactBlock`, `Overload`, `TerminalBlock`, `Fuse`,
159
+ `Breaker`, `PowerSupply`, `PlcCard`, `Device`. Twelve doors, each with its own `__all__` of the
160
+ names not exported at top level: `elecdef.vocab` (`Kind` and every vocabulary enum),
161
+ `elecdef.pins` (`Pin`, `PinRole`, `Side`, `Link`, `LinkKind`, `Marking` and the marking
162
+ functions), `elecdef.connections` (`SCREW`, `SPRING_CAGE`, `PUSH_IN`, `TENSION`, `IDC`, `CRIMP`,
163
+ `SOLDER`, `PRESS_IN`, `PLUG_IN`, `BOLT`, `WIRE_WRAP`, `THT`, `SMD`), `elecdef.tables`,
164
+ `elecdef.supply` (`SupplyInput`), `elecdef.fuse` (`FuseHolder`), `elecdef.plc` (`Channel`,
165
+ `ChannelGroup`, `DigitalInput`, `DigitalOutput`, `AnalogIo`), `elecdef.terminal` (`Level`),
166
+ `elecdef.conductor` (`Conductor`), `elecdef.cable` (`Cable`, `Core`, `TypeCode`),
167
+ `elecdef.connector` (`Connector`), `elecdef.computer` (`Computer`, `Port`). The examples in this file assume `from elecdef import
168
+ Unit as U`. `python -m elecdef api` prints every surface name with its signature and docstring;
169
+ `docs/api-snapshot.txt` is that output, and a test fails when they differ.
170
+
171
+ ## 6. Out of scope
172
+
173
+ Checks and judgements (is this load acceptable, is this wire big enough), derating and
174
+ temperature factors, conductor resistance, cable type-code decoding (the `Scheme` enum and the
175
+ opaque `TypeCode` leave the slot), calculations across blocks, instances and topology, where a
176
+ pin is wired, drawing, designations and PLC tags, file reading or writing, multilingual names,
177
+ assemblies of several products. Pilot devices, sensors, motors, passives and semiconductors
178
+ arrive as blocks after v1; until then they are a `Device`.
179
+
180
+ ## 7. Docs and agents
181
+
182
+ The repository publishes a GitHub Pages site built with Zensical (as `symdef` does) from the first
183
+ public release. The site holds this design, the rules, a guide with one runnable example per
184
+ surface name, and an API reference generated from the docstrings, so a name documented in code
185
+ is documented on the site by construction. The strict Zensical docs build runs in CI.
186
+
187
+ `llms.txt` at the site root lists, in plain text, what the library is, the surface names, the
188
+ guide pages and the rules, with one link each. `AGENTS.md` at the repository root tells an
189
+ agent: read `DESIGN.md` then `RULES.md`; read the surface with `python -m elecdef api` and never
190
+ the source to use the library; every public name has an example under `docs/guide/`; the tests
191
+ named in `RULES.md` are the definition of done; a change that touches the surface, the design or
192
+ the rules needs a design review before it is written.
193
+
194
+ ## 8. How it changes
195
+
196
+ Semantic versioning. Every surface change carries a changelog line. A pull request that edits
197
+ `DESIGN.md` or `RULES.md`, changes `docs/api-snapshot.txt` or changes a shipped table under
198
+ `src/elecdef/tables/`, carries a `Design-review:` trailer naming an approved review; a guard
199
+ script fails it otherwise. An application's missing need is never met by a workaround in the
200
+ application or a private path into the library: it is a reviewed design change that extends the
201
+ surface. An application that adopts elecdef shrinks: it deletes its copy of the fact and reads
202
+ the surface.
203
+
204
+ Who approves is family rule F12: the maintainer approves the contract, and the design reviewer approves surface and table changes.
205
+
206
+ ## 9. Licence
207
+
208
+ MIT, covering the code and the shipped tables. An open source used for a table is credited in
209
+ the README and is never share-alike or non-commercial.
elecdef-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ole Johan Bondahl
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
elecdef-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.5
2
+ Name: elecdef
3
+ Version: 0.1.0
4
+ Summary: Standard-agnostic electrical engineering vocabulary as strict, typed, immutable Python values
5
+ Project-URL: Repository, https://github.com/OleJBondahl/elecdef
6
+ Author: Ole Johan Bondahl
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.14
10
+ Description-Content-Type: text/markdown
11
+
12
+ # elecdef
13
+
14
+ Standard-agnostic electrical engineering vocabulary as strict, typed, immutable Python values.
15
+ The library has no runtime dependencies and does no file, network, clock or random access.
16
+ Every value is a frozen dataclass or an enum.
17
+
18
+ Status: pre-release, version 0.1.0. The package is an empty scaffold.
19
+
20
+ ## Install
21
+
22
+ ```text
23
+ uv add elecdef
24
+ ```
25
+
26
+ Compatible with IEC conventions.
27
+
28
+ Documentation: <https://olejbondahl.github.io/elecdef/>
29
+
30
+ Licence: MIT.
31
+
32
+ <!-- credit:start -->
33
+ Credits: none yet.
34
+ <!-- credit:end -->
@@ -0,0 +1,23 @@
1
+ # elecdef
2
+
3
+ Standard-agnostic electrical engineering vocabulary as strict, typed, immutable Python values.
4
+ The library has no runtime dependencies and does no file, network, clock or random access.
5
+ Every value is a frozen dataclass or an enum.
6
+
7
+ Status: pre-release, version 0.1.0. The package is an empty scaffold.
8
+
9
+ ## Install
10
+
11
+ ```text
12
+ uv add elecdef
13
+ ```
14
+
15
+ Compatible with IEC conventions.
16
+
17
+ Documentation: <https://olejbondahl.github.io/elecdef/>
18
+
19
+ Licence: MIT.
20
+
21
+ <!-- credit:start -->
22
+ Credits: none yet.
23
+ <!-- credit:end -->