-
-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bug: Test suite fails on Windows #22
Comments
Hi @dalito, thanks for the report. I'm aware of the issue, but wasn't able to reproduce it in CI (GitHub's Windows runners). I'm not willing to spend more time debugging Windows issues. The possible solutions are:
Let me know what you think 🙂 |
I think that it would be good to require less fiddling with environment variables because they affect programs run with duty. For example pytest works fine on Windows without setting any environment variable. But if these environment variables are set for duty/failprint, pytest does no longer print correct unicode to the console. But I very well understand
especially after reading pawamoy/duty#23 of @blairconrad. So I spent a little time. 😃 From my analysis there are three problems:
...looking forward to switching to utf8 on Windows by default in Python 3.15. |
Here is a demo how Capture is affected by import os
import subprocess
import sys
from failprint.capture import Capture, CaptureManager
def print_things() -> None:
print("1ö")
sys.stderr.write("2ä\n")
os.system("echo 3ß")
if sys.platform == "win32":
subprocess.run(["cmd.exe", "/c", "echo 4ü >&2"], shell=True)
else:
subprocess.run(["sh", "-c", "echo 4ü >&2"], shell=True)
print("Capturing with Capture.NONE")
with Capture.NONE.here() as captured:
print_things()
if captured._output is not None:
print(f"{captured}")
print("\n...done with Capture.NONE!")
captured = None
with CaptureManager(Capture.BOTH) as captured:
print_things()
print(f"\nCapturing with Capture.BOTH:\n{captured}")
print(f"Done!") Log from powershell 7.4.x on Windows 10 (codepage 850): (failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> $Env:PYTHONLEGACYWINDOWSSTDIO
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> py .\capture_problem.py
Capturing with Capture.NONE
1ö
2ä
3ß
4ü
...done with Capture.NONE!
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> $Env:PYTHONLEGACYWINDOWSSTDIO=1
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> $Env:PYTHONLEGACYWINDOWSSTDIO
1
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> py .\capture_problem.py
Capturing with Capture.NONE
1÷
2õ
3ß
4ü
...done with Capture.NONE!
Traceback (most recent call last):
File "C:\Users\david\MyProg\gh-dalito\failprint\capture_problem.py", line 28, in <module>
with CaptureManager(Capture.BOTH) as captured:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\david\MyProg\gh-dalito\failprint\src\failprint\capture.py", line 185, in __exit__
self._output = self._temp_file.read()
^^^^^^^^^^^^^^^^^^^^^^
File "C:\dev\Python312\Lib\tempfile.py", line 499, in func_wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "<frozen codecs>", line 322, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf6 in position 1: invalid start byte
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> $Env:PYTHONIOENCODING="cp850"
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> py capture_problem.py
Capturing with Capture.NONE
1ö
2ä
3ß
4ü
...done with Capture.NONE!
Traceback (most recent call last):
File "C:\Users\david\MyProg\gh-dalito\failprint\capture_problem.py", line 28, in <module>
with CaptureManager(Capture.BOTH) as captured:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\david\MyProg\gh-dalito\failprint\src\failprint\capture.py", line 185, in __exit__
self._output = self._temp_file.read()
^^^^^^^^^^^^^^^^^^^^^^
File "C:\dev\Python312\Lib\tempfile.py", line 499, in func_wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "<frozen codecs>", line 322, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x94 in position 1: invalid start byte
(failprint) PS C:\Users\david\MyProg\gh-dalito\failprint> Setting PYTHONLEGACYWINDOWSSTDIO leads to incorrect unicode with |
This is without any environment variable set. Attaching a debugger to my script above shows that (failprint) λ py capture_problem.py
Capturing with Capture.NONE
1ö
2ä
3ß
4ü
...done with Capture.NONE!
1ö
Traceback (most recent call last):
File "C:\Users\david\MyProg\gh-dalito\failprint\capture_problem.py", line 29, in <module>
print_things()
File "C:\Users\david\MyProg\gh-dalito\failprint\capture_problem.py", line 9, in print_things
print("1ö")
OSError: [WinError 6] Das Handle ist ungültig The file descriptor is 1 (determined by |
Thank you for doing this hard investigation work @dalito. I suppose we could look into how Pytest handles capturing output at the file descriptor level (it has a fixture for that indeed). |
I tried a bit more with some success. The code is in the branch https://github.com/dalito/failprint/tree/windows-encoding-fix With this branch the tests pass locally on Win10/Win11. The GitHub runner shows some strange behavior but passes the tests here https://github.com/dalito/failprint/actions/runs/12572960190 - The strange behavior is that With the demo script above, the first two captured lines give utf8 the next two cp850 encoded output (on German Windows 10/11). Therefore, I capture binary streams and try to decode the mess afterwards (which is hacky). The os.dup2 use which leads to a closed file handle (and the What I did is too hacky and too sensitive to minor differences to be used here. Now, after finding out more, I don't believe there is a good general solution for windows at the moment. I will stop my work on this. At least I learned quite a bit. |
OK, thanks for all your efforts. This will be invaluable if someone wants to pick it up from there 🙂 What I can offer is that we switch back to the old way of capturing output for Python callables, for Windows only: patching I initially didn't want to do that but seeing all the efforts you've made with @blairconrad, I think it's only fair 😄 |
Yes this way was also suggested by the C-Python devs. Let me know when I should try something out. |
Thanks for thinking of me, @pawamoy. For me, it looks like patching sys.std{out,err,in} would be an improvement for Windows users. Heck, with that behaviour, you and I probably never would have had to interact at all (which would be a loss, since I've had a good time, but also technically a success from the project's point of view). If you're particularly worried about negative effects, the behaviour could default to patching, with a command-line or environment variable override. But that feels like a lot of work for maybe no reason; I'd probably just start patching and wait for the fallout. And, like @dalito (nice work, BTW), I'm also available to test, code, review docs, whatever. |
Great, I opened #23. Closing this in favor of it 🙂 |
Off-topic, but kinda relevant 🤣 https://blog.orange.tw/posts/2025-01-worstfit-unveiling-hidden-transformers-in-windows-ansi/ |
Description of the bug
Running the test suite fails on Windows 10 or 11 (German locale). The failure is only obvious when running pytest directly.
It does not matter if it is run in powershell, cmd or cmder. The crash always happens in the test
test_capture_function_and_subprocess_output object address
.The same test fails when the test are run in bash shell from git for windows. However, it fails with an assertion error.
To Reproduce
The assertion error from the bash shell (probably unrelated):
Additional context
Environment information
The text was updated successfully, but these errors were encountered: