symdef 0.4.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 (288) hide show
  1. symdef-0.4.0/.gitattributes +1 -0
  2. symdef-0.4.0/.github/workflows/docs.yml +33 -0
  3. symdef-0.4.0/.github/workflows/publish.yml +37 -0
  4. symdef-0.4.0/.gitignore +8 -0
  5. symdef-0.4.0/.python-version +1 -0
  6. symdef-0.4.0/AGENTS.md +12 -0
  7. symdef-0.4.0/CLAUDE.md +62 -0
  8. symdef-0.4.0/LICENSE +21 -0
  9. symdef-0.4.0/PKG-INFO +114 -0
  10. symdef-0.4.0/README.md +102 -0
  11. symdef-0.4.0/docs/DECISIONS.md +814 -0
  12. symdef-0.4.0/docs/GUIDE.md +215 -0
  13. symdef-0.4.0/justfile +39 -0
  14. symdef-0.4.0/pyproject.toml +115 -0
  15. symdef-0.4.0/schema/symbol.schema.json +247 -0
  16. symdef-0.4.0/scripts/build.py +41 -0
  17. symdef-0.4.0/scripts/build_site.py +69 -0
  18. symdef-0.4.0/scripts/fp_purity_gate.py +66 -0
  19. symdef-0.4.0/src/symdef/__init__.py +97 -0
  20. symdef-0.4.0/src/symdef/_version.py +3 -0
  21. symdef-0.4.0/src/symdef/boxes.py +112 -0
  22. symdef-0.4.0/src/symdef/build.py +207 -0
  23. symdef-0.4.0/src/symdef/docs/SYMBOL_INTERFACE.html +573 -0
  24. symdef-0.4.0/src/symdef/errors.py +23 -0
  25. symdef-0.4.0/src/symdef/gallery.py +85 -0
  26. symdef-0.4.0/src/symdef/geometry.py +209 -0
  27. symdef-0.4.0/src/symdef/lint/__init__.py +141 -0
  28. symdef-0.4.0/src/symdef/lint/anchors.py +40 -0
  29. symdef-0.4.0/src/symdef/lint/connectivity.py +186 -0
  30. symdef-0.4.0/src/symdef/lint/exemptions.py +56 -0
  31. symdef-0.4.0/src/symdef/lint/file.py +124 -0
  32. symdef-0.4.0/src/symdef/lint/geometry.py +269 -0
  33. symdef-0.4.0/src/symdef/lint/overlap.py +274 -0
  34. symdef-0.4.0/src/symdef/lint/ports.py +253 -0
  35. symdef-0.4.0/src/symdef/lint/registry.py +172 -0
  36. symdef-0.4.0/src/symdef/lint/slots.py +136 -0
  37. symdef-0.4.0/src/symdef/load.py +621 -0
  38. symdef-0.4.0/src/symdef/model.py +248 -0
  39. symdef-0.4.0/src/symdef/orient.py +151 -0
  40. symdef-0.4.0/src/symdef/py.typed +0 -0
  41. symdef-0.4.0/src/symdef/repeat.py +99 -0
  42. symdef-0.4.0/src/symdef/resolve.py +586 -0
  43. symdef-0.4.0/src/symdef/serialize.py +239 -0
  44. symdef-0.4.0/src/symdef/svg.py +505 -0
  45. symdef-0.4.0/src/symdef/units.py +18 -0
  46. symdef-0.4.0/tests/build_symbol.py +19 -0
  47. symdef-0.4.0/tests/damage.py +63 -0
  48. symdef-0.4.0/tests/fixture_pipeline.py +62 -0
  49. symdef-0.4.0/tests/fixtures/broken/allow-unknown/symbols/S00001.toml +13 -0
  50. symdef-0.4.0/tests/fixtures/broken/allow-unknown/symbols/S00002.toml +13 -0
  51. symdef-0.4.0/tests/fixtures/broken/allow-unused/symbols/S00001.toml +13 -0
  52. symdef-0.4.0/tests/fixtures/broken/anchor-duplicate-id/symbols/S00001.toml +14 -0
  53. symdef-0.4.0/tests/fixtures/broken/anchor-off-geometry/symbols/S00001.toml +13 -0
  54. symdef-0.4.0/tests/fixtures/broken/degenerate/symbols/S00001.toml +14 -0
  55. symdef-0.4.0/tests/fixtures/broken/degenerate/symbols/S00002.toml +12 -0
  56. symdef-0.4.0/tests/fixtures/broken/export-unknown/symbols/S00001.toml +11 -0
  57. symdef-0.4.0/tests/fixtures/broken/export-unknown/symbols/S00010.toml +17 -0
  58. symdef-0.4.0/tests/fixtures/broken/id-format/symbols/S00001.toml +9 -0
  59. symdef-0.4.0/tests/fixtures/broken/id-format/symbols/S00002.toml +18 -0
  60. symdef-0.4.0/tests/fixtures/broken/id-format/symbols/S00010.toml +13 -0
  61. symdef-0.4.0/tests/fixtures/broken/lead-off-port/symbols/S00001.toml +19 -0
  62. symdef-0.4.0/tests/fixtures/broken/lead-off-port/symbols/S00002.toml +19 -0
  63. symdef-0.4.0/tests/fixtures/broken/lead-off-port/symbols/S00003.toml +20 -0
  64. symdef-0.4.0/tests/fixtures/broken/library.toml +3 -0
  65. symdef-0.4.0/tests/fixtures/broken/metadata/symbols/S00001.toml +9 -0
  66. symdef-0.4.0/tests/fixtures/broken/node-invalid/symbols/S00001.toml +21 -0
  67. symdef-0.4.0/tests/fixtures/broken/off-drawing-grid/symbols/S00001.toml +9 -0
  68. symdef-0.4.0/tests/fixtures/broken/off-drawing-grid/symbols/S00002.toml +13 -0
  69. symdef-0.4.0/tests/fixtures/broken/off-drawing-grid/symbols/S00003.toml +12 -0
  70. symdef-0.4.0/tests/fixtures/broken/off-drawing-grid/symbols/S00004.toml +12 -0
  71. symdef-0.4.0/tests/fixtures/broken/part-anchor/symbols/S00001.toml +10 -0
  72. symdef-0.4.0/tests/fixtures/broken/part-anchor/symbols/S00010.toml +13 -0
  73. symdef-0.4.0/tests/fixtures/broken/part-cycle/symbols/S00001.toml +9 -0
  74. symdef-0.4.0/tests/fixtures/broken/part-cycle/symbols/S00010.toml +9 -0
  75. symdef-0.4.0/tests/fixtures/broken/part-port-unexported/symbols/S00001.toml +11 -0
  76. symdef-0.4.0/tests/fixtures/broken/part-port-unexported/symbols/S00010.toml +18 -0
  77. symdef-0.4.0/tests/fixtures/broken/part-unknown/symbols/S00001.toml +9 -0
  78. symdef-0.4.0/tests/fixtures/broken/path-invalid/symbols/S00001.toml +20 -0
  79. symdef-0.4.0/tests/fixtures/broken/path-invalid/symbols/S00002.toml +23 -0
  80. symdef-0.4.0/tests/fixtures/broken/path-invalid/symbols/S00003.toml +20 -0
  81. symdef-0.4.0/tests/fixtures/broken/pitch-overflow/symbols/S00001.toml +27 -0
  82. symdef-0.4.0/tests/fixtures/broken/pitch-overflow/symbols/S00002.toml +26 -0
  83. symdef-0.4.0/tests/fixtures/broken/pitch-overflow/symbols/S00003.toml +26 -0
  84. symdef-0.4.0/tests/fixtures/broken/port-duplicate-id/symbols/S00001.toml +19 -0
  85. symdef-0.4.0/tests/fixtures/broken/port-isolated/symbols/S00001.toml +20 -0
  86. symdef-0.4.0/tests/fixtures/broken/port-lane-clear/symbols/S00001.toml +25 -0
  87. symdef-0.4.0/tests/fixtures/broken/port-lane-clear/symbols/S00002.toml +25 -0
  88. symdef-0.4.0/tests/fixtures/broken/port-off-geometry/symbols/S00001.toml +17 -0
  89. symdef-0.4.0/tests/fixtures/broken/port-off-wiring-grid/symbols/S00001.toml +17 -0
  90. symdef-0.4.0/tests/fixtures/broken/port-on-body-edge/symbols/S00001.toml +20 -0
  91. symdef-0.4.0/tests/fixtures/broken/port-on-body-edge/symbols/S00002.toml +21 -0
  92. symdef-0.4.0/tests/fixtures/broken/port-position-shared/symbols/S00001.toml +18 -0
  93. symdef-0.4.0/tests/fixtures/broken/port-spacing/symbols/S00001.toml +19 -0
  94. symdef-0.4.0/tests/fixtures/broken/schema/symbols/S00001.toml +9 -0
  95. symdef-0.4.0/tests/fixtures/broken/slot-missing/symbols/S00001.toml +24 -0
  96. symdef-0.4.0/tests/fixtures/broken/slot-missing/symbols/S00002.toml +24 -0
  97. symdef-0.4.0/tests/fixtures/broken/slot-overlap-body/symbols/S00001.toml +25 -0
  98. symdef-0.4.0/tests/fixtures/broken/slot-overlap-body/symbols/S00002.toml +26 -0
  99. symdef-0.4.0/tests/fixtures/broken/slot-overlap-slot/symbols/S00001.toml +23 -0
  100. symdef-0.4.0/tests/fixtures/broken/slot-overlap-slot/symbols/S00002.toml +26 -0
  101. symdef-0.4.0/tests/fixtures/broken/slot-unknown-port/symbols/S00001.toml +23 -0
  102. symdef-0.4.0/tests/fixtures/broken/text-too-large/symbols/S00001.toml +9 -0
  103. symdef-0.4.0/tests/fixtures/broken/through-axis/symbols/S00001.toml +19 -0
  104. symdef-0.4.0/tests/fixtures/broken/through-axis/symbols/S00002.toml +19 -0
  105. symdef-0.4.0/tests/fixtures/broken/through-axis/symbols/S00003.toml +19 -0
  106. symdef-0.4.0/tests/fixtures/broken/through-count/symbols/S00001.toml +29 -0
  107. symdef-0.4.0/tests/fixtures/broken/through-missing/symbols/S00001.toml +25 -0
  108. symdef-0.4.0/tests/fixtures/expected/S00227.json +159 -0
  109. symdef-0.4.0/tests/fixtures/expected/bundle.json +271 -0
  110. symdef-0.4.0/tests/fixtures/guide/library.toml +3 -0
  111. symdef-0.4.0/tests/fixtures/guide/symbols/S00016.toml +28 -0
  112. symdef-0.4.0/tests/fixtures/guide/symbols/S00171.toml +16 -0
  113. symdef-0.4.0/tests/fixtures/guide/symbols/S00227.toml +29 -0
  114. symdef-0.4.0/tests/fixtures/guide/symbols/S00230.toml +35 -0
  115. symdef-0.4.0/tests/fixtures/guide/symbols/S00254.toml +17 -0
  116. symdef-0.4.0/tests/fixtures/guide/symbols/S00305.toml +29 -0
  117. symdef-0.4.0/tests/fixtures/schema/invalid/anchor_at_missing.toml +18 -0
  118. symdef-0.4.0/tests/fixtures/schema/invalid/anchor_dir_unknown.toml +18 -0
  119. symdef-0.4.0/tests/fixtures/schema/invalid/arc_angle_above_limit.toml +10 -0
  120. symdef-0.4.0/tests/fixtures/schema/invalid/arc_end_missing.toml +17 -0
  121. symdef-0.4.0/tests/fixtures/schema/invalid/arc_start_string.toml +17 -0
  122. symdef-0.4.0/tests/fixtures/schema/invalid/body_box_wrong_shape.toml +18 -0
  123. symdef-0.4.0/tests/fixtures/schema/invalid/circle_centre_wrong_length.toml +17 -0
  124. symdef-0.4.0/tests/fixtures/schema/invalid/circle_radius_missing.toml +17 -0
  125. symdef-0.4.0/tests/fixtures/schema/invalid/circle_radius_string.toml +17 -0
  126. symdef-0.4.0/tests/fixtures/schema/invalid/composite_ports_string.toml +17 -0
  127. symdef-0.4.0/tests/fixtures/schema/invalid/composite_symbol_without_slots.toml +14 -0
  128. symdef-0.4.0/tests/fixtures/schema/invalid/element_closed_on_circle.toml +17 -0
  129. symdef-0.4.0/tests/fixtures/schema/invalid/element_closed_on_line.toml +17 -0
  130. symdef-0.4.0/tests/fixtures/schema/invalid/element_empty.toml +17 -0
  131. symdef-0.4.0/tests/fixtures/schema/invalid/element_fill_on_arc.toml +17 -0
  132. symdef-0.4.0/tests/fixtures/schema/invalid/element_fill_on_line.toml +17 -0
  133. symdef-0.4.0/tests/fixtures/schema/invalid/element_line_port_not_string.toml +10 -0
  134. symdef-0.4.0/tests/fixtures/schema/invalid/element_no_shape.toml +17 -0
  135. symdef-0.4.0/tests/fixtures/schema/invalid/element_not_table.toml +17 -0
  136. symdef-0.4.0/tests/fixtures/schema/invalid/element_radius_on_line.toml +17 -0
  137. symdef-0.4.0/tests/fixtures/schema/invalid/element_style_on_circle.toml +17 -0
  138. symdef-0.4.0/tests/fixtures/schema/invalid/element_style_on_text.toml +17 -0
  139. symdef-0.4.0/tests/fixtures/schema/invalid/element_style_unknown.toml +17 -0
  140. symdef-0.4.0/tests/fixtures/schema/invalid/element_two_shapes.toml +17 -0
  141. symdef-0.4.0/tests/fixtures/schema/invalid/element_weight_unknown.toml +17 -0
  142. symdef-0.4.0/tests/fixtures/schema/invalid/elements_not_array.toml +15 -0
  143. symdef-0.4.0/tests/fixtures/schema/invalid/integer_400_digits_as_number.toml +10 -0
  144. symdef-0.4.0/tests/fixtures/schema/invalid/keepout_box_bad_point.toml +18 -0
  145. symdef-0.4.0/tests/fixtures/schema/invalid/kind_unknown.toml +17 -0
  146. symdef-0.4.0/tests/fixtures/schema/invalid/kind_wrong_case.toml +17 -0
  147. symdef-0.4.0/tests/fixtures/schema/invalid/line_not_array.toml +17 -0
  148. symdef-0.4.0/tests/fixtures/schema/invalid/line_one_point.toml +17 -0
  149. symdef-0.4.0/tests/fixtures/schema/invalid/line_three_points.toml +17 -0
  150. symdef-0.4.0/tests/fixtures/schema/invalid/lint_allow_not_array.toml +18 -0
  151. symdef-0.4.0/tests/fixtures/schema/invalid/lint_allow_reason_missing.toml +18 -0
  152. symdef-0.4.0/tests/fixtures/schema/invalid/lint_allow_rule_not_string.toml +18 -0
  153. symdef-0.4.0/tests/fixtures/schema/invalid/name_is_a_date.toml +17 -0
  154. symdef-0.4.0/tests/fixtures/schema/invalid/name_missing.toml +16 -0
  155. symdef-0.4.0/tests/fixtures/schema/invalid/name_not_string.toml +17 -0
  156. symdef-0.4.0/tests/fixtures/schema/invalid/negative_integer_400_digits_as_number.toml +10 -0
  157. symdef-0.4.0/tests/fixtures/schema/invalid/neither_elements_nor_parts.toml +14 -0
  158. symdef-0.4.0/tests/fixtures/schema/invalid/node_port_not_string.toml +18 -0
  159. symdef-0.4.0/tests/fixtures/schema/invalid/node_ports_missing.toml +18 -0
  160. symdef-0.4.0/tests/fixtures/schema/invalid/node_ports_not_array.toml +18 -0
  161. symdef-0.4.0/tests/fixtures/schema/invalid/node_potential_unknown.toml +18 -0
  162. symdef-0.4.0/tests/fixtures/schema/invalid/number_1e7.toml +10 -0
  163. symdef-0.4.0/tests/fixtures/schema/invalid/number_above_limit.toml +10 -0
  164. symdef-0.4.0/tests/fixtures/schema/invalid/number_below_negative_limit.toml +10 -0
  165. symdef-0.4.0/tests/fixtures/schema/invalid/number_inf.toml +10 -0
  166. symdef-0.4.0/tests/fixtures/schema/invalid/number_nan.toml +10 -0
  167. symdef-0.4.0/tests/fixtures/schema/invalid/number_negative_inf.toml +10 -0
  168. symdef-0.4.0/tests/fixtures/schema/invalid/number_negative_nan.toml +10 -0
  169. symdef-0.4.0/tests/fixtures/schema/invalid/part_as_missing.toml +17 -0
  170. symdef-0.4.0/tests/fixtures/schema/invalid/part_at_400_digits.toml +10 -0
  171. symdef-0.4.0/tests/fixtures/schema/invalid/part_at_wrong_length.toml +17 -0
  172. symdef-0.4.0/tests/fixtures/schema/invalid/part_length_above_limit.toml +10 -0
  173. symdef-0.4.0/tests/fixtures/schema/invalid/part_length_string.toml +17 -0
  174. symdef-0.4.0/tests/fixtures/schema/invalid/part_orient_lowercase.toml +17 -0
  175. symdef-0.4.0/tests/fixtures/schema/invalid/part_orient_unknown.toml +17 -0
  176. symdef-0.4.0/tests/fixtures/schema/invalid/part_repeat_above_limit.toml +10 -0
  177. symdef-0.4.0/tests/fixtures/schema/invalid/part_repeat_fraction.toml +17 -0
  178. symdef-0.4.0/tests/fixtures/schema/invalid/part_unknown_key.toml +17 -0
  179. symdef-0.4.0/tests/fixtures/schema/invalid/part_use_missing.toml +17 -0
  180. symdef-0.4.0/tests/fixtures/schema/invalid/part_via_unknown.toml +17 -0
  181. symdef-0.4.0/tests/fixtures/schema/invalid/parts_not_array.toml +14 -0
  182. symdef-0.4.0/tests/fixtures/schema/invalid/path_from_not_string.toml +18 -0
  183. symdef-0.4.0/tests/fixtures/schema/invalid/path_kind_unknown.toml +18 -0
  184. symdef-0.4.0/tests/fixtures/schema/invalid/path_through_not_boolean.toml +18 -0
  185. symdef-0.4.0/tests/fixtures/schema/invalid/path_to_missing.toml +18 -0
  186. symdef-0.4.0/tests/fixtures/schema/invalid/point_boolean_coordinate.toml +17 -0
  187. symdef-0.4.0/tests/fixtures/schema/invalid/point_coordinate_inf.toml +10 -0
  188. symdef-0.4.0/tests/fixtures/schema/invalid/point_one_coordinate.toml +17 -0
  189. symdef-0.4.0/tests/fixtures/schema/invalid/point_string_coordinate.toml +17 -0
  190. symdef-0.4.0/tests/fixtures/schema/invalid/point_three_coordinates.toml +17 -0
  191. symdef-0.4.0/tests/fixtures/schema/invalid/pole_pitch_400_digits.toml +11 -0
  192. symdef-0.4.0/tests/fixtures/schema/invalid/pole_pitch_above_limit.toml +11 -0
  193. symdef-0.4.0/tests/fixtures/schema/invalid/pole_pitch_boolean.toml +18 -0
  194. symdef-0.4.0/tests/fixtures/schema/invalid/pole_pitch_fraction.toml +18 -0
  195. symdef-0.4.0/tests/fixtures/schema/invalid/pole_pitch_string.toml +18 -0
  196. symdef-0.4.0/tests/fixtures/schema/invalid/polyline_closed_not_boolean.toml +17 -0
  197. symdef-0.4.0/tests/fixtures/schema/invalid/polyline_fill_unknown.toml +17 -0
  198. symdef-0.4.0/tests/fixtures/schema/invalid/polyline_point_malformed.toml +17 -0
  199. symdef-0.4.0/tests/fixtures/schema/invalid/port_at_boolean.toml +17 -0
  200. symdef-0.4.0/tests/fixtures/schema/invalid/port_at_missing.toml +17 -0
  201. symdef-0.4.0/tests/fixtures/schema/invalid/port_description_not_string.toml +17 -0
  202. symdef-0.4.0/tests/fixtures/schema/invalid/port_dir_lowercase.toml +17 -0
  203. symdef-0.4.0/tests/fixtures/schema/invalid/port_dir_missing.toml +17 -0
  204. symdef-0.4.0/tests/fixtures/schema/invalid/port_dir_unknown.toml +17 -0
  205. symdef-0.4.0/tests/fixtures/schema/invalid/port_id_not_string.toml +17 -0
  206. symdef-0.4.0/tests/fixtures/schema/invalid/port_unknown_key.toml +17 -0
  207. symdef-0.4.0/tests/fixtures/schema/invalid/ports_array_of_strings.toml +14 -0
  208. symdef-0.4.0/tests/fixtures/schema/invalid/ports_map_value_not_string.toml +17 -0
  209. symdef-0.4.0/tests/fixtures/schema/invalid/ports_string.toml +14 -0
  210. symdef-0.4.0/tests/fixtures/schema/invalid/reference_not_table.toml +17 -0
  211. symdef-0.4.0/tests/fixtures/schema/invalid/reference_number_missing.toml +17 -0
  212. symdef-0.4.0/tests/fixtures/schema/invalid/reference_number_not_string.toml +17 -0
  213. symdef-0.4.0/tests/fixtures/schema/invalid/reference_unknown_key.toml +17 -0
  214. symdef-0.4.0/tests/fixtures/schema/invalid/schema_boolean.toml +17 -0
  215. symdef-0.4.0/tests/fixtures/schema/invalid/schema_missing.toml +16 -0
  216. symdef-0.4.0/tests/fixtures/schema/invalid/schema_string.toml +17 -0
  217. symdef-0.4.0/tests/fixtures/schema/invalid/schema_wrong_number.toml +17 -0
  218. symdef-0.4.0/tests/fixtures/schema/invalid/slot_at_missing.toml +17 -0
  219. symdef-0.4.0/tests/fixtures/schema/invalid/slot_box_missing.toml +17 -0
  220. symdef-0.4.0/tests/fixtures/schema/invalid/slot_box_nan.toml +13 -0
  221. symdef-0.4.0/tests/fixtures/schema/invalid/slot_box_not_numbers.toml +17 -0
  222. symdef-0.4.0/tests/fixtures/schema/invalid/slot_box_three_numbers.toml +17 -0
  223. symdef-0.4.0/tests/fixtures/schema/invalid/slot_side_unknown.toml +17 -0
  224. symdef-0.4.0/tests/fixtures/schema/invalid/slot_unknown_key.toml +17 -0
  225. symdef-0.4.0/tests/fixtures/schema/invalid/slot_value_integer.toml +17 -0
  226. symdef-0.4.0/tests/fixtures/schema/invalid/slots_not_table.toml +15 -0
  227. symdef-0.4.0/tests/fixtures/schema/invalid/status_unknown.toml +17 -0
  228. symdef-0.4.0/tests/fixtures/schema/invalid/symbol_without_slots.toml +16 -0
  229. symdef-0.4.0/tests/fixtures/schema/invalid/text_content_not_string.toml +17 -0
  230. symdef-0.4.0/tests/fixtures/schema/invalid/text_height_missing.toml +17 -0
  231. symdef-0.4.0/tests/fixtures/schema/invalid/text_position_missing.toml +17 -0
  232. symdef-0.4.0/tests/fixtures/schema/invalid/top_level_unknown_key.toml +18 -0
  233. symdef-0.4.0/tests/fixtures/schema/valid/atomic_all_keys.toml +59 -0
  234. symdef-0.4.0/tests/fixtures/schema/valid/atomic_minimal.toml +16 -0
  235. symdef-0.4.0/tests/fixtures/schema/valid/composite_empty_rename_map.toml +9 -0
  236. symdef-0.4.0/tests/fixtures/schema/valid/composite_parts.toml +19 -0
  237. symdef-0.4.0/tests/fixtures/schema/valid/composite_with_elements.toml +19 -0
  238. symdef-0.4.0/tests/fixtures/schema/valid/element_kind.toml +9 -0
  239. symdef-0.4.0/tests/fixtures/schema/valid/element_line_with_port.toml +9 -0
  240. symdef-0.4.0/tests/fixtures/schema/valid/empty_polyline.toml +9 -0
  241. symdef-0.4.0/tests/fixtures/schema/valid/empty_tables.toml +15 -0
  242. symdef-0.4.0/tests/fixtures/schema/valid/integral_floats.toml +12 -0
  243. symdef-0.4.0/tests/fixtures/schema/valid/lint_matters_are_not_schema_matters.toml +22 -0
  244. symdef-0.4.0/tests/fixtures/schema/valid/numbers_at_the_limit.toml +13 -0
  245. symdef-0.4.0/tests/fixtures/schema/valid/qualifier_without_slots.toml +9 -0
  246. symdef-0.4.0/tests/fixtures/schema/valid/resolved_form.toml +33 -0
  247. symdef-0.4.0/tests/fixtures/schema/valid/symbol_with_empty_slots.toml +16 -0
  248. symdef-0.4.0/tests/fixtures/schema/valid/symbol_with_slots_but_no_tag.toml +17 -0
  249. symdef-0.4.0/tests/orientations.py +10 -0
  250. symdef-0.4.0/tests/roundtrip.py +25 -0
  251. symdef-0.4.0/tests/test_boxes.py +190 -0
  252. symdef-0.4.0/tests/test_broken_fixtures.py +160 -0
  253. symdef-0.4.0/tests/test_build.py +375 -0
  254. symdef-0.4.0/tests/test_build_load.py +167 -0
  255. symdef-0.4.0/tests/test_bundle.py +269 -0
  256. symdef-0.4.0/tests/test_gallery.py +114 -0
  257. symdef-0.4.0/tests/test_geometry.py +132 -0
  258. symdef-0.4.0/tests/test_guide_fixtures.py +149 -0
  259. symdef-0.4.0/tests/test_guide_lint.py +115 -0
  260. symdef-0.4.0/tests/test_guide_package_data.py +22 -0
  261. symdef-0.4.0/tests/test_lint_anchors.py +115 -0
  262. symdef-0.4.0/tests/test_lint_connectivity.py +493 -0
  263. symdef-0.4.0/tests/test_lint_driver.py +459 -0
  264. symdef-0.4.0/tests/test_lint_exemptions.py +116 -0
  265. symdef-0.4.0/tests/test_lint_file.py +216 -0
  266. symdef-0.4.0/tests/test_lint_geometry.py +372 -0
  267. symdef-0.4.0/tests/test_lint_ports.py +858 -0
  268. symdef-0.4.0/tests/test_lint_registry.py +123 -0
  269. symdef-0.4.0/tests/test_lint_slots.py +595 -0
  270. symdef-0.4.0/tests/test_load.py +840 -0
  271. symdef-0.4.0/tests/test_model.py +163 -0
  272. symdef-0.4.0/tests/test_orient.py +361 -0
  273. symdef-0.4.0/tests/test_overlap.py +536 -0
  274. symdef-0.4.0/tests/test_public_api.py +151 -0
  275. symdef-0.4.0/tests/test_purity_gate.py +92 -0
  276. symdef-0.4.0/tests/test_readme.py +54 -0
  277. symdef-0.4.0/tests/test_repeat.py +357 -0
  278. symdef-0.4.0/tests/test_resolve.py +1256 -0
  279. symdef-0.4.0/tests/test_rules_complete.py +113 -0
  280. symdef-0.4.0/tests/test_schema_agreement.py +127 -0
  281. symdef-0.4.0/tests/test_serialize.py +440 -0
  282. symdef-0.4.0/tests/test_site.py +24 -0
  283. symdef-0.4.0/tests/test_svg.py +476 -0
  284. symdef-0.4.0/tests/test_svg_annotated.py +526 -0
  285. symdef-0.4.0/tests/test_svg_total.py +156 -0
  286. symdef-0.4.0/tests/test_units.py +20 -0
  287. symdef-0.4.0/tests/test_version.py +45 -0
  288. symdef-0.4.0/uv.lock +441 -0
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -0,0 +1,33 @@
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-latest
13
+ steps:
14
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
16
+ - run: uv run --only-group dev python scripts/build_site.py site
17
+ - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
18
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
19
+ with:
20
+ path: site/
21
+
22
+ deploy:
23
+ needs: build
24
+ runs-on: ubuntu-latest
25
+ environment:
26
+ name: github-pages
27
+ url: ${{ steps.deployment.outputs.page_url }}
28
+ permissions:
29
+ pages: write
30
+ id-token: write
31
+ steps:
32
+ - id: deployment
33
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
@@ -0,0 +1,37 @@
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-latest
13
+ steps:
14
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
15
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
16
+ - name: The tag matches the package version
17
+ run: test "v$(uv version --short)" = "$GITHUB_REF_NAME"
18
+ - run: uv build
19
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
20
+ with:
21
+ name: dist
22
+ path: dist/
23
+
24
+ pypi:
25
+ needs: build
26
+ runs-on: ubuntu-latest
27
+ environment: pypi
28
+ permissions:
29
+ id-token: write # trusted publishing: no token secret
30
+ steps:
31
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
36
+ with:
37
+ attestations: true
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ __pycache__/
3
+ .ruff_cache/
4
+ .pytest_cache/
5
+ .coverage
6
+ .superpowers/
7
+ claude-tools/
8
+ dist/
@@ -0,0 +1 @@
1
+ 3.15
symdef-0.4.0/AGENTS.md ADDED
@@ -0,0 +1,12 @@
1
+ # AGENTS.md
2
+
3
+ `CLAUDE.md` holds this repo's rules for any agent, not only Claude. This file restates none of
4
+ them.
5
+
6
+ - To work on the toolkit: read `CLAUDE.md`, then the spec,
7
+ `src/symdef/docs/SYMBOL_INTERFACE.html`. `just --list` names the recipes, and
8
+ `just ci` is the gate.
9
+ - To use the toolkit from another project: read `README.md` (install and a short tour), then
10
+ `docs/GUIDE.md` (the concepts and the calls). The docstrings of the names in
11
+ `symdef.__all__` are the API reference. The spec ships inside the package:
12
+ `importlib.resources.files("symdef").joinpath("docs/SYMBOL_INTERFACE.html")`.
symdef-0.4.0/CLAUDE.md ADDED
@@ -0,0 +1,62 @@
1
+ # symdef: agent guide
2
+
3
+ Standard-agnostic Python toolkit for graphical-symbol data. It reads TOML symbol files, resolves
4
+ composition, lints the wiring contract, renders SVG, and builds a resolved JSON bundle plus an SVG
5
+ gallery. Data packages hold only data and reuse this toolkit: `electrical-symbols` (a package
6
+ of the Fransys repo, `../fransys-dev`) now, ISO 14617 and ISA 5.1 later. Alpha,
7
+ single owner.
8
+
9
+ Fransys is
10
+ one user. It pins an exact PyPI version, and its `electrical-symbols` package is an IEC 60617
11
+ data package. A change Fransys needs lands here first, released as `vX.Y.Z` on PyPI, then
12
+ Fransys bumps its pin. No code, test or script here imports or names a Fransys package.
13
+
14
+ ## The spec decides
15
+
16
+ `src/graphical_symbols/docs/SYMBOL_INTERFACE.html` (the spec; older entries in
17
+ `docs/DECISIONS.md` call it the guide) is the only design document. It ships as package data,
18
+ readable at runtime through `importlib.resources`; `electrical-symbols`' verbatim gate reads it
19
+ from the installed distribution (D38). It is final and binding. Read all of it before doing
20
+ anything. Never edit it. Definitions in it
21
+ (extents, overlap, orientations, the wire lane, placement, `repeat`, the 33 lint rules, the JSON
22
+ output format) are exact: implement them as written. The toolkit has one rule beyond the spec's
23
+ 33, `lead-off-port` (D40).
24
+
25
+ Where the spec is silent, decide yourself and record the decision in `docs/DECISIONS.md`, one entry
26
+ each: what you decided, why, what it costs if wrong. If you believe the spec is wrong or
27
+ self-contradictory, do not edit it: implement it as written, record the concern in
28
+ `docs/DECISIONS.md` and report it to the owner at the end.
29
+
30
+ ## The work
31
+
32
+ The toolkit the spec describes is built (spec sections 4 to 12). Work now arrives as a work
33
+ order from the Fransys workflow's orchestrator: a change a Fransys package needs, a
34
+ bug, or a new public function, each recorded in `docs/DECISIONS.md`. Keep the public names of
35
+ spec section 11 exactly as written; a new name gets a decision entry.
36
+
37
+ ## Rules
38
+
39
+ - Python 3.15 (`.python-version`); `requires-python` is `>=3.14`, and `just
40
+ ci` runs the tests on 3.14 too, `uv` only (never bare `python` or `pip`), ruff, ty, pytest, hypothesis,
41
+ vulture, deal. Every dependency pinned to the exact latest stable version. Runtime dependency:
42
+ `deal` only (`jsonschema` may be a dev dependency for the agreement test). TOML is read with the
43
+ standard library's `tomllib`.
44
+ - Pure core: every module-level function in a pure module is `@deal.pure` and does not raise. Only
45
+ the modules that read or write files are impure. `scripts/fp_purity_gate.py` enforces it.
46
+ - `docs/` holds `DECISIONS.md` and `GUIDE.md` only; plans stay out of the repo.
47
+ - Frozen slotted dataclasses; transforms are free functions; no positional port aliases.
48
+ - Every gate and lint rule is proven able to fail with a deliberately broken fixture.
49
+ - `just ci` passes before every commit; quote its output. Look at rendered output: convert SVGs to
50
+ PNG and view them (`uv run --with cairosvg` or playwright, from a scratch directory outside
51
+ the repo).
52
+ - Git: stage named paths only; Conventional Commits ending with
53
+ the attribution line your session gives; work on a feature branch from `main`, never commit to
54
+ `main` directly. The orchestrator reviews, merges, pushes and tags the release (`vX.Y.Z`,
55
+ matching `version` in `pyproject.toml`); an implementer does not.
56
+ - Never edit another repo. A change Fransys needs because of yours goes in your hand-back.
57
+
58
+ ## Done means
59
+
60
+ The work order's acceptance is met, every new rule or gate has a failing fixture, `just ci` is
61
+ green (quote it), and the hand-back lists every decision recorded and anything the owner must
62
+ decide.
symdef-0.4.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OleJBondahl
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.
symdef-0.4.0/PKG-INFO ADDED
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.5
2
+ Name: symdef
3
+ Version: 0.4.0
4
+ Summary: Standard-agnostic core for graphical symbols as strict, typed, immutable Python values
5
+ Project-URL: Repository, https://github.com/OleJBondahl/symdef
6
+ Author: Ole Johan Bondahl
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.14
10
+ Requires-Dist: deal==4.24.6
11
+ Description-Content-Type: text/markdown
12
+
13
+ # symdef
14
+
15
+ Standard-agnostic toolkit for graphical-symbol data. Symbols are TOML files; the toolkit loads
16
+ them, resolves composition, lints the wiring contract, renders SVG, and builds a resolved JSON
17
+ bundle plus a gallery. Data packages hold only data and reuse the toolkit and its gates:
18
+ `electrical-symbols` (IEC 60617-style symbols, Fransys' `packages/electrical-symbols`) now, ISO 14617 and
19
+ ISA 5.1 later.
20
+
21
+ Status: alpha. The toolkit the spec below describes is built: reading, resolving, linting, SVG and
22
+ the build, with every lint rule and gate proven by a failing fixture. Released by exact git tags
23
+ (`v0.1.0` to `v0.3.0` as `graphical-symbols`; `v0.4.0` on as `symdef`); Fransys pins one of them.
24
+
25
+ ## Install
26
+
27
+ ```
28
+ uv add "symdef==0.4.0"
29
+ ```
30
+
31
+ Python 3.14 or later. The one runtime dependency is `deal`. More depth:
32
+ [docs/GUIDE.md](docs/GUIDE.md).
33
+
34
+ ## The design
35
+
36
+ [src/symdef/docs/SYMBOL_INTERFACE.html](src/symdef/docs/SYMBOL_INTERFACE.html)
37
+ is the binding, final spec. Open it in a browser; it ships as package data, readable at runtime
38
+ through `importlib.resources.files("symdef").joinpath("docs/SYMBOL_INTERFACE.html")`.
39
+ The key ideas:
40
+
41
+ - A symbol is a definition: no tag, terminal numbers, position or orientation. Those belong to
42
+ the placement in a drawing library.
43
+ - Port ids are roles (`in`, `out`, `anode`, ...), never terminal markings (`13`, `A1`).
44
+ - Electrical function is data: nodes and typed paths, so a consumer can netlist without
45
+ per-symbol knowledge.
46
+ - Text lives in slots: the symbol reserves boxes for tags and terminal markings.
47
+ - Module units (M = 2.5 mm), a 0.125 M drawing grid and a 1 M wiring grid. A strict wiring
48
+ contract is linted in all 8 orientations.
49
+ - Composition mirrors the standard: composites reference parts by anchor, no copied coordinates.
50
+
51
+ ## What is here
52
+
53
+ The data model and `orient`, the boxes and `repeat`, the TOML reader with its JSON Schema
54
+ (`schema/symbol.schema.json`), the composition resolver, 34 lint rules, plain and annotated SVG,
55
+ and the build (resolved JSON per symbol, the bundle, SVGs, a gallery README). Decisions the spec
56
+ leaves open, and concerns about the spec, are in [docs/DECISIONS.md](docs/DECISIONS.md).
57
+
58
+ ## Use
59
+
60
+ A data repo has `library.toml` and `symbols/<number>.toml`; `root` below is its directory.
61
+
62
+ ```python
63
+ from symdef import lint, load_library
64
+ from symdef.lint import RULES
65
+
66
+ library = load_library(root) # raises LibraryError, listing every problem in the files
67
+ contact = library.get("S00227") # by number; iterating a library is sorted by number
68
+ assert len(RULES) == 34 # the spec's 33 rules plus lead-off-port (D40)
69
+ findings = lint(contact) # all 34 rules, geometric ones in all 8 orientations
70
+ assert findings == () # the spec's make contact lints clean
71
+ for finding in findings: # each has .rule, .severity, .message, .location, .orientation
72
+ print(finding.rule, finding.location, finding.message)
73
+ ```
74
+
75
+ Symbols are immutable; `orient`, `translate` and `repeat` return new ones. A repeated symbol is
76
+ an ordinary symbol with ports such as `1.in` and `3.out`.
77
+
78
+ ```python
79
+ from symdef import Orientation, keepout_box, orient, repeat, to_svg
80
+
81
+ breaker = orient(repeat(contact, 3), Orientation.R0)
82
+ print(keepout_box(breaker))
83
+ svg = to_svg(breaker, annotate=True, texts={"tag": "-Q1"})
84
+ ```
85
+
86
+ `write_build` writes `build/` and `src/<package>/bundle.json` under the repo; `stale_build` lists
87
+ the files that differ from what it would write, so a test can fail when a committed build is out
88
+ of date. `load_bundle` reads a bundle back without the sources. `<package>` is the standard's
89
+ normalised name (lowercase letters and digits, D8): the example's standard is `IEC 60617`, so its
90
+ package is `iec60617`.
91
+
92
+ ```python
93
+ from symdef import load_bundle, stale_build, write_build
94
+
95
+ write_build(library, root)
96
+ assert stale_build(library, root) == ()
97
+ assert len(load_bundle(root / "src" / "iec60617" / "bundle.json")) == len(library)
98
+ ```
99
+
100
+ ## Development
101
+
102
+ ```
103
+ uv sync
104
+ just ci # format check, ruff, ty, vulture, purity gate, pytest with coverage
105
+ ```
106
+
107
+ Coverage must stay at 100% (`--cov-fail-under=100`), so a partial run such as
108
+ `uv run pytest tests/test_units.py` fails on coverage alone: add `--no-cov` for one.
109
+ `tests/test_readme.py` runs the examples above and those in [docs/GUIDE.md](docs/GUIDE.md), so
110
+ they stay true. Agents: start at [AGENTS.md](AGENTS.md).
111
+
112
+ ## License
113
+
114
+ MIT, see [LICENSE](LICENSE).
symdef-0.4.0/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # symdef
2
+
3
+ Standard-agnostic toolkit for graphical-symbol data. Symbols are TOML files; the toolkit loads
4
+ them, resolves composition, lints the wiring contract, renders SVG, and builds a resolved JSON
5
+ bundle plus a gallery. Data packages hold only data and reuse the toolkit and its gates:
6
+ `electrical-symbols` (IEC 60617-style symbols, Fransys' `packages/electrical-symbols`) now, ISO 14617 and
7
+ ISA 5.1 later.
8
+
9
+ Status: alpha. The toolkit the spec below describes is built: reading, resolving, linting, SVG and
10
+ the build, with every lint rule and gate proven by a failing fixture. Released by exact git tags
11
+ (`v0.1.0` to `v0.3.0` as `graphical-symbols`; `v0.4.0` on as `symdef`); Fransys pins one of them.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ uv add "symdef==0.4.0"
17
+ ```
18
+
19
+ Python 3.14 or later. The one runtime dependency is `deal`. More depth:
20
+ [docs/GUIDE.md](docs/GUIDE.md).
21
+
22
+ ## The design
23
+
24
+ [src/symdef/docs/SYMBOL_INTERFACE.html](src/symdef/docs/SYMBOL_INTERFACE.html)
25
+ is the binding, final spec. Open it in a browser; it ships as package data, readable at runtime
26
+ through `importlib.resources.files("symdef").joinpath("docs/SYMBOL_INTERFACE.html")`.
27
+ The key ideas:
28
+
29
+ - A symbol is a definition: no tag, terminal numbers, position or orientation. Those belong to
30
+ the placement in a drawing library.
31
+ - Port ids are roles (`in`, `out`, `anode`, ...), never terminal markings (`13`, `A1`).
32
+ - Electrical function is data: nodes and typed paths, so a consumer can netlist without
33
+ per-symbol knowledge.
34
+ - Text lives in slots: the symbol reserves boxes for tags and terminal markings.
35
+ - Module units (M = 2.5 mm), a 0.125 M drawing grid and a 1 M wiring grid. A strict wiring
36
+ contract is linted in all 8 orientations.
37
+ - Composition mirrors the standard: composites reference parts by anchor, no copied coordinates.
38
+
39
+ ## What is here
40
+
41
+ The data model and `orient`, the boxes and `repeat`, the TOML reader with its JSON Schema
42
+ (`schema/symbol.schema.json`), the composition resolver, 34 lint rules, plain and annotated SVG,
43
+ and the build (resolved JSON per symbol, the bundle, SVGs, a gallery README). Decisions the spec
44
+ leaves open, and concerns about the spec, are in [docs/DECISIONS.md](docs/DECISIONS.md).
45
+
46
+ ## Use
47
+
48
+ A data repo has `library.toml` and `symbols/<number>.toml`; `root` below is its directory.
49
+
50
+ ```python
51
+ from symdef import lint, load_library
52
+ from symdef.lint import RULES
53
+
54
+ library = load_library(root) # raises LibraryError, listing every problem in the files
55
+ contact = library.get("S00227") # by number; iterating a library is sorted by number
56
+ assert len(RULES) == 34 # the spec's 33 rules plus lead-off-port (D40)
57
+ findings = lint(contact) # all 34 rules, geometric ones in all 8 orientations
58
+ assert findings == () # the spec's make contact lints clean
59
+ for finding in findings: # each has .rule, .severity, .message, .location, .orientation
60
+ print(finding.rule, finding.location, finding.message)
61
+ ```
62
+
63
+ Symbols are immutable; `orient`, `translate` and `repeat` return new ones. A repeated symbol is
64
+ an ordinary symbol with ports such as `1.in` and `3.out`.
65
+
66
+ ```python
67
+ from symdef import Orientation, keepout_box, orient, repeat, to_svg
68
+
69
+ breaker = orient(repeat(contact, 3), Orientation.R0)
70
+ print(keepout_box(breaker))
71
+ svg = to_svg(breaker, annotate=True, texts={"tag": "-Q1"})
72
+ ```
73
+
74
+ `write_build` writes `build/` and `src/<package>/bundle.json` under the repo; `stale_build` lists
75
+ the files that differ from what it would write, so a test can fail when a committed build is out
76
+ of date. `load_bundle` reads a bundle back without the sources. `<package>` is the standard's
77
+ normalised name (lowercase letters and digits, D8): the example's standard is `IEC 60617`, so its
78
+ package is `iec60617`.
79
+
80
+ ```python
81
+ from symdef import load_bundle, stale_build, write_build
82
+
83
+ write_build(library, root)
84
+ assert stale_build(library, root) == ()
85
+ assert len(load_bundle(root / "src" / "iec60617" / "bundle.json")) == len(library)
86
+ ```
87
+
88
+ ## Development
89
+
90
+ ```
91
+ uv sync
92
+ just ci # format check, ruff, ty, vulture, purity gate, pytest with coverage
93
+ ```
94
+
95
+ Coverage must stay at 100% (`--cov-fail-under=100`), so a partial run such as
96
+ `uv run pytest tests/test_units.py` fails on coverage alone: add `--no-cov` for one.
97
+ `tests/test_readme.py` runs the examples above and those in [docs/GUIDE.md](docs/GUIDE.md), so
98
+ they stay true. Agents: start at [AGENTS.md](AGENTS.md).
99
+
100
+ ## License
101
+
102
+ MIT, see [LICENSE](LICENSE).