fix(task-8): pin every transcribed standards row, not a sampled subset

The module docstring claimed "every value asserted here is a named row of a
named table", but the converse did not hold: 27 of the 83 shipped rows had a
dimension no test compared against a literal. ISO 4014/4017 M5 (s,k), ISO 4032
M5 (m), ISO 7089 M5 (d1,d2,h), ISO 4762 M4/M6/M10/M16 (dk,s,b) and D/B for 20
of the 33 ISO 15 bearings were constrained only by inequality tests (series
growth) and by the bore-number rule - never by the transcribed number. For a
task whose worst failure is "a standards table row that is not in the
standard", those rows were exactly where such a row would hide.

Measured before the fix with a pytest plugin that rewrote five rows in memory
(M5 hex bolt s=k=99, M5 nut, M5 washer, M16 socket screw dk=s=b=99, 6307 as
78x20 instead of 80x21): 32 passed in 0.07s. All five now fail.

Each of the five tables is pinned by an ISO*_ROWS dict literal carrying every
dimension of every shipped size, parametrized one case per size/designation,
plus a completeness test asserting the table's key set equals the literal's -
so a row added later without a pin is red too (verified: a fabricated M14 head
and 6011 bearing fail the completeness gate). 32 -> 93 tests, 0.06 -> 0.13 s.

