Commit Graph

16551 Commits

Author SHA1 Message Date
Shahar Naveh
68a0bc030b Update test_isinstance.py to 3.14.4 (#7806) 2026-05-10 00:29:05 +09:00
Shahar Naveh
edcf3002de Add test_finalization.py from 3.14.4 (#7805) 2026-05-10 00:28:50 +09:00
Shahar Naveh
67630ffbfe Update test_context.py to 3.14.4 (#7804)
* Update `test_context.py` to 3.14.4

* Mark & fix some tests

* clippy
2026-05-10 00:28:30 +09:00
Shahar Naveh
6ed17c3cb5 Update test_complex.py to 3.14.4 (#7803) 2026-05-10 00:28:13 +09:00
Shahar Naveh
002fc1122e Fix comment confusion bug in update_lib (#7802)
* Use `unittest.expectedSuccess` to mark failing tests

* Fix comment find logic

* Add test for it
2026-05-10 00:27:51 +09:00
dependabot[bot]
ae5c9119c9 Bump fast-uri from 3.0.6 to 3.1.2 in /wasm/demo (#7801)
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.0.6 to 3.1.2.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.0.6...v3.1.2)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 00:26:55 +09:00
Joshua Megnauth
4aa73ddab2 Use rustix for uname; drop uname crate (#7799) 2026-05-10 00:26:26 +09:00
Shahar Naveh
7576d68060 Update test_venv.py to 3.14.4 (#7798) 2026-05-10 00:26:05 +09:00
Joshua Megnauth
3bab7a9086 Fix sys.implementation (#7793)
`sys.version_info` and `sys.implementation` are two different versioning
schemes. `version_info` refers to the target Python version while
`implementation` refers to the implementation version.

For RustPython, `version_info` should be 3.14 because it currently
targets Python 3.14. This was correct and remains unchanged.

`implementation`, however, should be set to RustPython's version. I
updated the code to correctly get RustPython's version from the
environment. I also ensured values are hard coded at build time which
reduces the overall code that needs to be run at runtime as well as
removes some unneeded allocations.

Closes: #7770
2026-05-10 00:25:14 +09:00
dependabot[bot]
e10a27b1ae Bump openssl from 0.10.78 to 0.10.79 (#7791)
Bumps [openssl](https://github.com/rust-openssl/rust-openssl) from 0.10.78 to 0.10.79.
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.78...openssl-v0.10.79)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.79
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-06 20:44:10 +09:00
fanninpm
3b364608d9 Make determine_changes step run on ubuntu-slim (#7790) 2026-05-06 20:43:55 +09:00
fanninpm
1c4361803d Move dependencies to workspace for vm crate (#7789)
* Add dependencies from `vm` crate to workspace

* Declare dependencies as workspace = true
2026-05-06 14:03:05 +09:00
Bas Schoenmaeckers
22d4f43ad4 Add minimal capi lifecycle support (#7648)
* Add minimal capi lifecycle support

* Force enable `threading` on `stdlib`

---------

Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2026-05-06 02:02:26 +09:00
Shahar Naveh
02a2b19839 Remove unused rust impl for formatting dis output (#7660)
* Remove unused rust impl for formatting dis output

* remove `examples/dis.rs`

* Added tests

* Update lock

* Try to set snapshot dir

* Remove verbose flag

* Regenerate snapshots after #7711

* Revert "Bump insta from 1.46.3 to 1.47.2 (#7706)"

This reverts commit e6d9ea6bfe.

* Debug info

* Show diff as well

* Show debug faster

* CI: true env

* Recert CI

* Add `CI: true` in ci emv

* Reapply "Bump insta from 1.46.3 to 1.47.2 (#7706)"

This reverts commit 693ca8cbe4d7885a81162a9be31e8bb567db885a.

* simplify macro

* trim on function side

* Force insta workspace root

* fix merge
2026-05-05 08:09:35 +00:00
Jeong, YunWon
ae7ff9c481 codegen: module seed CodeInfo emits empty flags (#7782)
CPython convention: top-level module / interactive / expression code
does not carry CO_NEWLOCALS or CO_OPTIMIZED. The per-scope mapping at
enter_scope::CompilerScope::Module already returns empty flags, but
Compiler::new seeded the root CodeInfo with CodeFlags::NEWLOCALS,
forcing module code into the NEWLOCALS arm of frame.rs:725-731 so
locals were allocated as a fresh empty dict instead of being bound to
globals (the correct semantics for exec(code, globals)).

Restore the seed to empty() so it matches the per-scope mapping and
CPython's compiler_enter_scope for module scope.
2026-05-05 16:22:55 +09:00
dependabot[bot]
d877c30417 Bump https://github.com/astral-sh/ruff-pre-commit (#7779)
Bumps [https://github.com/astral-sh/ruff-pre-commit](https://github.com/astral-sh/ruff-pre-commit) from v0.15.11 to 0.15.12.
- [Release notes](https://github.com/astral-sh/ruff-pre-commit/releases)
- [Commits](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.11...v0.15.12)

---
updated-dependencies:
- dependency-name: https://github.com/astral-sh/ruff-pre-commit
  dependency-version: 0.15.12
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 14:25:08 +09:00
dependabot[bot]
8bd4c5137e Bump mwilliamson/setup-wabt-action from 3.2.0 to 4.0.0 (#7778)
Bumps [mwilliamson/setup-wabt-action](https://github.com/mwilliamson/setup-wabt-action) from 3.2.0 to 4.0.0.
- [Release notes](https://github.com/mwilliamson/setup-wabt-action/releases)
- [Commits](https://github.com/mwilliamson/setup-wabt-action/compare/v3.2.0...427f2fdd70bc4dbc2e53c2eb4f19f66162d71bd2)

---
updated-dependencies:
- dependency-name: mwilliamson/setup-wabt-action
  dependency-version: 4.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 14:24:34 +09:00
Shahar Naveh
dc12bff0f4 Move clippy and cargo-shear to a dedicated job (#7741) 2026-05-05 14:24:21 +09:00
dependabot[bot]
e1dd3d43d5 Bump github/gh-aw from 0.68.3 to 0.71.1 (#7776)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.68.3 to 0.71.1.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](ce1794953e...f01a9d118a)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.71.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 14:22:14 +09:00
dependabot[bot]
a5cebc3242 Bump the wasmtime group across 1 directory with 2 updates (#7775)
Bumps the wasmtime group with 1 update in the / directory: [cranelift-jit](https://github.com/bytecodealliance/wasmtime).


Updates `cranelift-jit` from 0.131.0 to 0.131.1
- [Release notes](https://github.com/bytecodealliance/wasmtime/releases)
- [Changelog](https://github.com/bytecodealliance/wasmtime/blob/main/RELEASES.md)
- [Commits](https://github.com/bytecodealliance/wasmtime/commits)

Updates `cranelift-module` from 0.131.0 to 0.131.1
- [Release notes](https://github.com/bytecodealliance/wasmtime/releases)
- [Changelog](https://github.com/bytecodealliance/wasmtime/blob/main/RELEASES.md)
- [Commits](https://github.com/bytecodealliance/wasmtime/commits)

---
updated-dependencies:
- dependency-name: cranelift-jit
  dependency-version: 0.131.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: wasmtime
- dependency-name: cranelift-module
  dependency-version: 0.131.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: wasmtime
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 14:22:02 +09:00
Jeong, YunWon
ad5a55c1e3 Bytecode parity: align borrow optimization with CFG traversal (#7773)
* Align CFG cleanup bytecode with CPython

* Bytecode parity: fblock unwind, fstring join, folding, scope

- compile.rs: unwind_fblock_stack returns whether a finally ran so
  return-statement emission can adjust location handling; restructure
  try/except/finally cleanup to preserve or drop boundary NOPs based on
  whether the body falls through; rework f-string lowering with
  count/join helpers; remove the per-collection-type heuristic for
  AST-level folding and defer to flowgraph passes; add several folding
  helpers and a ComprehensionLoopControl enum.
- ir.rs: re-run unary/binop folding around tuple folding, add
  reorder_conditional_scope_exit_and_jump_back_blocks and several block
  classification helpers, add MAX_STR_SIZE, change is_exit_without_lineno
  to take the block list.
- symboltable.rs: in analyze_cells, remove names owned as cells in
  function-like scopes from the parent's free set; mark lambda scope
  type explicitly.

* Refine CFG scope-exit backedge ordering
2026-05-05 14:04:12 +09:00
fanninpm
e4d35b08ea Move dependencies to workspace for common crate (#7771)
* Add dependencies from `common` crate to workspace

* Declare dependencies as workspace = true
2026-05-05 14:02:57 +09:00
Noa
02932384d6 Use cfg_select in a bunch more places (#7740) 2026-05-04 20:26:16 +00:00
Shahar Naveh
0325fd429e Opcode name (#7433) 2026-05-04 20:49:47 +09:00
Shahar Naveh
4db0aca471 fix ci (#7774)
* fix ci

* more
2026-05-04 20:41:08 +09:00
Changjoon
83002d7369 Tighten CPython parity for str format spec, %-format, and str() constructor (#7769)
Five related CPython parity gaps in `str` formatting and construction:

1. **`str(bytes, errors=...)` triggers decode mode.** Previously, only
   `encoding=` triggered decode; passing only `errors=` fell back to
   `repr()`. CPython's behavior: presence of `encoding` OR `errors`
   triggers decode mode (default UTF-8 when only `errors` is given).

2. **`'{...}'.format() IndexError wording.** Generic Rust "tuple index
   out of range" replaced with CPython's "Replacement index N out of
   range for positional args tuple".

3. **`{0:3.2s}.format('abc')` → 'ab '.** String format spec applied
   precision after width padding; CPython truncates BEFORE padding.
   Reorder the operations.

4. **`%x` / `%o` / `%X` / `%c` accept `__index__` objects.** Previously
   only `PyInt` downcast was attempted. Mirror CPython's
   PyNumber_Index dispatch via `try_index_opt`.

5. **`%d` / `%u` / `%i` error wording.** "a number is required" →
   "a real number is required" (matches CPython).

Also adds `not <type>` suffix to `%c` error messages so the type is
visible in TypeError text (matches CPython structure even without
fully-qualified names).

Verified byte-identical with CPython 3.14.4 across 25+ probes covering
the format/spec/constructor combinations. Unmasks
`test_str.test_constructor_keyword_args` and
`test_str.test_constructor_defaults`. test_str/test_bytes/test_format/
test_codecs/test_io/test_unicode_identifiers — 1,429 tests pass, 0
regressions. All 188 `extra_tests/snippets/*.py` pass under the CI
feature set.

`test_str.test_format` and `test_str.test_formatting` markers retained:
`test_format` still trips on `'{0:08s}'.format('result')` (numeric
zero-pad treated as fill+left-align by CPython for str type — separate
format-spec parser concern). `test_formatting` still trips on
`%c` error message expecting fully qualified `module.qualname` (RP
returns bare class name — separate broader concern).
2026-05-04 10:28:43 +09:00
Changjoon
90cc888f4b Validate compile() filename type and dont_inherit __bool__ protocol (#7768)
Two CPython parity gaps in `compile()` argument handling:

1. **filename rejects only non-buffer-protocol types.** CPython's
   `PyUnicode_FSDecoder` accepts `str`, `bytes`, and objects with
   `__fspath__` only — `bytearray` / `memoryview` / `array.array` raise
   TypeError. RustPython's `FsPath::TryFromObject` impl falls back to
   any buffer-protocol object and silently converts to bytes, accepting
   them.

   ```python
   >>> compile('pass', bytearray(b'file.py'), 'exec')
   # CPython 3.14.4: TypeError
   # RustPython main: <code object>  
   ```

   Change `CompileArgs::filename` from `FsPath` (which uses the
   permissive `TryFromObject` impl) to `PyObjectRef`, then call the
   strict `FsPath::try_from_path_like` at the top of `compile()`. Other
   `FsPath` consumers are unchanged.

2. **dont_inherit strict-checks the `bool` type.** CPython routes
   `dont_inherit` through `PyObject_IsTrue`, calling `__bool__` on
   arbitrary objects and propagating exceptions raised there. RustPython
   typed it `OptionalArg<bool>`, which rejects subclass-less objects at
   binding time before `__bool__` runs.

   ```python
   >>> class EvilBool:
   ...     def __bool__(self): raise ValueError('hi')
   >>> compile('pass', 'f', 'exec', dont_inherit=EvilBool())
   # CPython 3.14.4: ValueError('hi')
   # RustPython main: TypeError('Expected type bool, not EvilBool')  
   ```

   Switch to `OptionalArg<ArgIntoBool>` — already used elsewhere in
   `builtins` (`all`, `any`, `print(..., flush=...)`).

Verified byte-identical with CPython 3.14.4 across:
- 6 filename types (str, bytes, bytearray, memoryview, list, int)
- 7 dont_inherit values (True/False/1/0/""/"yes"/None) plus the
  `__bool__`-raises-ValueError case

Unmasks `Lib/test/test_compile.py`:
- `test_compile_filename`
- `test_compile_filename_refleak`

`cargo run --release -- -m test test_compile test_builtin test_compileall
test_codeop test_ast` — 703 tests pass, 0 regressions. All 188
`extra_tests/snippets/*.py` pass under the CI feature set.
2026-05-04 10:27:53 +09:00
Jiseok CHOI
cf23884656 test(str): add regression test for isprintable() unicode 15 chars, (#7766)
Fixes #7525
2026-05-04 10:27:19 +09:00
Changjoon
c3c9292c8b Match CPython wording for compile() optimize and flags validation (#7765)
CPython's `builtin_compile_impl` (Python/bltinmodule.c) accepts only
optimize ∈ {-1, 0, 1, 2}; anything else raises
`ValueError("compile(): invalid optimize value")`. The previous logic
only validated via `i32::try_into::<u8>()`, which silently accepts every
value in [0, 255], so `compile(..., optimize=3)`, `optimize=99`, etc.
were silently truncated to a u8. The error wording also had the wrong
word order.

Replace the cast-based check with a `match` against the spec range.

Adjacent: the unrecognised-flags message used American spelling
("unrecognized") and missed the colon separator. CPython uses British
"unrecognised" with a colon — match it.

Verified byte-identical with CPython 3.14.4 across 12 boundary values
for optimize and 4 cases for flags. Preserves the existing OverflowError
path for `optimize=1 << 1000` (raised at the ArgPrimitiveIndex<i32>
conversion layer, before this check).
2026-05-04 10:26:16 +09:00
Shahar Naveh
ee5e9d0001 Enable some pedantic clippy lints (#7764) 2026-05-04 10:25:37 +09:00
Changjoon
181e4e7124 Drop redundant exception type prefix from float-to-int error messages (#7763)
CPython raises `OverflowError("cannot convert float infinity to integer")`
and `ValueError("cannot convert float NaN to integer")` from
`Objects/floatobject.c::float___trunc___impl` and friends. The exception
type name is added by Python's traceback display layer; the message
itself should not duplicate it.

`try_to_bigint` was producing
`OverflowError("OverflowError: cannot convert ...")` etc., which made
`repr(e)` and any code path that inspects `str(e)` diverge from CPython.

Affects all 5 callers of `try_to_bigint`: `__int__`, `__floor__`,
`__ceil__`, `__round__` (no-arg), `__trunc__` — i.e. `int(x)`,
`math.floor/ceil/trunc(x)`, `round(x)` for non-finite floats.

Verified byte-identical with CPython 3.14.4 across 14 affected sites.
2026-05-04 10:24:08 +09:00
Shahar Naveh
eb99a8ecf1 Warn on unreachable_pub (#7762) 2026-05-04 10:18:24 +09:00
fanninpm
acc167fc44 Move dependencies to workspace for stdlib crate (#7747)
* Add dependencies from `stdlib` crate to workspace

* Declare dependencies as workspace = true
2026-05-04 09:50:21 +09:00
Changjoon
c2910c06f3 Round float at the decimal level to match CPython's _Py_dg_dtoa (#7761)
* Round float at the decimal level to match CPython's _Py_dg_dtoa

CPython's `float.__round__` (Objects/floatobject.c) routes through
`_Py_dg_dtoa` and rounds at the decimal level. The previous
`round_float_digits` multiplied by 10**ndigits and rounded at the
IEEE 754 binary level, which diverges for values that aren't exactly
representable. For example, 2.675 stores as 2.67499...; dtoa correctly
rounds it down to 2.67, but `(2.675 * 100.0).round() / 100.0` lands on
2.68 because the multiplication produces a phantom 267.5 tie that
round-half-to-even snaps up.

Rust's `{:.*}` float formatting uses dtoa-style algorithms (Grisu3 +
Dragon4 fallback) and matches CPython's `_Py_dg_dtoa` byte-for-byte.
Replace the multiply-then-round step with `format!` + `parse` for
ndigits >= 0. The ndigits < 0 path is unchanged because dividing
typical inputs by 10**|ndigits| produces genuine ties rather than
synthesizing them.

Verified byte-identical with CPython 3.14.4 over a 108-case random
fuzz plus targeted half-tie probes. Unmasks
`test_float.RoundTestCase.test_matches_float_format` and
`test_previous_round_bugs`.

* Use #[expect] with reason for float_cmp suppression

Co-authored-by: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com>

---------

Co-authored-by: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com>
2026-05-03 00:16:27 +09:00
Shahar Naveh
ac3e067230 Only run cargo check when rust code is changed (#7760)
* Only run `cargo check` when rust code is changed

* Test

* Another test

* Revert "Test"

This reverts commit 1f23cc33f5.

* Revert "Another test"

This reverts commit 50a51d5e56.

* Add extra rust related files
2026-05-03 00:15:46 +09:00
Shahar Naveh
be7841f9c1 Update test_py_compile.py to 3.14.4 (#7759) 2026-05-03 00:15:22 +09:00
Shahar Naveh
3e66fb508a Update str related tests (#7758)
* Update `string_tests.py` to 3.14.4

* Update `test_bytes.py` to 3.14.4

* Update `test_userstring.py` to 3.14.4

* Mark failing tests
2026-05-03 00:15:03 +09:00
Shahar Naveh
f2ad84a489 AnyInstruction helpers + doc imrovments (#7757) 2026-05-03 00:13:31 +09:00
Shahar Naveh
926d69b50a Add some clippy lints (#7755) 2026-05-02 11:14:23 +09:00
dependabot[bot]
8d1c68c9a0 Bump actions/setup-node from 6.3.0 to 6.4.0 (#7689)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](53b83947a5...48b55a011b)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-02 08:16:15 +09:00
Shahar Naveh
d9fff99718 Pin actions to hash (cron-ci.yaml) (#7686) 2026-05-02 08:15:43 +09:00
Shahar Naveh
0f25d145fd Make updating actions cache synchronous (#7712) 2026-05-02 08:15:14 +09:00
Shahar Naveh
7fd0da92d3 Fix libs deps check (#7732)
* Convert parsing logic to python

* Trigger job. (DROP ME BEFORE MERGE!)

* Missing EOF

* Add found modules to set

* Suggestion by @fanninpm

* Fix missing trailing slash

* Ensure no `.py` at module name

* Strip any file auffix

* Revert "Trigger job. (DROP ME BEFORE MERGE!)"

This reverts commit 8cf9651a24.

* Handle quoted args
2026-05-02 08:14:55 +09:00
Shahar Naveh
c027ffc2ba Pin marocchino/sticky-pull-request-comment action to a hash (#7751) 2026-05-01 22:58:35 +09:00
Jiseok CHOI
192ba371e4 Fix function attribute tests: __defaults__ del, __code__ free vars ch… (#7749)
* Fix function attribute tests: __defaults__ del, __code__ free vars check, bound method dir()

- Support `del func.__defaults__` and `del func.__kwdefaults__` by changing
  setter signatures to `PySetterValue<Option<...>>` so Delete is treated as
  setting the value to None (matching CPython behaviour)
- Validate free-variable count when assigning to `func.__code__`: raise
  ValueError when the new code object's freevars count doesn't match the
  existing closure size
- Add `__dir__` to `PyBoundMethod` that delegates to the underlying
  function's dir(), so attributes set on the function show up in
  `dir(bound_method)`
- Remove `@unittest.expectedFailure` from the four tests that now pass:
  test_blank_func_defaults, test_func_default_args, test___code__,
  test_dir_includes_correct_attrs

* Remove expectedFailure markers for now-passing tests
2026-05-01 22:58:16 +09:00
Shahar Naveh
d25195ed0e Update test_binascii.py to 3.14.4 (#7753)
* Update `test_binascii.py` to 3.14.4

* Mark failing test
2026-05-01 12:18:19 +00:00
Shahar Naveh
9004089fd1 Update unittest to 3.14.4 (#7752)
* Update `unittest` from 3.14.4

* Update `test_unittest` to 3.14.4

* Unmark passing test
2026-05-01 11:06:24 +00:00
dependabot[bot]
f6e2fcffe7 Bump liblzma-sys from 0.4.5 to 0.4.6 (#7744)
Bumps [liblzma-sys](https://github.com/portable-network-archive/liblzma-rs) from 0.4.5 to 0.4.6.
- [Release notes](https://github.com/portable-network-archive/liblzma-rs/releases)
- [Commits](https://github.com/portable-network-archive/liblzma-rs/compare/liblzma-sys-0.4.5...liblzma-sys-0.4.6)

---
updated-dependencies:
- dependency-name: liblzma-sys
  dependency-version: 0.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 19:49:10 +09:00
Shahar Naveh
bb77ac6284 Update test_unicodedata.py to 3.14.4 (#7750)
* Update `test_unicodedata.py` to 3.14.4

* Add test asset

* Mark failing tests

* Add `expectedSuccess` docorator

* Add support for nee decorator in patch_spec

* Reapply patches
2026-05-01 19:48:41 +09:00
Changjoon
c2141a765f Apply titlecase mapping in str.title() for uppercase digraphs (#7748)
The uppercase/titlecase branch of PyStr::title() pushed characters
unchanged when starting a new word, which left Latin Extended-B
digraphs (U+01F1 'DZ', U+01C4 'DŽ', etc.) in their uppercase form
instead of mapping them to their distinct titlecase counterparts
(U+01F2 'Dz', U+01C5 'Dž'). For ASCII letters and characters where
to_titlecase is identity this had no effect, hiding the bug for the
common case.

Mirror the lowercase branch — which already calls to_titlecase()
when starting a new word — so both branches symmetrically apply
the titlecase mapping. char::to_titlecase is identity for already-
titlecase and ASCII-uppercase characters, so existing cases stay
correct.

Also unmasks test_unicodedata.UnicodeMiscTest.test_bug_4971, which
asserts exactly this behavior (`'DŽ'.title() == 'Dž'` etc.)
and was marked expectedFailure with reason `+ Dž`.

Closes #7527 (the only example from that issue still failing on
3.14.4; the other four examples already pass on current main).
2026-05-01 19:45:55 +09:00