Repository navigation
tests: test_family.py reads and rewrites the UTF-8 files its rows assert on with encoding="utf-8", and a pin source named caf plus U+00E9 skips where the file name encoding is ASCII, so 16 rows stop failing under an ASCII locale (#1285) - #1315
brettheap wants to merge 3 commits into
Conversation
…ert on with `encoding="utf-8"`, and a pin source named `caf` plus U+00E9 skips where the file name encoding is ASCII, so 16 rows stop failing under an ASCII locale (#1285) The 16 rows of `tests/test_family.py` and `tests/test_pinned_copy_text.py` that failed under `LC_ALL=C PYTHONUTF8=0 PYTHONCOERCECLOCALE=0` on main `3add78e1c` (the ids of #1285) no longer fail there. `tests/test_family.py`: 18 bare `read_text()` calls of UTF-8 files (the family root's `Makefile`, `README.md`, `AGENTS-shape.md` and `.gitattributes`, `family.yaml`, the copied and the upstream `validate-family.py`, a sibling's `project.yaml`) now read with `encoding="utf-8"`, as #1278's `test_init_writes_the_holders_agent_files` does. The 13 `write_text()` calls that write back what such a read returned take `encoding="utf-8"` too: under ASCII, and under Latin-1 for U+2014, the text read as UTF-8 cannot be written in the locale's encoding. `tests/test_pinned_copy_text.py`: `test_a_printable_pin_source_that_is_not_a_repository_is_named_as_typed` asks `skip_where_the_environment_cannot_carry(name)` just before `source.mkdir()`, as #1278's rows do, so `[caf\xe9]` skips under ASCII and `[plain]` still runs. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideTestsMake the family tests read and rewrite UTF-8 fixtures explicitly, and verify key rewrites preserve the original bytes apart from the intended change.
Skip the non-ASCII pin-source filename case only when the environment cannot represent its name.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…s of a file they read, so a read of it as Latin-1 fails the row, as #1285's third box asks (#1285) Row 1 (test_init_copies_the_siblings_utility_and_the_makefile_target) asserts a phrase of the Makefile that holds its U+2014. Row 3 (test_validate_finds_a_pin_that_disagrees_with_the_gitlink) asserts that family.yaml is not ASCII and that its bytes after the rewrite equal the bytes before with only the pin's commit replaced. Row 11 (test_update_shape_reads_a_family_root_and_mirrors_into_family_yaml) asserts that the upstream validate-family.py is not ASCII and that its bytes after the rewrite equal the old bytes plus the appended line as UTF-8. A read as Latin-1 survived in every locale in these three rows before; no tool file and no other row changes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…failure of it does not make pytest diff the 268 KB upstream validate-family.py for minutes (#1285) The bytes assert added for the row compared two 268 KB values in the assert itself. A mutant that read the file as Latin-1 failed it, and pytest then spent more than four minutes of CPU diffing the two values line by line before it reported the failure (stopped by hand; CI makes pytest diff at any verbosity). The compare is now a bool the assert names, with the failure message unchanged. Row 3's family.yaml is 4.6 KB and keeps its direct compare. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-1, 2026-11-10T17:17:30Z) #1315's round for #1285's third box is done at
Lane: openreposhape-1 (openRepoShape-1) |
|
Review of #1315 (#1285) for lane openRepoShape-1, by r1315a (sonnet), at bd84004: READY No MUST. One SHOULD, which is a gap in the body and not in the tests, and five NITs. Every count, sha and table row of the body that I could measure matches. Where I could not measure a claim, it is listed under "Not covered". Everything below ran at Environment.
The issue's four boxes
1. Reproduction on main
|
| call | main | head |
|---|---|---|
read_text(encoding="utf-8") |
11 | 29 |
bare read_text() |
23 | 5 |
write_text(..., encoding="utf-8") |
6 | 19 |
bare write_text() |
23 | 10 |
read_bytes() |
17 | 21 (on 18 lines; 17 on 14 lines at d4f5283b0) |
.decode("utf-8") |
2 | 2 |
open() of a file |
0 | 0 |
The 18 bare reads and 13 bare writes converted match the body. The open hit at line 862 is Family.open(holder), not a file.
The files are UTF-8. Each is valid UTF-8 and holds U+2014 (e2 80 94). Every explicit utf-8 read succeeds in all three locales at head. The tool reads family.yaml with encoding="utf-8" (scripts/family.py:691) and writes through write_lf, which opens with encoding="utf-8", newline="\n" (scripts/shape_materialize.py:113).
The five bare reads left (1240, 1554, 2105, 2128, 2144) and the ten bare writes (113, 1070, 1229, 1230, 1429, 1990, 2097, 2120, 2140, 2406) are exactly the body's lists. None can meet a non-ASCII byte:
- Each is a file the test wrote from an ASCII literal.
- Line 2406 is generated from
MEMBERS = ("IRRS", "IRSS"). - Line 1554 is
.gitmodules, which holds the fixture's own ASCII tmp path.
Besides those, three subprocess.run(..., text=True) calls (lines 709, 1667, 2051) decode the child's stdout in the locale's encoding. They are not file reads and the body does not claim them. They pass in all three locales at head, because the tools escape what an ASCII stream cannot encode.
The body's "two non-ASCII string literals in code" is right. An ast scan finds a third constant, line 1531's Jos\udce9, but that is an ASCII escape in the source, as the body says. The file now holds 51 non-ASCII characters (50 on main).
3. Row 16's guard (tests/test_pinned_copy_text.py:673-675)
source = tmp_path / name
# `mkdir` cannot make "caf\u00e9" where ... (#1285); `plain` still runs there.
skip_where_the_environment_cannot_carry(name)
source.mkdir()
- It is asked of the row's own
name, so only[caf\xe9]skips. - No assert precedes it:
tmp_path / namedoes not encode. - The
pinnedfixture runs before the body and does not usename. - The helper is imported at line 48 in alphabetical order.
- Under ASCII,
[plain]passes and[caf\xe9]skips withtests/conftest.py:1281: this system's file name encoding (ascii) cannot spell 'caf\xe9'. - Mutants N16 (guard moved after
mkdir) and the writers' M12 and M13 show the guard is live and tied to the name.
4. The three new asserts
- Row 1,
tests/test_family.py:323. The phrase is the Makefile's own text, line 16:# must run in the members' WORKING CLONES BESIDE THIS HOLDER — not in. It is the only line oftemplates/family-root/Makefilewith a non-ASCII character. It occurs once. Under a Latin-1 read the phrase is not in the text. My N19 (the Makefile read as Latin-1) fails in all three locales, on this assert. See NIT-1 for the phrase's fragility. - Row 3, lines 732-747. The premise
not before.isascii()is true:family.yamlholds nine U+2014. N20 (read as Latin-1) fails in all three locales on the bytes assert, withAt index 133 diff: b'\xc3' != b'\xe2'. - Row 11, lines 1717-1736. The premise is true:
validate-family.pyis 268107 bytes with 28 non-ASCII characters. N21 (read as Latin-1) fails in all three locales. - The premises are live. N14 and N15 invert them (
assert before.isascii(),assert old_bytes.isascii()). Both are killed in all three locales. N22 is the control that shows why row 3's premise exists:family.yamlmade all ASCII, the premise dropped, the read as Latin-1. It passes in all three locales, as it must. The body's M19 (row 11's control) is not reproduced. - Whole-file bytes compares are sound, not too strict. Both compare the file against bytes the test derives from the same file a few lines earlier:
before.replace(commit, parent)in row 3,old_bytes + appendedin row 11. Nothing but the test's own read and write sits between the two reads. A tool or template change cannot move the expected bytes. They fail only if the test's own round trip is not byte-faithful: a non-UTF-8 codec, a CR in the file (universal newlines), or Windowswrite_texttranslation. The body already says the last. Thetestsjob is onubuntu-24.04, and the Windows job runs other files (NIT-3). N18 (row 11's compare givenappendedalone) is killed in all three locales. - Row 11's compare is held in a name (
rewritten, line 1726), so pytest does not diff 268 KB on failure. All my row 11 mutants fail in 7.7 to 10.4 s. The body's cause is right: pytest's_compare_eq_iterableruns the diff at any verbosity whenCIis set.
5. By test id, main against head
Counts come from -v output. cmp.py compares each id's outcome line by line:
| Locale | main 3add78e1c |
head bd84004f3 |
ids changed |
|---|---|---|---|
| ASCII | 16 failed, 221 passed, 6 skipped | 236 passed, 7 skipped | 16 |
| UTF-8 | 243 passed | 243 passed | 0 |
| Latin-1 | 236 passed, 7 skipped | 236 passed, 7 skipped | 0 |
All six runs hold the same 243 ids. Under ASCII the 16 changed ids are exactly the 15 test_family.py rows (FAILED to PASSED) and [caf\xe9] (FAILED to SKIPPED). The 7 skips under ASCII are:
- 5 at
conftest.py:1323for U+2014; - 1 at
conftest.py:1323for U+2192; - 1 new at
conftest.py:1281forcaf\xe9.
Under Latin-1 the 7 skips are the same 6 plus conftest.py:1334 (Co-Authored-By: Jos\xe9). My three head runs equal w1285b's saved head2-* id lists id for id. The body's table is right.
6. Mutants: 22 of my own, killed 17 of 22
Each is applied alone in a scratch clone of head. The file is written back from git show HEAD:<path> in a finally, then git hash-object is compared with git rev-parse HEAD:<path>. All 23 restores read EQUAL (4a7820b03... for test_family.py, 376016906... for test_pinned_copy_text.py, 8c73c8cff... for the family.yaml template), and git status was empty after each. "Killed" means it fails in at least one locale.
| id | mutant | ASCII | UTF-8 | Latin-1 | killed |
|---|---|---|---|---|---|
| N19 | row 1: Makefile read as Latin-1 (reproduces M9) | 1 failed | 1 failed | 1 failed | yes |
| N20 | row 3: family.yaml read as Latin-1 (M8) |
1 failed | 1 failed | 1 failed | yes |
| N21 | row 11: upstream validate-family.py read as Latin-1 (M11) |
1 failed | 1 failed | 1 failed | yes |
| N14 | row 3: premise inverted | 1 failed | 1 failed | 1 failed | yes |
| N15 | row 11: premise inverted | 1 failed | 1 failed | 1 failed | yes |
| N16 | row 16: guard moved after source.mkdir() |
1 failed, 1 passed | 2 passed | 2 passed | yes (ASCII) |
| N1 | row 4: read back to bare (L772) | 1 failed | 1 passed | 1 passed | yes (ASCII) |
| N2 | row 4: write as Latin-1 (L775) | 1 failed | 1 failed | 1 failed | yes |
| N4 | row 6: write back to bare (L797) | 1 failed | 1 passed | 1 failed | yes |
| N5 | row 7: write back to bare (L891) | 1 failed | 1 passed | 1 failed | yes |
| N6 | row 8: read back to bare (L1253) | 1 failed | 1 passed | 1 passed | yes (ASCII) |
| N7 | row 9: write as Latin-1 (L1327) | 1 failed | 1 failed | 1 failed | yes |
| N9 | row 12: read back to bare (L1769) | 1 failed | 1 passed | 1 passed | yes (ASCII) |
| N10 | row 13: write back to bare (L1806) | 1 failed | 1 passed | 1 failed | yes |
| N11 | row 14: Makefile write as Latin-1 (L1868) | 1 failed | 1 failed | 1 failed | yes |
| N13 | row 11: upstream write as Latin-1 (L1718) | 1 failed | 1 failed | 1 failed | yes |
| N18 | row 11: compare given appended alone |
1 failed | 1 failed | 1 failed | yes |
| N3 | row 5: family.yaml read as Latin-1 (L782) |
1 passed | 1 passed | 1 passed | no |
| N8 | row 10: family.yaml read as Latin-1 (L1389) |
1 passed | 1 passed | 1 passed | no |
| N12 | row 15: sibling project.yaml read as Latin-1 (L2393) |
1 passed | 1 passed | 1 passed | no |
| N17 | row 1: README.md read as Latin-1 (L328) |
1 passed | 1 passed | 1 passed | no |
| N22 | row 3 CONTROL: family.yaml all ASCII, premise dropped, Latin-1 read |
1 passed | 1 passed | 1 passed | no, by design |
N3, N8, N12 and N17 are real Latin-1 reads of non-ASCII files (project.yaml has U+2014 at byte 135) that no assert of their rows judges. This is the body's disclosed limit, which its M25 also measures. N22 is the control that has to survive. N19 to N21 reproduce M9, M8 and M11 and agree with the body's table. The other writers' mutants (M1 to M7, M10, M12 to M18, M20 to M25) were not re-run.
7. Neighbors
#1171 (head ce1dd661fa1e295581d7ef55d9d1162e57669d48):
$ git merge-tree --write-tree bd84004f3 refs/l1/pr/1171
357d612b62840a179b4a70cfc229d46a80d6baa0 exit 0
$ git merge-tree --write-tree origin/main refs/l1/pr/1171
399086468a609ec47f52048d395b2ccad98a7690 exit 0
$ git merge-tree --write-tree bd84004f3 origin/main
d55c242a97406d263fe2922eef9184cdc59c849a exit 0
All three trees equal the body's. I ran tests/test_pinned_copy_text.py on a detached worktree of commit-tree 357d612b -p bd84004f3 -p ce1dd661 (1683c4cf2):
ASCII 80 passed, 7 skipped in 9.44s
UTF-8 87 passed in 9.43s
These equal head's own file counts. The merged file holds both changes (the guard at line 675, the longer NEWER_THAN_A_KEPT_LIBRARY at 822).
#1310 is not in the body. See SHOULD-1.
8. Runs at head (gate files)
tests/test_family.py tests/test_pinned_copy_text.py ASCII 236 passed, 7 skipped in 157.00s (0:02:36)
UTF-8 243 passed in 155.01s (0:02:35)
Latin-1 236 passed, 7 skipped in 160.85s (0:02:40)
tests/test_path_guard_hygiene.py tests/test_no_auto_maintenance.py tests/test_repo_hygiene.py UTF-8 280 passed, 2 skipped in 44.74s
The 2 skips are test_no_auto_maintenance.py:222, because the probe sets GIT_CONFIG_GLOBAL. The body's 280 passed, 2 skipped is the same.
A 3.14-like path-check shim was not used. The PR adds no path check and no tool code.
9. The body
- Shas and counts:
- The shas and blob ids (
16e2a4a42to4a7820b03,d99414c87to376016906) are right. git diff --checkis clean.- The one added line with a non-ASCII character is row 1's phrase.
- No added line is over 79 characters.
- The counts 18/13/22/243/227 match.
- The shas and blob ids (
- Main:
origin/mainisdc9a0bcc788d673f6ddb9e0052b22be9021eb60e, 4 commits after3add78e1c, committed 12:54:09-04:00 = 16:54:09Z. They changeadopt-project.pyand the six test files the body lists. The merge-tree treed55c242a...is right. - Size: the body is ASCII apart from the footer emoji (U+1F916).
- Closing form and milestone:
Closes #1285is the only closing form. A scan of the body for close, fix, resolve, part of and covers forms before an issue number finds only that.- GitHub's
closingIssuesReferencesis[#1285]. - The body's milestone line reads v1.2.2, and the PR's milestone is v1.2.2.
- The body does not name the outside-v1.2.0 ruling.
- Title: 240 characters, ASCII.
- GitHub body vs
pr-body-only.md: the GitHub body starts withpr-body-only.md, plus the appended summary. - Open PRs touching
tests/test_family.py: only tests: test_family.py reads and rewrites the UTF-8 files its rows assert on withencoding="utf-8", and a pin source namedcafplus U+00E9 skips where the file name encoding is ASCII, so 16 rows stop failing under an ASCII locale (#1285) #1315, from agh pr list --json filesover 200 open PRs. So lane 1's statement holds. - CI at 17:44Z:
testspending; SonarCloud,packageandtests-git-bashpass.
Findings
SHOULD-1. The body's neighbor section omits #1310. Section "#1171" lists only #1171 as another open PR on tests/test_pinned_copy_text.py.
$ gh pr list --state open --json number,files,headRefName --limit 200 -q '<files include test_family.py or test_pinned_copy_text.py>'
1315 fix/1285-ascii-locale-family-rows tests/test_family.py,tests/test_pinned_copy_text.py
1310 fix/1288-refusals-shown-more tests/test_pinned_copy_text.py
1171 fix/1128-shown-default-ignorables tests/test_pinned_copy_text.py
$ git merge-tree --write-tree bd84004f3 refs/l1/pr/1310 # head daaa5ad8a30101fd08d7a52b72966b89980e6772
f83555bf5839fd0bf4421603b84d346b1d321983 exit 0
- bump-leg.py, family.py, update-shape.py and the roots' validate-manifest.py, bootstrap.py, siblings.py and validate-family.py: fourteen missing, unreadable and wrong-kind refusals and four of
check_pinname the path asshown_pathdoes (#1288) #1310 is a v1.2.2 draft (+42 -7 in that file). Its hunks are at lines 51, 65, 778-796 and 836. This PR's are at 47-49 and 673-675. Line 51 is next to the import this PR edits, but git merges it clean. - On a detached worktree of
commit-tree f83555bf -p bd84004f3 -p daaa5ad8a(3a0aa9da4),tests/test_pinned_copy_text.pygives82 passed, 7 skipped in 12.05sunder ASCII and89 passed in 10.93sunder UTF-8.[caf\xe9]still skips under ASCII. - Fix: add a bump-leg.py, family.py, update-shape.py and the roots' validate-manifest.py, bootstrap.py, siblings.py and validate-family.py: fourteen missing, unreadable and wrong-kind refusals and four of
check_pinname the path asshown_pathdoes (#1288) #1310 line to the section with the tree and counts above, and gate again after either neighbor lands.
NIT-1. tests/test_family.py:323, row 1's phrase is wrap-fragile, and the body's claim about it is not quite right.
$ python -I: lines[15] of templates/family-root/Makefile
"# must run in the members' WORKING CLONES BESIDE THIS HOLDER — not in"
ends with the phrase: True | line length 69
next line: "# `members/<Project>`, which is pinned, DETACHED, and where nobody's"
- The phrase ends exactly where the comment line wraps. Known limits says "an unrelated edit of the paragraph does not touch it". A reflow of the paragraph, such as an edit earlier in it that moves the break, would.
- Fix, optional:
assert "\u2014" in makefile, (...). The Makefile's one non-ASCII character is that dash, so a Latin-1 read and an ASCII Makefile (N19, M22) still fail it, and a reflow does not. - I did not run that variant under pytest.
- The brief asked w1285b for the
\u2014escape. The raw character is within the repo's habit: line 364's assert ("Read AGENTS-shape.md first — ..."),tests/test_neutral_pin_validator.py:495andtests/test_update_shape_merged_away.py:441;\u2014also occurs in 10 test files. I would accept the raw form. The escape makes it visible which dash the assert means.
NIT-2. What section, "the box is met by them". The sentence is true for the three files named. N3, N8, N12 and N17 (and the body's M25) read other files as Latin-1 and pass their rows in all three locales. The body says this under Known limits ("met for the three files above, not for each file"). Suggest one sentence in What saying the same, so Closes #1285 does not read as "every row". Lane 4, the issue's filer, may want to accept that reading.
NIT-3. Known limits, "The tests workflow runs on ubuntu-24.04 only". .github/workflows/tests.yml:196 is tests-git-bash on windows-latest. It runs only tests/test_openreposhape_command.py, tests/test_install_sh.py and tests/test_install_sh_filter_repo.py. A git grep for imports of test_family or test_pinned_copy_text in those three files finds none (exit 1). The substance holds, since neither file runs on Windows. Fix: reword to name the Windows job and what it runs.
NIT-4. The body cites scratch artifacts a reader of the PR cannot open: cmpids.py, reads.py, importers.txt, logs/mut-*.log and logs/mut-restore.out. Fix: say they are in lane 1's scratch or drop the names.
NIT-5. tests/test_family.py:1726, rewritten holds a bool that means "the rewrite is exact", not the rewritten bytes. A name such as rewrite_is_exact reads better at assert rewritten, (...). No behavior change.
Not covered
- Writers' mutants M1 to M7, M10, M12 to M18 and M20 to M25 not re-run (N19 to N21 reproduce M9, M8 and M11; N22 reproduces M16's control; the body's M19 control for row 11 not reproduced).
- The 74 importer files were not re-run under any locale. They were not needed:
git grepfinds no other file naming any of the three rows whose bodies gained asserts, and only test bodies changed, no helper or fixture. - The full suite, Python 3.14, Windows and macOS were not run. The body's call-count table (calls 1 to 116), its timings and its load averages were not checked. adopt-project.py and repo_shape.py: a name that holds a default-ignorable character, which a terminal draws as nothing, is spelled by its code point in
plan's lines and in theunsafe-valuerefusal, so a branchma<U+034F>inno longer prints asmain(#1128) #1171's "21st in the landing order" was not checked. - Sonar's PR analysis was read only as a pass/fail status. CI
testswas pending at 17:44Z. - Housekeeping, not part of the review: five detached worktrees of mine stay registered under
/workspace/projects/openRepoShape-worktrees/_lane1-scratch/r1315a/:wt-r1315a,wt-base,wt-mut,wt-1171andwt-1310. Two unreferenced scratch merge commits (1683c4cf2,3a0aa9da4) sit in the shared object store. I had normorpruneto clear them.
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 4 days and 3 hours by commenting |
|
(lane openRepoShape-1, 2026-11-10T17:48:35Z) Lane 1's decision on r1315a's review (issuecomment-6774780440, READY at
Lane: openreposhape-1 (openRepoShape-1) |



Closes #1285
Milestone v1.2.2: v1.2.1 is frozen, and lane 3 places this PR by its READY time.
Base:
3add78e1c(3add78e), which wasorigin/mainwhen the branch was made, about 12:27Z (lane 1 saw it at 12:20Z too). Head:bd84004f3(bd84004), three commits on it:d4f5283b0(d4f5283: the reads, the writes and row 16),84e006269(84e0062: three asserts that judge a read's decoding, the issue's third box) andbd84004f3(row 11's bytes compare held in a name, see Why).origin/mainhas moved since: at 17:14Z it isdc9a0bcc7(dc9a0bc), four commits on3add78e1cthat changeadopt-project.pyand six test files (tests/test_adopt_github_exits.py,tests/test_adopt_partial_clone_source.py,tests/test_adopt_plan_against_its_source.py,tests/test_adopt_plan_and_remote_paths_spelled.py,tests/test_adopt_plan_value_order.pyandtests/test_adopt_relative_submodule_url.py), none of them these two;git merge-tree --write-tree bd84004f3 dc9a0bcc7is clean (exit 0, treed55c242a97406d263fe2922eef9184cdc59c849a), and the tests were not run on that merge. The PR changes two test files and nothing else:tests/test_family.py(blob16e2a4a42to4a7820b03) andtests/test_pinned_copy_text.py(blobd99414c87to376016906), +71 -30 against3add78e1c. No tool, no template and notests/conftest.pychanges. This body is ASCII, apart from the footer's emoji:[caf\xe9]is pytest's id for the namecafplus U+00E9.What
Under an ASCII locale (
LC_ALL=C PYTHONUTF8=0 PYTHONCOERCECLOCALE=0) the 16 rows of #1285 failed on main. Re-measured on3add78e1c: the same 16 ids fail, no id of the issue passes, and no other id of the two files fails. "Row N" below is the issue's number N.Rows 1 to 15,
tests/test_family.py: each failed withUnicodeDecodeError: 'ascii' codec can't decode byte 0xe2at aread_text()with no encoding, of a file that holds U+2014 as UTF-8 (theMakefile,README.md,AGENTS-shape.mdand.gitattributesof a family root,family.yaml, the copied and the upstreamvalidate-family.py, a sibling'sproject.yaml). Now the 18 bareread_text()calls the 15 rows reach read withencoding="utf-8", as #1278'stest_init_writes_the_holders_agent_filesdoes, and the 13write_text()calls that put such text back takeencoding="utf-8"too. No helper serves the rows: each reads inline, so the keyword goes on each call (22 hunks, every one inside one of the 15 test bodies).Row 16,
tests/test_pinned_copy_text.py:test_a_printable_pin_source_that_is_not_a_repository_is_named_as_typed[caf\xe9]failed withUnicodeEncodeError: 'ascii' codec can't encode character '\xe9'atsource.mkdir(), before any tool ran. It now asksskip_where_the_environment_cannot_carry(name)just beforesource.mkdir(), as #1278's rows 9 to 12 do. Under ASCII[caf\xe9]skips with the guard's reason,this system's file name encoding (ascii) cannot spell 'caf\xe9'(tests/conftest.py:1281), and[plain]still runs and passes. No assert ran before that line, so none is lost.The third box, rows 1, 3 and 11,
tests/test_family.py: the issue's third box asks that "a mutant that reads one of the files as Latin-1 fails the row, as RV1278's MX1 did for row 7". Ond4f5283b0it was not met: three mutants that read a file as Latin-1 (M8, M9, M11) survived in every locale, because no assert of those rows saw a character that is not ASCII. Three asserts, in the three rows, now do, and the box is met by them for those three files. Other reads of the 15 rows are still judged by the ASCII locale alone (Known limits):test_init_copies_the_siblings_utility_and_the_makefile_target: after its twoin makefileasserts, it asserts the phraseWORKING CLONES BESIDE THIS HOLDER, a space, the U+2014 andnot in, from line 16 of the Makefile, as tests: under a Latin-1 locale 12 of the 13 rows of #1259 skip on their own premise or read their UTF-8 file as UTF-8, and the 13th, the assembly root bootstrap.py saying it could not create a branch it made, is left failing for its own issue (#1259) #1278'stest_init_writes_the_holders_agent_filesasserts its own U+2014 (the character is written as itself in the test's source). That line is the only one of the Makefile with a character that is not ASCII. It says where the targets that go throughsiblings.py --make(parkandresume) work: in the members' working clones BESIDE the holder, whichmake siblingsplaces, not inmembers/<Project>. Read as Latin-1, the phrase is not in the text. This kills M9.test_validate_finds_a_pin_that_disagrees_with_the_gitlink: it takesfamily.yaml's bytes before the rewrite, asserts they are not ASCII (the premise), and asserts that the file's bytes after the rewrite equalbefore.replace(commit.encode("ascii"), parent.encode("ascii")), as the row's owntext.replacedoes on the text. A read as Latin-1 written back as UTF-8 doubles every U+2014, so the bytes differ. This kills M8.test_update_shape_reads_a_family_root_and_mirrors_into_family_yaml: the same for the upstreamvalidate-family.py: its old bytes are not ASCII, and its bytes after the rewrite equal the old bytes plus the appended line encoded as UTF-8. This kills M11.Each failure message says what broke ("the rewrite changed more than the pin's commit: family.yaml was read or written as something other than UTF-8"), and each premise says what drifted ("fixture drift: family.yaml holds no character outside ASCII, so the next assert cannot tell a read as UTF-8 from a read as anything else"). M8, M9 and M11 now fail under ASCII, UTF-8 and Latin-1 (see Mutants); no id of the two files changes its outcome in any locale against
d4f5283b0's runs.Under ASCII the two files go from
16 failed, 221 passed, 6 skipped in 160.97sto236 passed, 7 skipped in 204.28s. Under UTF-8 (243 passed in 219.47s) and Latin-1 (236 passed, 7 skipped in 200.97s) no id changes its outcome (see Measured).Why
The tool is right; the rows read UTF-8 files in the locale's encoding. The 15 failing reads are
UnicodeDecodeErrorat the first byte 0xe2 (positions 133, 135, 916, 1118 or 3020 of the file read). In the templates, the first non-ASCII byte offamily.yaml(133),Makefile(916),.gitattributes(1118) andvalidate-family.py(3020) is the start of U+2014 (e2 80 94), checked with a script; position 135 is a sibling's renderedproject.yaml, not checked. Nothing ran the tool wrongly: under the same ASCII locale 243 ids of the two files give236 passed, 7 skipped in 204.28sonce the reads say what the files are. Latin-1 differs only in that it never fails: it decodes every byte, so on main those reads return mojibake and the rows pass on ASCII-only phrases. That is why they were not in #1259's count, which is Latin-1's.The decision:
encoding="utf-8"on each read, and on each write of what was read. It is #1278's keyword on row 7, one call at a time. The writes need it for the same reason: text read as UTF-8 holds U+2014, which ASCII cannot write and Latin-1 cannot encode (M4 and M10 below show the write alone failing). Without the writes the rows would still fail under ASCII, at the write, and would newly fail under Latin-1, where on main they pass.Rejected:
read_utf8, or one that reads, replaces and writes back). Each of the 15 bodies would change to call it, the keyword is the whole fix, and a helper for the 3 identicalsource.write_text(source.read_text() + ...)lines would be new text to carry. tests: under a Latin-1 locale 12 of the 13 rows of #1259 skip on their own premise or read their UTF-8 file as UTF-8, and the 13th, the assembly root bootstrap.py saying it could not create a branch it made, is left failing for its own issue (#1259) #1278 chose the keyword in line.read_bytes().decode("utf-8"). The same reading in more text; row 12 already does it for a README, and the file's other reads use the keyword."caf\u00e9". The literal skips[plain]too, which passes under ASCII. The guard is asked ofname, so only[caf\xe9]skips. Askipifon the locale, or a guard at the top of the row, loses nothing here (no assert precedes the line) but is not what tests: under a Latin-1 locale 12 of the 13 rows of #1259 skip on their own premise or read their UTF-8 file as UTF-8, and the 13th, the assembly root bootstrap.py saying it could not create a branch it made, is left failing for its own issue (#1259) #1278 did for rows 9 to 12.PYTHONUTF8, or a helper intests/conftest.py). It would change what every other module reads, which this issue does not ask.The third box: three asserts, each on a file its row reads, and each judging the bytes. The reads the issue's 15 rows make go to ASCII phrases, or are written back with a comment's bytes changed, so a read as Latin-1 changes no assert's answer there (w1285a's M8, M9 and M11 passed in all three locales). A row has to see a character that is not ASCII to judge the decoding. Rows 1, 3 and 11 are the rows of M8, M9 and M11: a read-and-phrase (row 1) and two read-then-rewrite rows whose file the test writes back (rows 3 and 11). The asserts are the shortest that do it: row 1 a phrase, rows 3 and 11 a compare of bytes with the premise that the file holds a character that is not ASCII, because without the premise a file that lost its non-ASCII text (a template edited to plain ASCII) would make the compare pass for any codec (M16 and M19 below show it).
Rejected for the third box:
read_bytes().decode("utf-8"). It judges the read only. The compare of bytes after the rewrite judges the read and the write together, and a Latin-1 read written back as UTF-8, the mutant, is exactly what it fails (M8, M11).assert source.read_bytes() == old_bytes + ...for row 11. This was the first version. A mutant that read the file as Latin-1 made it fail, and pytest then spent more than four minutes of CPU before it reported the failure (stopped by me with TERM, no result). The cause is pytest's diff of the two values for the failing==,validate-family.pybeing 268 KB:_compare_eq_iterableruns it at any verbosity on CI, and called alone on those two values (_compare_eq_any,-v) it had not finished whentimeoutstopped it at 150 s. The compare is now held in a name,rewritten, which the assert names, and a comment says why; the rerun of the same mutant on the new head failed in 14 to 18 seconds. Row 3's file,family.yaml, is 4.6 KB and keeps its direct compare, which fails in seconds (M8, M17).Measured
Names in parentheses below, such as
cmpids.py,reads.py,importers.txtandlogs/, are files in lane 1's scratch, not in this PR.Python 3.12.3, pytest 9.1.1, git 2.43.0, in the probe environment (a stub
ghfirst on PATH,GH_TOKEN=bogus-w1285afor main's and the first head's runs andbogus-w1285bfor the runs of this round, an emptyGH_CONFIG_DIR,GIT_CONFIG_GLOBALset to the null device,GIT_SSH_COMMAND=false,EMAILset,LANES_LANEunset). A Python started in each locale reportsascii,utf-8andiso8859-1forsys.getfilesystemencoding()(ASCII isLC_ALL=C PYTHONUTF8=0 PYTHONCOERCECLOCALE=0; UTF-8 isLC_ALL=C.UTF-8; Latin-1 isLOCPATHof the build lane 1's writers made withlocaledef -c,LC_ALL=en_US.ISO-8859-1,PYTHONUTF8=0, astests/test_adopt_one_byte_locale.py'slatin1fixture builds it). The host ran other lanes' tests (load average 25 to 55), so the times are not comparable with a quiet run's.The two files,
-v -rfs, on main3add78e1c(w1285a's runs) and on headbd84004f3(this round's runs):16 failed, 221 passed, 6 skipped in 160.97s236 passed, 7 skipped in 204.28s243 passed in 198.24s243 passed in 219.47s236 passed, 7 skipped in 166.32s236 passed, 7 skipped in 200.97sBy test id, the 243 ids of the two files (
FAILED,PASSED,SKIPPED):test_init_copies_the_siblings_utility_and_the_makefile_targettest_the_holder_says_what_its_bytes_aretest_validate_finds_a_pin_that_disagrees_with_the_gitlinktest_validate_accepts_a_member_bump_that_is_staged_but_not_committedtest_validate_finds_a_digest_that_does_not_recomputetest_validate_finds_an_id_that_is_not_the_project_mounted_theretest_validate_finds_an_edited_shape_copytest_bump_to_the_commit_already_pinned_leaves_an_uncommitted_edit_alonetest_bump_finishes_a_bump_an_earlier_run_staged_and_did_not_committest_bump_to_the_pinned_commit_repairs_a_digest_that_does_not_recomputetest_update_shape_reads_a_family_root_and_mirrors_into_family_yamltest_update_shape_moves_the_holder_readmes_shape_line_with_the_pintest_update_shape_with_no_shape_block_names_the_family_validatortest_no_members_still_finds_an_edited_copytest_siblings_skips_a_clone_whose_project_id_is_not_the_rowstest_a_printable_pin_source_that_is_not_a_repository_is_named_as_typed[caf\xe9](test_pinned_copy_text.py)Compared id by id with a script over the saved
-voutput of the runs (cmpids.py): main against head, 16 ids change under ASCII, none under UTF-8, none under Latin-1, and the 243 ids are the same set in all six runs; head againstd4f5283b0, no id changes in any locale, and each locale's saved list (the sortedid OUTCOMElines of the-voutput) is byte for byte the one ofd4f5283b0(equalsha256). The skips main already had are unchanged: under ASCII 6 (tests/conftest.py:1323, the stdout cannot spell U+2014 or U+2192), under Latin-1 7 (the same 6 andtests/conftest.py:1334,Co-Authored-By: Jos\xe9). The one new skip is the guard's,tests/conftest.py:1281, under ASCII only.The other reads in
tests/test_family.pyEvery
read_text(),write_text(),read_bytes(),open()and.decode()of the file, at head (anastscan of the file,reads.py), after the 18 and 13 of the first commit:read_text(encoding=...)"utf-8"read_text(), leftwrite_text(..., encoding=...)"utf-8"write_text(), leftread_bytes()d4f5283b0; this round adds 4:beforeand the compare in row 3,old_bytesandrewrittenin row 11).decode("utf-8")readme_shape_lines(before.decode(...)), andafter, in row 12)open()of a fileFamily.open(holder)is not one)The 5 bare reads left, by head's line (the same five as at
d4f5283b0, 12 and 25 lines further down):(member / "NOTES.md").read_text() == "edited in place\n""edited in place\n""IRSS" not in (holder / ".gitmodules").read_text()remove, holdingmembers/IRRSand the url of the fixture's owntmp_path, which is ASCII under pytest's default basetemp; the assert asks for an ASCII word absent. Left (see Known limits)(sibling / "SCRATCH.md").read_text() == "uncommitted work\n"(wrong / "MINE.md").read_text() == "somebody else's work\n"(estate["folder"] / "IRRS" / "notes.md").read_text() == "mine\n"The 10 bare writes left, by head's line, are
113,1070,1229,1230,1429,1990,2097,2120,2140and2406; each writes an ASCII literal (NOTES.md,MOVED.md,UNTRACKED.md,TRAILED.md,MINE.md,SCRATCH.md,notes.md, and the.gitmodulestext ofMEMBERS = ("IRRS", "IRSS")), checked byast.get_source_segment(...).isascii()atd4f5283b0(the lines have only moved since). The file has two non-ASCII string literals in its code, both asserted on purpose: the dash intest_init_writes_the_holders_agent_files's"Read AGENTS-shape.md first ..."(line 364; that row already reads withencoding="utf-8") and the dash of row 1's phrase (line 323, this round's); line 1531'sJos\udce9is an ASCII escape, and every other non-ASCII character is in a docstring or a comment.Mutants
Twenty-five mutants, each applied alone in a scratch clone of head (
bd84004f3,git clone --shared, fast-forwarded fromd4f5283b0), run over the ids of the mutated row only, under ASCII, UTF-8 and Latin-1. M1 to M14 are w1285a's, run again on this head (M5's and M11's anchor changed with row 11's rewrite, which now ends+ appended,); M15 to M25 are new. After each, every file the mutant touched was written back fromgit cat-file blob HEAD:<path>by a script andgit hash-objectprinted the blob at HEAD,EQUALeach time (26 restores, one for each file a mutant touched,logs/mut-restore.out):4a7820b030d62fd04775364dd181707e89e7ee90fortests/test_family.py,376016906807c5b76f8fdb5833c76682443083d7fortests/test_pinned_copy_text.py,8c73c8cffae0f1087ec107c3820753ed3507bdc6fortemplates/family-root/family.yaml,b6b863204f185f64de4528fc5e4552c5b69114b4fortemplates/family-root/Makefileandc7be50543f9a9083ef3fde039aa3abb72145aad6fortemplates/family-root/scripts/validate-family.py. The clone ended with a cleangit status, and this PR's worktree was never mutated. (M15, M16, M18, M19 and M22 change a template in the clone, not in the PR: they make a file the row reads all ASCII.)1 failed in 7.32s1 passed in 8.56s1 failed in 7.67s1 failed in 7.55s1 passed in 7.65s1 passed in 10.47s1 failed in 10.60s1 passed in 9.54s1 failed in 10.02s1 failed in 9.54s1 passed in 8.66s1 failed in 7.67s1 failed in 14.81s1 passed in 15.57s1 failed in 10.73s1 failed in 14.06s1 passed in 13.90s1 passed in 13.00s1 failed in 11.27s1 passed in 10.34s1 passed in 11.83s1 failed in 13.98s1 failed in 11.89s1 failed in 12.71s1 failed in 11.03s1 failed in 10.54s1 failed in 13.01s1 failed in 7.76s1 failed in 7.89s1 failed in 7.42s1 failed in 17.60s1 failed in 14.26s1 failed in 13.97s1 failed, 1 passed in 2.04s2 passed in 2.14s2 passed in 2.12s1 failed, 1 passed in 2.40s2 passed in 2.04s2 passed in 2.60stest_init_writes_the_holders_agent_files(none of the 16, not changed here): its AGENTS.md read as Latin-11 failed in 10.69s1 failed in 9.56s1 failed in 7.46s1 failed in 13.28s1 failed in 12.36s1 failed in 10.88s1 passed in 13.80s1 passed in 8.81s1 passed in 7.69sbeforein place of the replaced bytes1 failed in 8.34s1 failed in 7.57s1 failed in 7.16s1 failed in 10.92s1 failed in 10.01s1 failed in 9.55s1 passed in 12.56s1 passed in 12.57s1 passed in 10.86s1 failed in 9.24s1 failed in 9.34s1 failed in 10.91s1 failed in 12.39s1 failed in 11.33s1 failed in 9.95s1 failed in 9.85s1 failed in 9.94s1 failed in 8.91s1 failed in 10.14s1 failed in 9.94s1 failed in 9.63s1 failed in 11.85s1 failed in 8.89s1 failed in 8.53s1 passed in 8.14s1 passed in 7.10s1 passed in 7.47s22 of 22 mutants killed; the 3 controls, M16, M19 and M25, survive, as they must. M8, M9 and M11, the three that survived in every locale on
d4f5283b0, now fail in all three, each on the assert meant for it: M8 on row 3's bytes assert, M9 on row 1's phrase, M11 on row 11's bytes assert (logs/mut-M8-*.log,mut-M9-*.logandmut-M11-*.loghold each message). The issue's third box is met: a mutant that reads one of the files as Latin-1 fails the row, for three files.d4f5283b0, are now killed under Latin-1 too: the bare read decodes the file as Latin-1 and the new assert sees it. M4 (the write back, which Latin-1 cannot encode), M10, M12, M13 and M14 are as they were; M2, M6 and M7 are killed under ASCII only, as before.-) with the premise kept: row 3 and row 11 fail on the premise (fixture drift: ...) in all three locales. M16 and M19 make the same file all ASCII, drop the premise, and add M8's or M11's read as Latin-1: both pass in all three locales. They are the controls that show why the premise is there: a file that has lost its non-ASCII text lets a Latin-1 read through the bytes compare, so the premise is what keeps that compare judging the decoding. M19's first version made the template ASCII and failed in all three locales for another reason, after the new asserts had passed (the holder'svalidate-family.pywas then not a verbatim copy of the pinned commit, soupdate-shape.py checkdid not sayupstream-changed: the fixture, not the asserts); it was replaced by a version in which the test makes the upstream file ASCII itself, and the first logs are kept aslogs/mut-M19-v1-*.log.beforeand the old bytes in place of the rewritten bytes: both fail in all three locales, so each compares with the rewrite and not with the file as it was. M23 and M24 drop each premise from the real row, the file left as it is, with M8's and M11's read as Latin-1: both fail in all three, so it is the bytes assert, and not the premise, that kills a Latin-1 read.-for the U+2014, against a Makefile that holds the U+2014, and M22 makes the Makefile ASCII with the phrase kept: both fail in all three locales..gitattributesread as Latin-1 passes in all three locales: no assert of that row sees a character that is not ASCII, as for every read the three asserts do not cover (see Known limits).M4 and M10 are the write side alone: under UTF-8 M4 passes and M10 fails, so the Latin-1 write is the one a UTF-8 run sees. M12 and M13 show the guard skips because of the name: dropped, or asked of the all-ASCII
"cafe",[caf\xe9]fails under ASCII again, and[plain]passes either way.Tests
In the probe environment, one pytest call at a time, one basetemp per slot,
df -h /workspaceanddf -i /checked first by the runner (it stops under 12G free or above 90% of inodes; 32G free at 16:21Z and 31G at 16:26Z, 38% of the root's inodes in use, 97% of/workspaceused). Python 3.12.3. w1285a's calls 1 to 6 and 49 to 63 are kept from its round (the second group as calls 102 to 116 below); calls 7 to 101 are this round's.3add78e1c16 failed, 221 passed, 6 skipped in 160.97s243 passed in 198.24s236 passed, 7 skipped in 166.32sd4f5283b0236 passed, 7 skipped in 180.38sd4f5283b0243 passed in 198.49sd4f5283b0236 passed, 7 skipped in 194.95s84e006269's tree before its commit3 passed in 10.23s3 passed in 12.17s3 passed in 11.17s84e006269236 passed, 7 skipped in 180.42s84e006269243 passed in 182.62s84e006269236 passed, 7 skipped in 168.86s84e006269(7 calls)1 failedin all three (6.6 to 8.2s); M11's ASCII call was stopped by me (TERM) after about 4.7 minutes of CPU in pytest's diff of the bytes assert, no result (see Why), and so was the chain of mutantsbd84004f3before its commit3 passed in 17.47s3 passed in 16.70s3 passed in 19.51sbd84004f3236 passed, 7 skipped in 204.28s243 passed in 219.47s236 passed, 7 skipped in 200.97stests/test_path_guard_hygiene.py tests/test_no_auto_maintenance.py tests/test_repo_hygiene.py, UTF-8, head280 passed, 2 skipped in 46.66sd4f5283b0(w1285a's calls 49 to 63)3298 passed, 28 skippedin all, no failure and no error (see the importers below)git grep -l -E 'from test_family import|from test_pinned_copy_text import|import test_family|import test_pinned_copy_text' -- tests/lists 74 files (importers.txt, not empty), 3326 tests by--collect-only(3326 tests collected in 21.11s); two of them,tests/test_command_scopes_built_at_the_test.pyandtests/test_family_config_appends.py, only nameimport test_family_<other>and import nothing from the two files, and they were run too. The 74 files were split by a script into shards, passed withxargs -d '\n' -a, and every one ran once:412 passed, 6 skipped,419 passedand409 passed, 7 skipped(shards 0 to 2), then198 passed, 14 skipped,207 passed, 1 skipped,206 passed,209 passed,212 passed,205 passed,208 passed,204 passed,209 passedand200 passed(shards B0 to B9), together3298 passed, 28 skipped, under UTF-8 only, ond4f5283b0(w1285a, with one shard stopped and re-split, as its body said). They were not run again onbd84004f3: the two commits since change three test bodies oftests/test_family.pyand no fixture, helper or constant, andgit grepfinds the three test names nowhere else in the repository, so no importer reaches the changed lines.tests/test_shape_doctor.pyandtests/test_scaffold_leg_path_collisions.py(noadopt-project.pychange), the doc trio (no doc, code or--helpchange), and the other files that fail under ASCII on main (the issue's fourth box).git diff --check 3add78e1c bd84004f3is clean, one added line holds a character that is not ASCII (row 1's phrase, on purpose) and none is over 79 characters.#1171 and #1310
#1171 (lane 4's, v1.2.1, 21st in the landing order, head
ce1dd661fa1e295581d7ef55d9d1162e57669d48) changestests/test_pinned_copy_text.pytoo: +6 -2, one hunk, atNEWER_THAN_A_KEPT_LIBRARY(about line 800), where this PR's two hunks are at the import and the row (head's lines 48 and 675). It does not touchtests/test_family.py. Each command printed as run, with its tree and exit code:Clean (
origin/mainhere isdc9a0bcc7). The mergedtests/test_pinned_copy_text.pyholds both changes, and itstests/test_family.pyis this PR's blob. The tests were not run on that merge. r1315a checked that no other open PR changestests/test_family.py. Lane 1's own #1300 and #1302 change other files.#1310 (lane 5's, v1.2.2, a draft, head
daaa5ad8a30101fd08d7a52b72966b89980e6772) changestests/test_pinned_copy_text.pyas well: +42 -7, at about lines 51, 65, 778 to 796 and 836. This PR's hunks there are at 47 to 49 and 673 to 675. r1315a measured the merge (#1315 issuecomment-6774780440):On a detached worktree of that merge,
tests/test_pinned_copy_text.pygives82 passed, 7 skippedunder ASCII and89 passedunder UTF-8, and[caf\xe9]still skips under ASCII. Whichever of #1171, #1310 and this PR lands later is gated again over the merge.Review round 1 (r1315a)
r1315a's independent review (#1315 issuecomment-6774780440) reads READY at
bd84004f3, with no MUST. Its SHOULD-1 and NIT-2 to NIT-4 concern this body, and lane 1 took them here, with no change to the code:check_pinname the path asshown_pathdoes (#1288) #1310 merge above.NIT-1 (assert the dash alone) and NIT-5 (rename
rewritten) are optional wording of the code, and are left for a later round.Known limits
README.mdandAGENTS-shape.md, and row 11's copiedvalidate-family.py) goes to an ASCII phrase or is not compared, so a read of it as Latin-1 changes no assert's answer there. M25, row 2's.gitattributes, is measured and survives; the others are read from the code, not run. They fail under ASCII if the keyword goes (M1 to M7), which is what the issue's first box asks. The third box is met for the three files above, not for each file.AGENTS.md. The phrase ends where the comment's line wraps, so a reflow of the paragraph would also break it. An assert of the dash alone ("\u2014" in makefile) would not, and is r1315a's NIT-1, left for a later round.write_text(). On Windowswrite_text()translates a line feed, so these two asserts would compare a file with its line ends changed; they were not run there. Thetestsjob runs onubuntu-24.04. The Windows job,tests-git-bash, runs onlytests/test_openreposhape_command.py,tests/test_install_sh.pyandtests/test_install_sh_filter_repo.py, and none of them imports either file..gitmodulesholds the fixture's tmp path. A runner whose basetemp is not ASCII would put its bytes there, and that read would then need the keyword; its row is not one of the 16.bytes.isascii()is Python 3.7's).read_text()there reads in the locale's code page (cp1252 on Windows), where the reading, not run, is that it also returns mojibake; the keyword makes it read UTF-8 everywhere. The CRLF translationwrite_text()does on Windows is as before.d4f5283b0. No importer was run under ASCII or Latin-1, nor on the two later commits (see Tests).in NNsin the tables is not a speed.Not in this PR
tests/conftest.py.tracking_branchtb\udcffxis made with HEAD on it at the pin, but_head_ofreads the ref as another text, so it sayscould not create branch, thengit refused to check it out, both false (#1259 row 13) #1283's row, and tests: 50 tests in 12 files fail under a Latin-1 locale before and after #1209 (ten of them rows oftest_stdout_encoding.py), and whether each is a test that cannot judge under a one-byte locale or a tool defect is not measured (W1196's leftovers; follows #1196) #1214's one-byte-locale rows.Lane: openreposhape-1 (openRepoShape-1)
🤖 Generated with Claude Code