No production code changed; no ActiveX member is involved, so no live probe was
required for this finding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-22 19:07:53 +03:00
co-authored by Claude Opus 5
parent 49b972f007
commit 6024723d6b
2 changed files with 155 additions and 87 deletions
+55 -21
View File
@@ -1,4 +1,10 @@
"""ISO 15 boundary dimensions and the ISO 8826 representation conventions.""" """ISO 15 boundary dimensions and the ISO 8826 representation conventions.
``ISO15_ROWS`` below pins d, D and B for *every* one of the 33 shipped
designations, and a completeness test asserts its key set equals the module's.
Inequality tests (series growth, the bore-number rule) constrain a row; only a
literal pins it.
"""
from __future__ import annotations from __future__ import annotations
@@ -17,32 +23,60 @@ from engineering.mech.standards.bearings import (
list_bearings, list_bearings,
) )
#: ISO 15:2017 Table 4 - every transcribed designation, (d, D, B) in mm.
ISO15_ROWS: dict[str, tuple[float, float, float]] = {
# dimension series 10 (600x)
"6000": (10.0, 26.0, 8.0),
"6001": (12.0, 28.0, 8.0),
"6002": (15.0, 32.0, 9.0),
"6003": (17.0, 35.0, 10.0),
"6004": (20.0, 42.0, 12.0),
"6005": (25.0, 47.0, 12.0),
"6006": (30.0, 55.0, 13.0),
"6007": (35.0, 62.0, 14.0),
"6008": (40.0, 68.0, 15.0),
"6009": (45.0, 75.0, 16.0),
"6010": (50.0, 80.0, 16.0),
# dimension series 02 (620x)
"6200": (10.0, 30.0, 9.0),
"6201": (12.0, 32.0, 10.0),
"6202": (15.0, 35.0, 11.0),
"6203": (17.0, 40.0, 12.0),
"6204": (20.0, 47.0, 14.0),
"6205": (25.0, 52.0, 15.0),
"6206": (30.0, 62.0, 16.0),
"6207": (35.0, 72.0, 17.0),
"6208": (40.0, 80.0, 18.0),
"6209": (45.0, 85.0, 19.0),
"6210": (50.0, 90.0, 20.0),
# dimension series 03 (630x)
"6300": (10.0, 35.0, 11.0),
"6301": (12.0, 37.0, 12.0),
"6302": (15.0, 42.0, 13.0),
"6303": (17.0, 47.0, 14.0),
"6304": (20.0, 52.0, 15.0),
"6305": (25.0, 62.0, 17.0),
"6306": (30.0, 72.0, 19.0),
"6307": (35.0, 80.0, 21.0),
"6308": (40.0, 90.0, 23.0),
"6309": (45.0, 100.0, 25.0),
"6310": (50.0, 110.0, 27.0),
}
@pytest.mark.parametrize(
("designation", "bore", "outer", "width"), @pytest.mark.parametrize(("designation", "expected"), sorted(ISO15_ROWS.items()))
[ def test_named_rows_match_iso15(designation, expected):
("6000", 10.0, 26.0, 8.0),
("6004", 20.0, 42.0, 12.0),
("6006", 30.0, 55.0, 13.0),
("6010", 50.0, 80.0, 16.0),
("6200", 10.0, 30.0, 9.0),
("6203", 17.0, 40.0, 12.0),
("6205", 25.0, 52.0, 15.0),
("6208", 40.0, 80.0, 18.0),
("6210", 50.0, 90.0, 20.0),
("6300", 10.0, 35.0, 11.0),
("6305", 25.0, 62.0, 17.0),
("6309", 45.0, 100.0, 25.0),
("6310", 50.0, 110.0, 27.0),
],
)
def test_named_rows_match_iso15(designation, bore, outer, width):
row = bearing(designation) row = bearing(designation)
assert (row["d"], row["D"], row["B"]) == (bore, outer, width) assert (row["d"], row["D"], row["B"]) == expected
assert row["designation"] == designation assert row["designation"] == designation
assert row["source"] == SOURCE_ISO_15 assert row["source"] == SOURCE_ISO_15
def test_every_shipped_bearing_is_pinned_by_a_literal():
"""No shipped designation may escape the table above - that is where a wrong row hides."""
assert set(DEEP_GROOVE) == set(ISO15_ROWS)
def test_bore_number_rule_holds_across_every_series(): def test_bore_number_rule_holds_across_every_series():
"""ISO 15: bore 00/01/02/03 = 10/12/15/17 mm; 04 and up = 5 x bore number.""" """ISO 15: bore 00/01/02/03 = 10/12/15/17 mm; 04 and up = 5 x bore number."""
for series in ("60", "62", "63"): for series in ("60", "62", "63"):
+100 -66
View File
@@ -1,8 +1,11 @@
"""Provenance and coverage of the ISO fastener tables (the table rule, spec 5). """Provenance and coverage of the ISO fastener tables (the table rule, spec 5).
Every value asserted here is a named row of a named table. A row that could not Every value asserted here is a named row of a named table, and the converse holds
be verified against the standard is not in the module at all, so the coverage too: the ``ISO*_ROWS`` literals below carry *every* dimension of *every* shipped
refusals below are as load-bearing as the values. size, and a completeness test asserts each table's key set equals its literal's.
A row nobody pinned is exactly where a wrong across-flats would hide. A row that
could not be verified against the standard is not in the module at all, so the
coverage refusals below are as load-bearing as the values.
""" """
from __future__ import annotations from __future__ import annotations
@@ -14,9 +17,11 @@ import pytest
from engineering.mech.standards import standards from engineering.mech.standards import standards
from engineering.mech.standards.fasteners import ( from engineering.mech.standards.fasteners import (
COARSE_PITCH, COARSE_PITCH,
HEX_HEAD,
HEX_NUT, HEX_NUT,
SOCKET_HEAD, SOCKET_HEAD,
SOURCE_ISO_4014, SOURCE_ISO_4014,
WASHER,
across_corners, across_corners,
hex_head, hex_head,
hex_nut, hex_nut,
@@ -28,18 +33,74 @@ from engineering.mech.standards.fasteners import (
washer, washer,
) )
#: ISO 4014:2011 / ISO 4017:2014 Table 1 - every transcribed size, (s, k).
ISO4014_ROWS: dict[str, tuple[float, float]] = {
"M5": (8.0, 3.5),
"M6": (10.0, 4.0),
"M8": (13.0, 5.3),
"M10": (16.0, 6.4),
"M12": (18.0, 7.5),
"M16": (24.0, 10.0),
"M20": (30.0, 12.5),
"M24": (36.0, 15.0),
"M30": (46.0, 18.7),
"M36": (55.0, 22.5),
}
def test_iso4014_named_rows_match_the_standard(): #: ISO 4032:2012 Table 1 - every transcribed size, (s, m).
ISO4032_ROWS: dict[str, tuple[float, float]] = {
"M5": (8.0, 4.7),
"M6": (10.0, 5.2),
"M8": (13.0, 6.8),
"M10": (16.0, 8.4),
"M12": (18.0, 10.8),
"M16": (24.0, 14.8),
"M20": (30.0, 18.0),
"M24": (36.0, 21.5),
"M30": (46.0, 25.6),
"M36": (55.0, 31.0),
}
#: ISO 7089:2000 Table 1 - every transcribed size, (d1, d2, h).
ISO7089_ROWS: dict[str, tuple[float, float, float]] = {
"M5": (5.3, 10.0, 1.0),
"M6": (6.4, 12.0, 1.6),
"M8": (8.4, 16.0, 1.6),
"M10": (10.5, 20.0, 2.0),
"M12": (13.0, 24.0, 2.5),
"M16": (17.0, 30.0, 3.0),
"M20": (21.0, 37.0, 3.0),
"M24": (25.0, 44.0, 4.0),
"M30": (31.0, 56.0, 4.0),
"M36": (37.0, 66.0, 5.0),
}
#: ISO 4762:2004 Table 1 - every transcribed size, (dk, k, s, b).
ISO4762_ROWS: dict[str, tuple[float, float, float, float]] = {
"M3": (5.5, 3.0, 2.5, 18.0),
"M4": (7.0, 4.0, 3.0, 20.0),
"M5": (8.5, 5.0, 4.0, 22.0),
"M6": (10.0, 6.0, 5.0, 24.0),
"M8": (13.0, 8.0, 6.0, 28.0),
"M10": (16.0, 10.0, 8.0, 32.0),
"M12": (18.0, 12.0, 10.0, 36.0),
"M16": (24.0, 16.0, 14.0, 44.0),
"M20": (30.0, 20.0, 17.0, 52.0),
"M24": (36.0, 24.0, 19.0, 60.0),
}
@pytest.mark.parametrize(("size", "expected"), sorted(ISO4014_ROWS.items()))
def test_iso4014_named_rows_match_the_standard(size, expected):
"""ISO 4014:2011 Table 1 - width across flats s and head height k.""" """ISO 4014:2011 Table 1 - width across flats s and head height k."""
assert (hex_head("M6")["s"], hex_head("M6")["k"]) == (10.0, 4.0) row = hex_head(size)
assert (hex_head("M8")["s"], hex_head("M8")["k"]) == (13.0, 5.3) assert (row["s"], row["k"]) == expected
assert (hex_head("M10")["s"], hex_head("M10")["k"]) == (16.0, 6.4)
assert (hex_head("M12")["s"], hex_head("M12")["k"]) == (18.0, 7.5)
assert (hex_head("M16")["s"], hex_head("M16")["k"]) == (24.0, 10.0) def test_every_iso4014_row_is_pinned_by_a_literal():
assert (hex_head("M20")["s"], hex_head("M20")["k"]) == (30.0, 12.5) """No shipped size may escape the table above - that is where a wrong row hides."""
assert (hex_head("M24")["s"], hex_head("M24")["k"]) == (36.0, 15.0) assert set(HEX_HEAD) == set(ISO4014_ROWS)
assert (hex_head("M30")["s"], hex_head("M30")["k"]) == (46.0, 18.7) assert sizes("ISO 4014") == tuple(HEX_HEAD)
assert (hex_head("M36")["s"], hex_head("M36")["k"]) == (55.0, 22.5)
def test_iso4017_shares_the_iso4014_head(): def test_iso4017_shares_the_iso4014_head():
@@ -47,66 +108,39 @@ def test_iso4017_shares_the_iso4014_head():
assert sizes("ISO 4017") == sizes("ISO 4014") assert sizes("ISO 4017") == sizes("ISO 4014")
def test_iso4032_named_rows_match_the_standard(): @pytest.mark.parametrize(("size", "expected"), sorted(ISO4032_ROWS.items()))
"""ISO 4032:2012 Table 1 - nut height m (max).""" def test_iso4032_named_rows_match_the_standard(size, expected):
assert hex_nut("M6")["m"] == 5.2 """ISO 4032:2012 Table 1 - width across flats s and nut height m (max)."""
assert hex_nut("M8")["m"] == 6.8 row = hex_nut(size)
assert hex_nut("M10")["m"] == 8.4 assert (row["s"], row["m"]) == expected
assert hex_nut("M12")["m"] == 10.8
assert hex_nut("M16")["m"] == 14.8
assert hex_nut("M20")["m"] == 18.0 def test_every_iso4032_row_is_pinned_and_shares_the_bolt_across_flats():
assert hex_nut("M24")["m"] == 21.5 assert set(HEX_NUT) == set(ISO4032_ROWS)
assert hex_nut("M30")["m"] == 25.6
assert hex_nut("M36")["m"] == 31.0
for size in HEX_NUT: for size in HEX_NUT:
assert hex_nut(size)["s"] == hex_head(size)["s"] assert hex_nut(size)["s"] == hex_head(size)["s"]
def test_iso7089_named_rows_match_the_standard(): @pytest.mark.parametrize(("size", "expected"), sorted(ISO7089_ROWS.items()))
def test_iso7089_named_rows_match_the_standard(size, expected):
"""ISO 7089:2000 Table 1 - d1, d2, h (nominal), normal series, grade A.""" """ISO 7089:2000 Table 1 - d1, d2, h (nominal), normal series, grade A."""
assert (washer("M6")["d1"], washer("M6")["d2"], washer("M6")["h"]) == (6.4, 12.0, 1.6) row = washer(size)
assert (washer("M8")["d1"], washer("M8")["d2"], washer("M8")["h"]) == (8.4, 16.0, 1.6) assert (row["d1"], row["d2"], row["h"]) == expected
assert (washer("M10")["d1"], washer("M10")["d2"], washer("M10")["h"]) == (10.5, 20.0, 2.0)
assert (washer("M12")["d1"], washer("M12")["d2"], washer("M12")["h"]) == (13.0, 24.0, 2.5)
assert (washer("M16")["d1"], washer("M16")["d2"], washer("M16")["h"]) == (17.0, 30.0, 3.0)
assert (washer("M20")["d1"], washer("M20")["d2"], washer("M20")["h"]) == (21.0, 37.0, 3.0)
assert (washer("M24")["d1"], washer("M24")["d2"], washer("M24")["h"]) == (25.0, 44.0, 4.0)
assert (washer("M30")["d1"], washer("M30")["d2"], washer("M30")["h"]) == (31.0, 56.0, 4.0)
assert (washer("M36")["d1"], washer("M36")["d2"], washer("M36")["h"]) == (37.0, 66.0, 5.0)
def test_iso4762_named_rows_match_the_standard(): def test_every_iso7089_row_is_pinned_by_a_literal():
"""ISO 4762:2004 Table 1 - head dk (max), socket s, thread length b.""" assert set(WASHER) == set(ISO7089_ROWS)
assert (
socket_head("M3")["dk"],
socket_head("M3")["s"], @pytest.mark.parametrize(("size", "expected"), sorted(ISO4762_ROWS.items()))
socket_head("M3")["b"], def test_iso4762_named_rows_match_the_standard(size, expected):
) == (5.5, 2.5, 18.0) """ISO 4762:2004 Table 1 - head dk (max), head height k (max), socket s, thread length b."""
assert ( row = socket_head(size)
socket_head("M5")["dk"], assert (row["dk"], row["k"], row["s"], row["b"]) == expected
socket_head("M5")["s"],
socket_head("M5")["b"],
) == (8.5, 4.0, 22.0) def test_every_iso4762_row_is_pinned_and_its_head_height_equals_the_thread_diameter():
assert ( assert set(SOCKET_HEAD) == set(ISO4762_ROWS)
socket_head("M8")["dk"],
socket_head("M8")["s"],
socket_head("M8")["b"],
) == (13.0, 6.0, 28.0)
assert (
socket_head("M12")["dk"],
socket_head("M12")["s"],
socket_head("M12")["b"],
) == (18.0, 10.0, 36.0)
assert (
socket_head("M20")["dk"],
socket_head("M20")["s"],
socket_head("M20")["b"],
) == (30.0, 17.0, 52.0)
assert (
socket_head("M24")["dk"],
socket_head("M24")["s"],
socket_head("M24")["b"],
) == (36.0, 19.0, 60.0)
# ISO 4762 head height equals the nominal thread diameter at every size here. # ISO 4762 head height equals the nominal thread diameter at every size here.
for size in SOCKET_HEAD: for size in SOCKET_HEAD:
assert socket_head(size)["k"] == nominal_diameter(size) assert socket_head(size)["k"] == nominal_diameter(size)