fix(diffStix): write the changelog markdown as UTF-8 - #246
Open
ppcvote wants to merge 1 commit into
Open
Conversation
`get_new_changelog_md` opened the markdown file without an encoding, so
CPython fell back to the locale codec. ATT&CK changelog text is not ASCII, so
on any machine whose locale encoding is not UTF-8 the write raises:
UnicodeEncodeError: 'cp950' codec can't encode character '\u041e'
changelog_helper.py:2433 in get_new_changelog_md
Reproduced on Windows with a cp950 console; cp1252, the default on en-US
Windows, fails the same way on Cyrillic. The write sits at the top of the
output block, so the exception also skips the HTML, detailed HTML, layer and
JSON writers, leaving an empty markdown file and no other artifacts.
This is an oversight rather than a policy: the sibling writers in the same
module at lines 1849 and 1955 both pass `encoding="utf-8"` already.
The remaining bare opens in this module (1861, 1866, 1871, 2465) go through
`json.dump`, whose `ensure_ascii` default keeps their payload ASCII, so they
do not raise today and are left alone.
The three test-side `read_text()` calls are given the matching encoding.
Before this change the round trip was symmetric by accident, locale-encoded on
both ends; once the writer is UTF-8 the readers have to say so, or
`file_content == markdown_result` compares a mis-decoded string.
The new test drives the writer in a subprocess under
`-X warn_default_encoding -W error::EncodingWarning`, rather than asserting on
a round trip. A round trip passes on an unfixed tree wherever the locale
encoding is already UTF-8, so on Linux CI it would have gone green either way
and pinned nothing. Under PEP 597 it fails on every platform if the encoding
argument goes missing again.
Verified: fails before, passes after. `tests/changelog` has the same 7 failures
before and after this change, all in `cli/` and all pre-existing Windows
environment assumptions such as `pytest.raises(PermissionError)`.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What breaks
get_new_changelog_mdopens the markdown file without an encoding, so CPython falls back to the locale codec. ATT&CK changelog text is not ASCII, so the write raises on any machine whose locale encoding is not UTF-8:Reproduced on Windows with a cp950 console. cp1252, the default on en-US Windows, fails the same way on Cyrillic. Linux and macOS default to UTF-8, which is why CI has never seen it.
The blast radius is larger than the one file. That write sits at the top of the output block in
get_new_changelog_md, so the exception also skips the HTML writer, the detailed HTML writer, the layer writers and the JSON writer. A run ends with an empty markdown file and none of the other artifacts.This looks like an oversight rather than a decision: the sibling writers in the same module already pass the encoding explicitly, at line 1849 and line 1955.
The change
One argument at
changelog_helper.py:2432:The other bare opens in this module are deliberately left alone. Lines 1861, 1866, 1871 and 2465 also open without an encoding, but they write through
json.dump, whoseensure_asciidefault keeps the payload ASCII, so they do not raise today. Happy to include them if you would rather the module be clean under-X warn_default_encoding, but they are a different question from this bug.Three
read_text()calls in the tests get the matching encoding. Before this change the round trip was symmetric by accident, locale-encoded at both ends. Once the writer is UTF-8, a reader that does not say so decodes with the locale codec, andfile_content == markdown_resultcompares a mis-decoded string against the original. On my machine that turned two passing tests red, which is how I found them; on Linux CI they would have stayed green and the assertion would have quietly stopped meaning anything.About the test
It drives the writer in a subprocess under
-X warn_default_encoding -W error::EncodingWarningrather than asserting on a round trip.A round trip is the obvious test and it is useless here: it passes on an unfixed tree wherever the locale encoding is already UTF-8, so it would have gone green on your CI both before and after the fix. I checked that before writing this one. Under PEP 597 any
open()that relies on the locale codec becomes an error, so the test fails on every platform if the encoding argument goes missing again, and the traceback names the exact line.The driver passes
layers=[]andjson_file=Noneso only the markdown writer is exercised, keeping the assertion pointed at one thing.Verification
main, passes with the fix. Confirmed in both directions by stashing the change and re-running.tests/changelog: 7 failures before this change and the same 7 after, none of them new. They are all incli/and are pre-existing Windows environment assumptions, for example apytest.raises(PermissionError)that Windows does not raise.ruff checkandruff format --checkclean on all four touched files.