New subclass payload layout by youknowone · Pull Request #6319 · RustPython/RustPython
🏁 Script executed:
cd /tmp && find . -name "ssl.rs" -type f 2>/dev/null | head -5
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# First, find the ssl.rs file find . -path "*/stdlib/src/ssl.rs" -type f
Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Search for ssl.rs in the repository fd -t f "ssl.rs" | head -10
Repository: RustPython/RustPython
Length of output: 118
🏁 Script executed:
# Read the ssl.rs file around lines 1870-1881 with context sed -n '1850,1900p' ./crates/stdlib/src/ssl.rs | cat -n
Repository: RustPython/RustPython
Length of output: 2713
🏁 Script executed:
# Get more context - read from line 1850 to 1900 to see the function signature head -1900 ./crates/stdlib/src/ssl.rs | tail -60 | cat -n
Repository: RustPython/RustPython
Length of output: 3016
🏁 Script executed:
# Let's check the file size first
wc -l ./crates/stdlib/src/ssl.rsRepository: RustPython/RustPython
Length of output: 96
🏁 Script executed:
# Search for how other modules use new_os_subtype_error and errno rg -A5 "new_os_subtype_error" --type rs | head -80
Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Search for FileNotFoundError usage patterns rg -B3 -A3 "file_not_found_error" --type rs | head -100
Repository: RustPython/RustPython
Length of output: 91
🏁 Script executed:
# Check how CPython's ssl module handles file not found errors
web_searchRepository: RustPython/RustPython
Length of output: 115
🏁 Script executed:
# Search for how other modules use new_os_subtype_error rg "new_os_subtype_error" -A5 | head -100
Repository: RustPython/RustPython
Length of output: 5611
🏁 Script executed:
# Search for FileNotFoundError patterns in the codebase rg "file_not_found_error" -B3 -A3 | head -100
Repository: RustPython/RustPython
Length of output: 7049
🏁 Script executed:
# Check if there's any errno constant definition or usage patterns rg "errno.*2\|ENOENT" --max-count 20
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check how std::fs errors are handled elsewhere rg "std::fs::" | grep -i "error\|map_err" | head -20
Repository: RustPython/RustPython
Length of output: 483
🏁 Script executed:
# Check for errno/ENOENT constants rg "ENOENT|errors::" crates/vm/src/exceptions.rs | head -20
Repository: RustPython/RustPython
Length of output: 1148
🏁 Script executed:
# Look at the full socket.rs pattern for errno extraction rg -B5 -A5 "error_num\(\)" crates/stdlib/src/socket.rs | head -40
Repository: RustPython/RustPython
Length of output: 966
🏁 Script executed:
# Check the error mapping in exceptions.rs more carefully rg -B2 -A2 "ENOENT" crates/vm/src/exceptions.rs
Repository: RustPython/RustPython
Length of output: 364
🌐 Web query:
CPython ssl.load_dh_params FileNotFoundError errno Windows POSIX behavior
💡 Result:
Short answer
- ssl.SSLContext.load_dh_params(dhfile) opens the file with the C runtime and calls OpenSSL's PEM_read_DHparams; if fopen/_Py_fopen_obj fails Python exposes the OS error (FileNotFoundError / OSError) with errno. [1][2]
- On POSIX this typically behaves as you expect (fopen fails → errno = ENOENT → FileNotFoundError). On Windows the same underlying fopen can also fail for other reasons (path encoding/mbcs issues, wide/ANSI conversion, or Windows-specific fopen behavior), so you can also get FileNotFoundError or other OSError variants even when a path that looks valid to Python strings is used. This is a known class of platform differences for ssl file-loading APIs. [2][3]
Why this happens (brief technical outline)
- CPython's _ssl implementation calls _Py_fopen_obj(filepath, "rb") and then PEM_read_DHparams on the FILE*. If _Py_fopen_obj returns NULL, CPython inspects errno and raises an OSError (FileNotFoundError if errno==ENOENT). If PEM_read_DHparams returns NULL with errno==0, a different SSL error is raised. (Code and reproduction notes shown in community / source snippets). [2]
- Differences in how Windows converts Python filename objects to the C fopen call (MBCS/ANSI vs wide APIs used by Python internals, and what OpenSSL or the C runtime expects) produce platform-dependent failures; similar issues have been reported and fixed historically for other ssl file-loading functions (e.g. load_verify_locations). [3][4]
What to do
- Verify the file exists and is readable from Python (os.path.exists, open(...,'rb')). If that succeeds but load_dh_params still fails, the problem is likely an encoding/ABI/path-form issue on Windows (non-ASCII characters, reserved names, long paths). Use a plain ASCII path or a path exposed via a file descriptor opened in Python and re-create the file in a known-good location. [1][2]
- If you need a robust workaround on Windows for non-encodable paths, open the file yourself in Python (open(path,'rb')) to confirm accessibility; report/attach a minimal reproducer to the CPython issue tracker if fopen succeeds but load_dh_params still fails. See linked reports about related Windows path/encoding behavior in the ssl module. [3][4]
Sources
- CPython ssl docs (SSLContext.load_dh_params). [1]
- Community excerpt showing the _ssl.c implementation calling _Py_fopen_obj and handling errno / PEM_read_DHparams. [2]
- Python bug tracker / issue about Windows path/encoding differences for ssl.load_verify_locations (same class of problem). [3]
- Python ssl module documentation note about platform-dependent behavior (OpenSSL/OS differences). [4]
References
[1] Python ssl docs — SSLContext.load_dh_params. (docs.python.org)
[2] Community discussion / snippet showing _ssl__SSLContext_load_dh_params implementation and errno handling. (community.letsencrypt.org)
[3] Python issue: SSLContext.load_verify_locations Windows encoding/path behavior. (bugs.python.org)
[4] Python ssl module notes on platform/OpenSSL differences. (docs.python.org)
Citations:
- 1: https://docs.python.org/library/ssl.html?utm_source=openai
- 2: https://community.letsencrypt.org/t/renew-failure-unknown-error-ssl-c-3057/153930?utm_source=openai
- 3: https://bugs.python.org/issue27354?utm_source=openai
- 4: https://docs.python.org/library/ssl.html?utm_source=openai
🏁 Script executed:
# Check the stat module mentioned in learnings for platform-specific handling pattern rg -B5 -A5 "SF_SUPPORTED\|SF_SYNTHETIC" crates/stdlib/src/stat.rs | head -50
Repository: RustPython/RustPython
Length of output: 130
🏁 Script executed:
# Check if there's a utility function to convert std::io::Error to proper errno rg -B3 -A3 "e.kind\(\)" crates/stdlib/src/ | head -60
Repository: RustPython/RustPython
Length of output: 3925
🏁 Script executed:
# Check how into_pyexception() works rg -B3 -A5 "into_pyexception" crates/stdlib/src/ssl.rs | head -60
Repository: RustPython/RustPython
Length of output: 1890
🏁 Script executed:
# Check the trait definition or implementation rg "into_pyexception" crates/ | grep -E "(fn|impl|trait)" | head -20
Repository: RustPython/RustPython
Length of output: 1166
🏁 Script executed:
# Look at the specific load_dh_params code more carefully sed -n '1840,1895p' ./crates/stdlib/src/ssl.rs | cat -n
Repository: RustPython/RustPython
Length of output: 2880
🏁 Script executed:
# Check the into_pyexception implementation for std::io::Error rg -B5 -A15 "impl.*into_pyexception.*std::io::Error|fn into_pyexception.*io::Error" crates/
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Check os.rs implementation sed -n '1,100p' crates/vm/src/stdlib/os.rs | grep -A20 "into_pyexception"
Repository: RustPython/RustPython
Length of output: 982
🏁 Script executed:
# Find the exact implementation in os.rs rg -B2 -A10 "fn into_pyexception" crates/vm/src/stdlib/os.rs
Repository: RustPython/RustPython
Length of output: 668
🏁 Script executed:
# Find the io::Error into_pyexception implementation rg -B5 -A15 "impl.*IntoPyException.*for.*io::Error" crates/
Repository: RustPython/RustPython
Length of output: 1183
🏁 Script executed:
# Check the convert/to_pyobject.rs file for io::Error rg -B5 -A20 "io::Error" crates/vm/src/convert/to_pyobject.rs | head -80
Repository: RustPython/RustPython
Length of output: 47
🏁 Script executed:
# Find to_pyexception implementation rg -B5 -A20 "fn to_pyexception" crates/vm/src/stdlib/io.rs | head -100
Repository: RustPython/RustPython
Length of output: 1160
🏁 Script executed:
# Check the full to_pyexception implementation to see how it maps errno to exception types rg -B5 -A50 "fn to_pyexception" crates/vm/src/stdlib/io.rs | tail -80
Repository: RustPython/RustPython
Length of output: 2135
Refactor to eliminate the existence check and use proper error handling. Instead of checking Path::exists() and hardcoding errno=2, try to read the file directly and match on std::io::ErrorKind to convert errors properly via into_pyexception(). This approach: