Compare commits

...

1731 Commits

Author SHA1 Message Date
Jeong, YunWon
18657330c9 Drop old PyObjectRef outside type lock to prevent deadlock
Dropping values inside with_type_lock can trigger weakref callbacks,
which may access attributes (LOAD_ATTR specialization) and re-acquire
the non-reentrant type mutex, causing deadlock.

Return old values from lock closures so they drop after lock release.
2026-03-21 00:39:45 +09:00
Jeong, YunWon
ea2d66e799 type lock 2026-03-20 22:47:55 +09:00
Jeong, YunWon
38de7462c0 Fix Constants newtype usage in init_cleanup_code 2026-03-20 22:47:55 +09:00
Jeong, YunWon
cb2db07463 Extract datastack_frame_size_bytes_for_code, skip monitoring for init_cleanup frames, guard trace dispatch
- Extract datastack_frame_size_bytes_for_code as free function, use it
  to compute init_cleanup stack bytes instead of hardcoded constant
- Add monitoring_disabled_for_code to skip instrumentation for
  synthetic init_cleanup code object in RESUME and execute_instrumented
- Add is_trace_event guard so profile-only events skip trace_func dispatch
- Reformat core.rs (rustfmt)
2026-03-20 22:47:55 +09:00
Jeong, YunWon
76e6ece941 address review: check datastack space for extra_bytes, require CO_OPTIMIZED in vectorcall fast path 2026-03-20 22:47:55 +09:00
Jeong, YunWon
fb0dfa102c address review: invalidate init cache on type modification, add cspell words 2026-03-20 22:47:55 +09:00
Jeong, YunWon
9df4787aed Align call-init frame flow and spec cache atomic ordering 2026-03-20 22:47:55 +09:00
Jeong, YunWon
e19335e8f2 Tighten CALL_ALLOC_AND_ENTER_INIT stack-space guard 2026-03-20 22:47:55 +09:00
Jeong, YunWon
b3daabf169 Align type _spec_cache and latin1 singleton string paths 2026-03-20 22:47:55 +09:00
Jeong, YunWon
471fe551fa Align BINARY_OP_EXTEND with CPython descriptor cache model 2026-03-20 22:47:55 +09:00
Lee Dogeon
38f742aa8d Implement Hashable for PyBoundMethod (#7474) 2026-03-20 13:15:40 +00:00
김은빈
1b9dc4ef14 Detect list mutation during sort even when list length is unchanged (#7432)
* Detect list mutation during sort even when list length is unchanged

* Use mutation counter instead of capacity check for sort mutation detection

The capacity heuristic missed mutations when `clear()` reset capacity to
0 via `mem::take`. An AtomicU32 counter on PyList, incremented in
`borrow_vec_mut()`, reliably detects all mutations during sort.

* Hold write guard during sort mutation counter reads

* Fix list mutation counter race in `borrow_vec_mut`
2026-03-20 21:40:54 +09:00
Shahar Naveh
4141e74e14 Add zizmor CI (#7463) 2026-03-20 21:37:12 +09:00
Shahar Naveh
2ef77f82e1 Run CI tests with max processes (#7254)
* Run CI witb max cores

* Step for flaky MP tests

* Add `test_class` to flkay mp tests
2026-03-20 20:35:18 +09:00
Jeong, YunWon
8be5230a9b Fix allow_threads and EINTR handling (#7457)
* Fix allow_threads and EINTR handling

- Wrap Windows SemLock acquire wait with allow_threads
- Retry nanosleep on EINTR with remaining time instead of
  returning early
- Remove expectedFailure for test_sleep in _test_eintr.py

* Remove expectedFailureIfWindows for testHashComparisonOfMethods
2026-03-20 19:59:13 +09:00
dependabot[bot]
247044a805 Bump which from 8.0.0 to 8.0.2 (#7469)
Bumps [which](https://github.com/harryfei/which-rs) from 8.0.0 to 8.0.2.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/8.0.0...8.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-version: 8.0.2
  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-03-20 12:43:07 +09:00
Shahar Naveh
68cf736a9f Add cargo shear CI step (#7461)
* Add `cargo shear` CI step

* Remove some deps

* Move `ruff_python_parser` to dev deps

* Remove `thread_local` crate

* Cleanup `num-integer` usage

* Remove parser fro. wasm

* Remove windows-sys from venv launcher

* Update lock

* Resolve more

* Fix wasm

* Update lock
2026-03-20 12:42:51 +09:00
Shahar Naveh
53fa525fc9 Resume opcode to hold ResumeType (#7465) 2026-03-19 14:24:07 +00:00
Shahar Naveh
b11b8e66ce Add actionlint step (#7464) 2026-03-19 20:41:59 +09:00
김은빈
79e17cb1cf Fix subclass right-op dispatch for Python classes (#7462)
* Fix subclass right-op dispatch for Python classes

* Separate fallback queueing from subclass priority in op dispatch
2026-03-19 09:51:56 +09:00
Copilot
2f181efed8 Preserve str subclasses in ascii() for ASCII-only __repr__ results (#7455) 2026-03-19 09:48:42 +09:00
Shahar Naveh
9a5de28b79 Align _opcode_metadata.py to 3.14.3 (#7456)
* Align `_opcode_metadata.py` to 3.14.3

* Unmark passing test

* Ensure python 3.14 runs on CI

* Update banner

* Fix `test__opcode.py`

* Adjust generate script

* Fix docs
2026-03-18 22:09:00 +09:00
Shahar Naveh
31480243f0 Only save cache if running on main (#7460) 2026-03-18 21:57:19 +09:00
Shahar Naveh
f2765982cd Remove some tests from PLATFORM_INDEPENDENT_TESTS (#7459)
* Remove some platform dependent tests

* Mark failing tests
2026-03-18 21:55:56 +09:00
Jeong, YunWon
62edc4722a Add per-type vectorcall for builtin constructors (#7407)
Add vectorcall fast paths for dict, list, set, int, float, str,
bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is
overridden in Python. Prevent constructor vectorcall inheritance
to heap subclasses. Fix stat_result to use struct_sequence_new
with reference-copy for hidden time fields.
2026-03-18 21:44:35 +09:00
Jeong, YunWon
0768cf80d3 Merge pull request #7440 from youknowone/pycode
Upgrade test_code and fix code bugs
2026-03-17 20:53:42 +09:00
Lee Dogeon
f868f81db6 Implement complex.from_number (#7453)
Co-authored-by: CPython Developers <>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 20:50:28 +09:00
Shahar Naveh
6b768ff702 Cleanup ci.yaml matrix usage (#7260)
* cleanup

* Don't enable all resources on windows
2026-03-17 20:46:33 +09:00
Jeong, YunWon
2ea5960998 update cspell config (#7452)
* add allowCompoundWords

* cspell dict
2026-03-17 20:18:31 +09:00
dependabot[bot]
2a61237341 Bump lz4_flex from 0.12.0 to 0.12.1 (#7449)
Bumps [lz4_flex](https://github.com/pseitz/lz4_flex) from 0.12.0 to 0.12.1.
- [Release notes](https://github.com/pseitz/lz4_flex/releases)
- [Changelog](https://github.com/PSeitz/lz4_flex/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pseitz/lz4_flex/compare/0.12.0...0.12.1)

---
updated-dependencies:
- dependency-name: lz4_flex
  dependency-version: 0.12.1
  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-03-17 12:57:31 +09:00
dependabot[bot]
87f5c7dd29 Bump rustix from 1.1.3 to 1.1.4 (#7444)
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v1.1.3...v1.1.4)

---
updated-dependencies:
- dependency-name: rustix
  dependency-version: 1.1.4
  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-03-17 12:57:23 +09:00
dependabot[bot]
cda9f8247f Bump num_enum from 0.7.5 to 0.7.6 in the num_enum group (#7443)
Bumps the num_enum group with 1 update: [num_enum](https://github.com/illicitonion/num_enum).


Updates `num_enum` from 0.7.5 to 0.7.6
- [Commits](https://github.com/illicitonion/num_enum/compare/0.7.5...0.7.6)

---
updated-dependencies:
- dependency-name: num_enum
  dependency-version: 0.7.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: num_enum
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-17 12:57:14 +09:00
dependabot[bot]
d04490ee25 Bump actions/download-artifact from 8.0.0 to 8.0.1 (#7448)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v8...v8.0.1)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  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-03-17 12:56:58 +09:00
dependabot[bot]
b2b337588d Bump strum_macros from 0.27.2 to 0.28.0 (#7445)
Bumps [strum_macros](https://github.com/Peternator7/strum) from 0.27.2 to 0.28.0.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/compare/v0.27.2...v0.28.0)

---
updated-dependencies:
- dependency-name: strum_macros
  dependency-version: 0.28.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-03-17 12:24:29 +09:00
Jeong, YunWon
c57f4decd5 Fix test_code: compiler and code object improvements
- Add CO_NESTED flag (0x10) for nested function scopes
- Emit LOAD_SMALL_INT for integers 0..=255 instead of LOAD_CONST
- Eliminate dead constant expression statements (no side effects)
- Ensure None in co_consts for functions with no other constants
- Add code.__replace__() for copy.replace() support
- Mark test_co_lnotab and test_invalid_bytecode as expectedFailure
2026-03-17 10:54:12 +09:00
dependabot[bot]
423b6ca59b Bump marocchino/sticky-pull-request-comment from 2 to 3 (#7447)
Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2 to 3.
- [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases)
- [Commits](https://github.com/marocchino/sticky-pull-request-comment/compare/v2...v3)

---
updated-dependencies:
- dependency-name: marocchino/sticky-pull-request-comment
  dependency-version: '3'
  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-03-16 23:45:52 +09:00
dependabot[bot]
caca67cf27 Bump github/gh-aw from 0.56.2 to 0.58.3 (#7446)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.56.2 to 0.58.3.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](f1073c5498...08a903b1fb)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.58.3
  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-03-16 23:45:26 +09:00
Lee Dogeon
5f1d5d2815 Cleanup some direct magic method definitions (#7441)
* Migrate direct __repr__ definitions to Representable trait

Move legacy #[pymethod] __repr__ to impl Representable for:
- PySSLContext, PySSLSocket (ssl.rs)
- BufferedReader, BufferedWriter, BufferedRandom (_io.rs)

Enable __repr__ guard in derive to prevent future direct definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Migrate direct __del__ definition to Destructor trait for PySocket

Move #[pymethod] __del__ to impl Destructor for PySocket.
Preserves ResourceWarning emission and close behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove redundant __iter__ pymethods from PyFuture and PyTask

Both types already have impl Iterable, so the direct #[pymethod]
__iter__ definitions were duplicates. Enable __iter__/__next__
guards in derive to prevent future direct definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Enable derive guards for __get__/__set__/__delete__ descriptor methods

All concrete types already use GetDescriptor/SetDescriptor traits.
Activate the compile-time guard to prevent future direct definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Enable derive guards for AsMapping/AsSequence slot methods

Remove redundant #[pymethod(name = "__len__")] from PyStr
(already provided via AsMapping/AsSequence trait impls).

Enable compile-time guards for __len__, __contains__, __getitem__,
__setitem__, __delitem__ to prevent future direct definitions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:49:45 +09:00
Jeong, YunWon
f27490c92b Consume nested scope tables in optimized-out asserts (#7438)
When -O flag removes assert statements, any nested scopes
(generators, comprehensions, lambdas) inside the assert
expression still have symbol tables in the sub_tables list.
Without consuming them, the next_sub_table index gets
misaligned, causing later scopes to use wrong symbol tables.

Walk the skipped assert expression with an AST visitor to
find and consume nested scope symbol tables, keeping the
index aligned with AST traversal order.
2026-03-16 16:09:17 +09:00
Jeong, YunWon
d2d2822bb9 Implement code object __eq__/__hash__
- Add Comparable and Hashable traits for PyCode
- Compare by name, args, flags, bytecode, consts, names, vars, linetable
- Hash by tuple of key attributes matching CPython's code_hash
- Remove unused custom_ops slice in Instruction::try_from
- Add co_lnotab intentional non-implementation comment
2026-03-16 15:25:29 +09:00
Jeong, YunWon
47c2acd40e Fix code_offset_to_line handling 2026-03-16 15:25:29 +09:00
CPython Developers
2b06a0b172 Upgrade test_code from CPython 3.14.3 2026-03-16 15:25:29 +09:00
Shahar Naveh
f1ddb4fc5e Pin checkout to commit. Don't persist creds if not needed (#7430) 2026-03-16 12:34:43 +09:00
Lee Dogeon
97790a88b2 Implement missing slots for NoneType (#7437)
* Define only wrapper_descriptor

* Implement missing slots for NoneType
2026-03-16 11:58:40 +09:00
Jeong, YunWon
03b7c4ebb8 Mark RustPython 0.5.0 2026-03-16 11:32:27 +09:00
ShaharNaveh
f49af3fd48 Update _test_eintr.py from 3.14.3 2026-03-16 00:10:09 +09:00
Lee Dogeon
9ddd07a656 Preserve imaginary zero signs when adding real values to complex numbers (#7421)
* Preserve imaginary zero signs when adding real values to complex numbers

* Refactor complex_add with match expression

* Correct complex real subtract op

* Remove unnecessary vm arugment
2026-03-15 17:04:23 +09:00
Shahar Naveh
9a297aad2b Newtype var_nums oparg (#7431) 2026-03-15 17:01:03 +09:00
Shahar Naveh
005860cc0b Don't trigger update libs status on forks (#7428) 2026-03-14 20:23:08 +09:00
Shahar Naveh
0c14ad195e Update ruff to 0.15.6 (#7427)
* Update ruff to `0.15.6`

* Unmark passing tests
2026-03-14 20:22:42 +09:00
Lee Dogeon
d1ea35962b Match divmod zero-division message with CPython (#7426) 2026-03-14 20:21:23 +09:00
Jeong, YunWon
40fc7c210d Upgrade pymath to 0.2.0 (#7429) 2026-03-14 20:13:30 +09:00
Jiseok CHOI
430eb5ffe4 Fix sqlite3 Cursor.fetchmany to not fetch rows when size=0 (#7425) 2026-03-14 15:49:55 +09:00
Lee Dogeon
e7d2b57dcc Match __module__ setter behavior to CPython (#7424)
* Remove stale __firstlineno__ when updating type.__module__

* Register and use __firstlineno__ as identifier
2026-03-14 15:48:48 +09:00
Lee Dogeon
21d549caf6 Handle chr() range checks without Rust integer overflow (#7422) 2026-03-14 15:47:45 +09:00
Lee Dogeon
ed032d31fb Make singleton builtin types immutable (#7423) 2026-03-14 09:45:43 +09:00
Shahar Naveh
4483c31c65 Update test_marshal.py from 3.14.3 (#7409)
---------

Co-authored-by: Lee Dogeon <dev.moreal@gmail.com>
2026-03-14 09:45:12 +09:00
Jeong, YunWon
be6025ab03 Adapt bitflagset to use enum (#7419) 2026-03-14 09:44:02 +09:00
Jiseok CHOI
7d63f65f21 Fix classmethod descr_get to match CPython behavior (#7420)
Simplify classmethod.__get__ to always create a PyBoundMethod binding
  the callable to the class, matching CPython's cm_descr_get which simply
  calls PyMethod_New(cm->cm_callable, type).

  The previous implementation incorrectly tried to call __get__ on the
  wrapped callable, which broke when a bound method was passed to
  classmethod() (e.g. classmethod(A().foo)).
2026-03-14 03:40:16 +09:00
Jeong, YunWon
073adbd8da Fix SSL read over-consuming TCP data (#7418)
Use single-record reading (recv_one_tls_record) for all SSL
reads, not just handshake. This prevents rustls from eagerly
consuming close_notify alongside application data, which left
the TCP buffer empty and caused select()-based servers to miss
readability and time out.

Also fix recv_one_tls_record to return Eof (not WantRead) when
peek returns empty bytes, since empty peek means the peer has
closed the TCP connection.
2026-03-13 21:02:31 +09:00
Jeong, YunWon
7c0981b9ce Fix SSL handshake over-reading in STARTTLS (#7417)
During STARTTLS handshake, sock_recv(16KB) could consume
application data that arrived alongside handshake records.
The consumed data ended up in rustls's internal buffer where
select() could not detect it, causing asyncore-based servers
to miss readable events and the peer to time out.

Use MSG_PEEK to find the TLS record boundary, then recv()
only one complete record. Remaining data stays in the kernel
TCP buffer, visible to select(). This matches OpenSSL's
default no-read-ahead behaviour.

Fixes flaky test_poplib (TestPOP3_TLSClass) failures.
2026-03-13 11:46:35 +09:00
김은빈
a733c8baa7 Fix weakref proxy number protocol, right-side ops, and non-iterator error (#7410) 2026-03-13 11:36:16 +09:00
dependabot[bot]
83e3785a1c Bump memmap2 from 0.9.9 to 0.9.10 (#7415)
Bumps [memmap2](https://github.com/RazrFalcon/memmap2-rs) from 0.9.9 to 0.9.10.
- [Changelog](https://github.com/RazrFalcon/memmap2-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/RazrFalcon/memmap2-rs/compare/v0.9.9...v0.9.10)

---
updated-dependencies:
- dependency-name: memmap2
  dependency-version: 0.9.10
  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-03-13 10:19:04 +09:00
dependabot[bot]
f4eaee1a62 Bump quote from 1.0.44 to 1.0.45 (#7414)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.44 to 1.0.45.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.44...1.0.45)

---
updated-dependencies:
- dependency-name: quote
  dependency-version: 1.0.45
  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-03-13 08:53:48 +09:00
dependabot[bot]
021286e9ab Bump the openssl group with 2 updates (#7413)
Bumps the openssl group with 2 updates: [openssl](https://github.com/rust-openssl/rust-openssl) and [openssl-sys](https://github.com/rust-openssl/rust-openssl).


Updates `openssl` from 0.10.75 to 0.10.76
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.75...openssl-v0.10.76)

Updates `openssl-sys` from 0.9.111 to 0.9.112
- [Release notes](https://github.com/rust-openssl/rust-openssl/releases)
- [Commits](https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.111...openssl-sys-v0.9.112)

---
updated-dependencies:
- dependency-name: openssl
  dependency-version: 0.10.76
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: openssl
- dependency-name: openssl-sys
  dependency-version: 0.9.112
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: openssl
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 08:53:28 +09:00
Jeong, YunWon
7c05bef5ff Merge pull request #7411 from youknowone/ci-format
Fix pr-format CI to fail on formatting changes
2026-03-13 08:53:01 +09:00
Shahar Naveh
b58a910026 Upgrade libc to 0.2.183 (#7406) 2026-03-13 01:21:18 +09:00
EtherealPsyche
0f61464c54 If the host_env feature is enabled, then enable the _ctypes module on the Android platform. (#7402) 2026-03-13 01:20:32 +09:00
Jeong, YunWon
a14f856af4 fmt 2026-03-13 01:19:10 +09:00
Jeong, YunWon
bdfe2cf923 Fix pr-format CI to fail on formatting changes
Add git diff --exit-code step to fail the check when
formatters produce changes. Previously reviewdog only
posted suggestions without failing the workflow.
2026-03-13 01:19:10 +09:00
김은빈
61b3b4f42b Add strict parameter to map() builtin (#7405)
* Add strict parameter to map() builtin

* Refactor map IterNext to match zip style
2026-03-12 15:02:53 +00:00
Jeong, YunWon
f26752c5ef Merge pull request #7408 from ShaharNaveh/update-test-gen
Update generator related tests from 3.14.3
2026-03-12 23:47:31 +09:00
Jeong, YunWon
04b1febd3b Merge pull request #7404 from moreal/bump-3.14.3-modulefinder
Bump 3.14.3 modulefinder
2026-03-12 23:42:27 +09:00
Shahar Naveh
bb36783206 Newtype oparg align methods (#7403)
* Align methods for newtype opargs
2026-03-12 23:41:21 +09:00
ShaharNaveh
93865e6523 Update test_yield_from.py from 3.14.3 2026-03-12 13:33:04 +01:00
ShaharNaveh
8530439202 Mark failing tests 2026-03-12 13:24:29 +01:00
ShaharNaveh
9a20be7b9e Update test_generators.py from 3.14.3 2026-03-12 13:21:30 +01:00
ShaharNaveh
fcd1bb6e9b Add test_generator_stop.py from 3.14.3 2026-03-12 13:19:19 +01:00
Lee Dogeon
e9ab35fdd7 Mark failing tests for modulefinder
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:52:26 +09:00
CPython Developers
ca5a3a7c88 Update modulefinder from v3.14.3 2026-03-12 20:52:24 +09:00
Jeong, YunWon
c578ac0b21 gc: add CollectResult, stats fields, get_referrers, and fix count reset (#7354)
* gc: add CollectResult, stats fields, get_referrers, and fix count reset

- Add CollectResult struct with collected/uncollectable/candidates/duration
- Add candidates and duration fields to GcStats and gc.get_stats()
- Pass CollectResult to gc.callbacks info dict
- Reset generation counts for all collected generations (0..=N)
- Return 0 for third value in gc.get_threshold() (3.13+)
- Implement gc.get_referrers() by scanning all tracked objects
- Add DEBUG_COLLECTABLE output for collectable objects
- Update test_gc.py to expect candidates/duration in stats

* Update test_gc from v3.14.3

* Update test_gc.py from CPython v3.15.0a5

Taken from v3.15 (not v3.14.3) because get_stats() candidates/duration
fields were added in 3.13+ and the corresponding test assertions only
exist in 3.15.

* Fix gc_state build on wasm32: skip Instant timing

* Add candidates/duration to gc callback info, mark v3.15 test failures

* Fix gc.get_referrers to exclude executing frames, fix Future cancelled exc leak

- get_referrers: skip frame objects on the execution stack, since
  they are not GC-tracked in CPython (_PyInterpreterFrame)
- _asyncio Future/Task make_cancelled_error_impl: clear the stored
  cancelled exception after returning it, matching the Python
  _make_cancelled_error behavior

* Fix gc.get_threshold to return actual gen2 threshold value

* Fix inconsistent GC count reset in early-return paths

Use the same reset_end formula in unreachable-empty early returns
as in the main collection path and collecting-empty path.

* Accept keyword arguments in socket.__init__

Use a FromArgs struct instead of a positional-only tuple so that
family, type, proto, and fileno can be passed as keyword arguments.

* Disable comp_inlined in symbol table to match compiler

The compiler does not yet implement PEP 709 inlined comprehensions
(is_inlined_comprehension_context always returns false), but the
symbol table was marking comprehensions as inlined. This mismatch
could cause comprehension-local symbols to be merged into the parent
scope while the compiler still looks them up in a separate scope.

---------

Co-authored-by: CPython Developers <>
2026-03-12 20:48:22 +09:00
CPython Developers
331029e043 Update poplib from v3.14.3 2026-03-12 18:08:56 +09:00
dependabot[bot]
3f20619ee0 Bump socket2 from 0.6.2 to 0.6.3 (#7373)
Bumps [socket2](https://github.com/rust-lang/socket2) from 0.6.2 to 0.6.3.
- [Release notes](https://github.com/rust-lang/socket2/releases)
- [Changelog](https://github.com/rust-lang/socket2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/socket2/compare/v0.6.2...v0.6.3)

---
updated-dependencies:
- dependency-name: socket2
  dependency-version: 0.6.3
  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-03-12 11:00:24 +09:00
Shahar Naveh
da745ba48e Enable jit on macos (#7401) 2026-03-12 09:20:24 +09:00
Shahar Naveh
5c631e5129 Newtype var_num oparg (#7400) 2026-03-12 09:20:01 +09:00
Shahar Naveh
7e4727001d Use matrix for cargo check (#7388)
* Use matrix for cargo_check

* Tweak `gcc-aarch64-linux-gnu` deps

* Re-add redox comment
2026-03-11 22:21:25 +09:00
Jeong, YunWon
85eca218af Fix atexit unraisable exception message format (#7399)
* Fix atexit unraisable exception message format

Match PyErr_FormatUnraisable behavior: use
"Exception ignored in atexit callback {func!r}" as err_msg
and pass None as object instead of the callback function.

* Fix atexit unregister deadlock with reentrant __eq__

Release the lock during equality comparison in unregister so
that __eq__ can safely call atexit.unregister or atexit._clear.
Store callbacks in LIFO order (insert at front) and use
identity-based search after comparison to handle list mutations,
matching atexitmodule.c behavior.

Also pass None as err_msg when func.repr() fails, matching
CPython's PyErr_FormatUnraisable fallback.
2026-03-11 22:20:37 +09:00
Jeong, YunWon
d248a04cae Refine specialization caches and extend binary-op coverage (#7386)
* Align BINARY_OP_EXTEND with CPython descriptor cache model

* Align type _spec_cache and latin1 singleton string paths

* Add specialization differential harness and align init error text

* Tighten CALL_ALLOC_AND_ENTER_INIT stack-space guard

* Align call-init frame flow and spec cache atomic ordering

* Refine call-init recursion guard and cache swap lifetime handling

* Align spec cache write locking with CPython contract

* Align load attr miss cooldown with CPython

* Align CALL descriptor and class-call specialization with CPython

* Extract datastack_frame_size_bytes_for_code, skip monitoring for init_cleanup frames, guard trace dispatch

- Extract datastack_frame_size_bytes_for_code as free function, use it
  to compute init_cleanup stack bytes instead of hardcoded constant
- Add monitoring_disabled_for_code to skip instrumentation for
  synthetic init_cleanup code object in RESUME and execute_instrumented
- Add is_trace_event guard so profile-only events skip trace_func dispatch
2026-03-10 15:39:55 +09:00
Jeong, YunWon
a854ef2a2b Rename stdlib files to match Python module names (#7397) 2026-03-10 15:29:17 +09:00
Jeong, YunWon
45d93f4b20 Save errno inside allow_threads in semaphore acquire (#7391)
allow_threads may call attach_thread() on return, which
can invoke syscalls that clobber errno. Capture errno
inside the closure before it is lost.
2026-03-10 14:04:28 +09:00
Jeong, YunWon
0355885651 Fix parking lot hash table after fork (#6963)
* Use patched parking_lot_core with fork-safe HASHTABLE reset

parking_lot_core's global HASHTABLE retains stale ThreadData after
fork(), causing segfaults when contended locks enter park(). Use the
patched version from youknowone/parking_lot (rustpython branch) which
registers a pthread_atfork handler to reset the hash table.

Unskip test_asyncio TestFork. Add Manager+fork integration test.

* Unskip fork-related flaky tests after parking_lot fix

With parking_lot_core's HASHTABLE now properly reset via
pthread_atfork, fork-related segfaults and connection errors
in multiprocessing tests should be resolved.

Remove skip/expectedFailure markers from:
- test_concurrent_futures/test_wait.py (6 tests)
- test_concurrent_futures/test_process_pool.py (1 test)
- test_multiprocessing_fork/test_manager.py (all WithManagerTest*)
- test_multiprocessing_fork/test_misc.py (5 tests)
- test_multiprocessing_fork/test_threads.py (2 tests)
- _test_multiprocessing.py (2 shared_memory tests)

Keep test_repr_rlock skipped (flaky thread start latency,
not fork-related).
2026-03-10 12:57:29 +09:00
Jeong, YunWon
e645761142 Implement _tokenize and update tokenize from v3.14.3 (#7392)
* Base implementation of _tokenize module

Port from PR #6240 by ShaharNaveh, adapted to current codebase.
Uses ruff_python_parser for tokenization via TokenizerIter.

* Update tokenize from v3.14.3

* Rewrite _tokenize with 2-phase model

Replace per-line reparsing with single-pass tokenization:
- Read all lines via readline, parse once, yield tokens
- Fix token type values (COMMENT=65, NL=66, OP=55)
- Fix NEWLINE/NL end positions and implicit newline handling
- Fix DEDENT positions via look-ahead to next non-DEDENT token
- Handle FSTRING_MIDDLE brace unescaping ({{ → {, }} → })
- Emit implicit NL before ENDMARKER when source lacks trailing newline
- Raise IndentationError from lexer errors
- Remove 13 expectedFailure marks for now-passing tests

---------

Co-authored-by: ShaharNaveh <shaharnaveh@users.noreply.github.com>
Co-authored-by: CPython Developers <>
2026-03-10 12:27:09 +09:00
dependabot[bot]
ae8d8c7926 Bump insta from 1.46.1 to 1.46.3 (#7394)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.46.1 to 1.46.3.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.46.1...1.46.3)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.46.3
  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-03-10 11:50:31 +09:00
Jeong, YunWon
55737ede63 Rewrite _tokenize with 2-phase model
Replace per-line reparsing with single-pass tokenization:
- Read all lines via readline, parse once, yield tokens
- Fix token type values (COMMENT=65, NL=66, OP=55)
- Fix NEWLINE/NL end positions and implicit newline handling
- Fix DEDENT positions via look-ahead to next non-DEDENT token
- Handle FSTRING_MIDDLE brace unescaping ({{ → {, }} → })
- Emit implicit NL before ENDMARKER when source lacks trailing newline
- Raise IndentationError from lexer errors
- Remove 13 expectedFailure marks for now-passing tests
2026-03-10 11:28:16 +09:00
CPython Developers
bf2b993c93 Update tokenize from v3.14.3 2026-03-10 11:28:16 +09:00
ShaharNaveh
5c5aff8fd9 Base implementation of _tokenize module
Port from PR #6240 by ShaharNaveh, adapted to current codebase.
Uses ruff_python_parser for tokenization via TokenizerIter.
2026-03-10 11:28:16 +09:00
dependabot[bot]
5b0fb03fc4 Bump syn from 2.0.114 to 2.0.117 (#7393)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.114 to 2.0.117.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.114...2.0.117)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.117
  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-03-10 10:24:58 +09:00
dependabot[bot]
d81db8afc5 Bump actions/checkout from 4 to 6 (#7396)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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-03-10 00:46:25 +09:00
dependabot[bot]
13a89be61b Bump github/gh-aw from 0.51.5 to 0.56.2 (#7395)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.51.5 to 0.56.2.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](88319be75a...f1073c5498)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.56.2
  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-03-10 00:46:01 +09:00
Jeong, YunWon
2266ba7f47 Add per-size tuple freelist (20 buckets × 2000 each) (#7361)
* Add per-size tuple freelist (20 buckets × 2000 each)

Implement PyTuple freelist matching tuples[PyTuple_MAXSAVESIZE]:
- TupleFreeList with 20 per-size buckets (sizes 1..=20, 2000 capacity each)
- freelist_push uses pre-clear size hint for correct bucket selection
- freelist_pop takes &Self payload to select bucket by size
- Type guard in new_ref handles structseq types sharing PyTuple vtable
- Add pyinner_layout<T>() helper for custom freelist Drop impls
- Update freelist_pop/push signatures across all freelist types

* freelist: exact type check at pop call-site

Move exact-type filtering from freelist_pop implementations to the
single call-site in new_ref. This prevents structseq and other subtypes
from popping tuple freelist entries entirely, rather than popping and
then deallocating on type mismatch.

Add Context::try_genesis() that returns None during bootstrap to avoid
deadlock when genesis() is called during Context initialization.

* Move exact type check from freelist_push to call-site in default_dealloc

Remove typ parameter from freelist_push trait signature. The exact type
check is now done once at the call-site alongside the heaptype check,
simplifying all freelist_push implementations.

* Remove freelist_hint; call freelist_push before tp_clear

By calling freelist_push before tp_clear, the payload is still intact
and can be read directly (e.g. tuple element count for bucket selection).
This eliminates freelist_hint and the hint parameter entirely.
2026-03-09 22:34:37 +09:00
Jeong, YunWon
6c12152aeb rustfmt (#7390) 2026-03-09 22:31:29 +09:00
Shahar Naveh
82e9b5d9e1 Newtype ConstIdx, Constants (#7358)
* Newtype ConstIdx, Constants

* Set generic
2026-03-09 11:47:24 +09:00
Jiseok CHOI
fa4f84ce3f Introduce TimeoutSeconds utility type for timeout parameters (#7271)
* Introduce TimeoutSeconds utility type for timeout parameters

Follow-up refactoring from #7237.

Python timeout parameters typically accept both float and int. Several
places in the codebase used Either<f64, i64> for this, each repeating
the same match-and-convert boilerplate. This extracts that into a
TimeoutSeconds type in vm::function::number.

Refactored sites:
- _sqlite3::ConnectArgs.timeout
- _thread::AcquireArgs.timeout
- _thread::ThreadHandle::join timeout

Either<f64, i64> in time.rs (6 sites) left unchanged: those are
timestamp values with per-branch logic (floor, range checks, etc).
Either<f64, isize> in select.rs also left unchanged (different type).

* Validate timeout in ThreadHandle::join to prevent panic

* refactor: move TimeoutSeconds from number to time module
2026-03-09 11:36:03 +09:00
Jeong, YunWon
ead2d98557 Separate WeakRefList from ObjExt as independent prefix (#7365)
- Remove weak_list from ObjExt, allocate WeakRefList as its own
  prefix slot before PyInner
- Add MANAGED_WEAKREF flag (1 << 3) to PyTypeFlags
- Normalize MANAGED_WEAKREF from HAS_WEAKREF after flag assembly
- Use Layout::extend offsets in bootstrap allocator
2026-03-09 02:09:26 +09:00
Jeong, YunWon
ce8952b1f5 Use borrowed pointers in TYPE_CACHE instead of strong references (#7384)
The method cache (TYPE_CACHE) was storing strong references (Arc
clones) to cached attribute values, which inflated sys.getrefcount().
This caused intermittent test_memoryview failures where refcount
assertions would fail depending on GC collection timing.

Store borrowed raw pointers instead. Safety is guaranteed because:
- type_cache_clear() nullifies all entries during GC collection,
  before the collector breaks cycles
- type_cache_clear_version() nullifies entries when a type is
  modified, before the source dict entry is removed
- Readers use try_to_owned_from_ptr (safe_inc) to atomically
  validate and increment the refcount on cache hit
2026-03-08 22:55:22 +09:00
Jeong, YunWon
cbcc19751e fix format (#7385) 2026-03-08 22:53:26 +09:00
Jeong, YunWon
46abff6880 Harden CALL specialization guards and cache callables (#7360)
* vm: align CALL/CALL_KW specialization core guards with CPython

* vm: keep specialization hot on misses and add heaptype getitem parity

* vm: align call-alloc/getitem cache guards and call fastpath ordering

* vm: align BINARY_OP, STORE_SUBSCR, UNPACK_SEQUENCE specialization guards

* vm: finalize unicode/subscr specialization parity and regressions

* vm: finalize specialization GC safety, tests, and cleanup
2026-03-08 21:15:30 +09:00
Jeong, YunWon
45d81296e4 More fork safety (#7380)
* apply more allow_threads

* Simplify STW thread state transitions

- Fix park_detached_threads: successful CAS no longer sets
  all_suspended=false, avoiding unnecessary polling rounds
- Replace park_timeout(50µs) with park() in wait_while_suspended
- Remove redundant self-suspension in attach_thread and detach_thread;
  the STW controller handles DETACHED→SUSPENDED via park_detached_threads
- Add double-check under mutex before condvar wait to prevent lost wakes
- Remove dead stats_detach_wait_yields field and add_detach_wait_yields

* Representable for ThreadHandle

* Set ThreadHandle state to Running in parent thread after spawn

Like CPython's ThreadHandle_start, set RUNNING state in the parent
thread immediately after spawn() succeeds, rather than in the child.
This eliminates a race where join() could see Starting state if called
before the child thread executes.

Also reverts the macOS skip for test_start_new_thread_failed since the
root cause is fixed.

* Set ThreadHandle state to Running in parent thread after spawn

* Add debug_assert for thread state in start_the_world

* Unskip now-passing test_get_event_loop_thread and test_start_new_thread_at_finalization

* Wrap IO locks and file ops in allow_threads

Add lock_wrapped to ThreadMutex for detaching thread state
while waiting on contended locks. Use it for buffered and
text IO locks. Wrap FileIO read/write in allow_threads via
crt_fd to prevent STW hangs on blocking file operations.

* Use std::sync for thread start/ready events

Replace parking_lot Mutex/Condvar with std::sync (pthread-based)
for started_event and handle_ready_event. This prevents hangs
in forked children where parking_lot's global HASHTABLE may be
corrupted.
2026-03-08 18:06:23 +09:00
Jeong, YunWon
0046c627f1 Wait for overlapped WriteFile completion in Rust (#7383)
On Windows, SimpleQueue skips write locking because pipe
writes are assumed atomic. Without GIL, PipeConnection.
_send_bytes races on _send_ov when multiple threads call
send_bytes concurrently (e.g. _terminate_pool vs workers).

Wait for pending overlapped writes inside WriteFile before
returning to Python, so ERROR_IO_PENDING is never exposed
and the _send_ov assignment window is negligible.
2026-03-08 18:04:19 +09:00
Shahar Naveh
fbab8fc5be Update ruff to 0.15.5 (#7366) 2026-03-08 10:36:27 +09:00
Shahar Naveh
e669944efe Unified action for installing Linux deps (#7381) 2026-03-08 10:35:24 +09:00
Shahar Naveh
f680f8a181 Unified action for installing macos deps (#7379)
* Add `install-macos-deps` action

* Use new action
2026-03-07 21:13:42 +09:00
dependabot[bot]
abfb51efc9 Bump uuid from 1.21.0 to 1.22.0 (#7372)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.21.0 to 1.22.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.21.0...v1.22.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.22.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-03-07 20:30:52 +09:00
Shahar Naveh
28bda1d34f Fix error message in ctypes (#7368)
* Remove unnecessary `to_{owned,string}()` calls (#7367)

* Fix error message in ctype
2026-03-07 20:30:09 +09:00
dependabot[bot]
89889acc08 Bump rustls from 0.23.36 to 0.23.37 (#7374)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.36 to 0.23.37.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.36...v/0.23.37)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.37
  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-03-07 20:29:23 +09:00
Shahar Naveh
4aeec7f2dc Remove duplicate cargo check (#7377) 2026-03-07 20:28:58 +09:00
Shahar Naveh
ab32c785ea Restructure CI with matrix approach and multi-feature support (#7376)
* `--features` flag can take multiple values

* Simplify CI job

* Remove bad default

* Enable jit on macOS
2026-03-07 20:28:26 +09:00
Shahar Naveh
d9020c5841 Unpin nightly channel for miri tests (#7378) 2026-03-07 20:22:07 +09:00
Jeong, YunWon
2bb9173caf Suspend Python threads before fork() (#7364)
* Suspend Python threads before fork()

Add stop-the-world thread suspension around fork() to prevent
deadlocks from locks held by dead parent threads in the child.

- Thread states: DETACHED / ATTACHED / SUSPENDED with atomic CAS
  transitions matching _PyThreadState_{Attach,Detach,Suspend}
- stop_the_world / start_the_world: park all non-requester threads
  before fork, resume after (parent) or reset (child)
- allow_threads (Py_BEGIN/END_ALLOW_THREADS): detach around blocking
  syscalls (os.read/write, waitpid, Lock.acquire, time.sleep) so
  stop_the_world can force-park via CAS
- Acquire/release import lock around fork lifecycle
- zero_reinit_after_fork: generic lock reset for parking_lot types
- gc_clear_raw: detach dict instead of clearing entries
- Lock-free double-check for descriptor cache reads (no read-side
  seqlock); write-side seqlock retained for writer serialization
- fork() returns PyResult, checks PythonFinalizationError, calls
  sys.audit
2026-03-07 20:20:16 +09:00
Jeong, YunWon
ed5bffeec6 replace auto commit to format suggestion (#7375) 2026-03-07 14:53:25 +09:00
Shahar Naveh
fc1c27896b Remove unnecessary to_{owned,string}() calls (#7367) 2026-03-07 00:06:18 +09:00
CPython Developers
a27d812286 Update locale from v3.14.3 2026-03-06 06:00:08 +09:00
Jeong, YunWon
04cf5dacca Object header slimming: prefix allocation for ObjExt (#7334)
* Object header slimming: prefix allocation for ObjExt

Extract dict, weak_list, and slots fields from PyInner<T> into a
separate ObjExt struct allocated as a prefix before PyInner using
Layout::extend(). Objects that don't need these fields (int, str,
float, list, tuple, dict, etc.) skip the prefix entirely.

- Add HAS_WEAKREF flag to PyTypeFlags for per-type weakref control
- Add HAS_EXT bit to GcBits indicating prefix presence
- Define ObjExt struct with dict, weak_list, slots
- Shrink PyInner header from ~80-88 bytes to ~32 bytes for lightweight objects
- Update all accessor methods to go through ext_ref()
- Update bootstrap type hierarchy to use prefix allocation
- Add __weakref__ getset descriptor for heap types
- Set HAS_WEAKREF on builtin types that support weak references
- Remove test_weak_keyed_bad_delitem expectedFailure (now passes)

* Add HAS_WEAKREF to _asyncio Future/Task, rename weakref helpers

- Add HAS_WEAKREF flag to PyFuture and PyTask (matches CPython)
- Rename subtype_getweakref/setweakref to subtype_get_weakref/set_weakref
  to fix cspell unknown word lint

* Add HAS_WEAKREF to array, deque, _grouper; remove expectedFailure markers

- Add HAS_WEAKREF to PyArray and PyDeque (matches CPython)
- Add HAS_WEAKREF to PyItertoolsGrouper (internal use by groupby)
- Remove 6 expectedFailure markers from test_dataclasses for weakref/slots
  tests that now pass

* Add HAS_WEAKREF to code, union, partial, lock, IO, mmap, sre, sqlite3, typevar types

Add HAS_WEAKREF flag to built-in types that support weakref:
- PyCode, PyUnion, PyPartial, Lock, RLock
- All IO base/concrete classes (_IOBase, _RawIOBase, _BufferedIOBase,
  _TextIOBase, BufferedReader, BufferedWriter, BufferedRandom,
  BufferedRWPair, TextIOWrapper, StringIO, BytesIO, FileIO,
  WindowsConsoleIO)
- PyMmap, sre Pattern, sqlite3 Connection/Cursor
- TypeVar, ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeVarTuple

Remove 3 expectedFailure markers from test_descr for now-passing tests.

* Add HAS_DICT to type flags and handle non-METHOD/CLASS in descr_get

- Add HAS_DICT flag to PyType (type metaclass) alongside existing
  HAS_WEAKREF. All type objects are instances of type and need dict
  support, matching CPython's PyType_Type.
- Replace unimplemented!() in PyMethodDescriptor::descr_get with
  fallback to bind obj directly, matching CPython's method_get which
  uses PyCFunction_NewEx for non-METH_METHOD methods.

* Fix ext detection, HeapMethodDef ownership, WASM error

- Remove HAS_EXT gc_bits flag; detect ext from type flags
  using raw pointer reads to avoid Stacked Borrows violations
- Store HeapMethodDef owner in payload instead of dict hack
- Clear dict entries in gc_clear_raw to break cycles
- Add WASM error fallback when exception serialization fails
2026-03-06 01:19:22 +09:00
Jeong, YunWon
3b91466f62 Implement locale-aware 'n' format specifier for int, float, complex (#7350)
* Implement locale-aware 'n' format specifier for int, float, complex

Add LocaleInfo struct and locale-aware formatting methods to FormatSpec.
The 'n' format type now reads thousands_sep, decimal_point, and grouping
from C localeconv() and applies proper locale-based number grouping.
Remove @unittest.skip from test_format.test_locale.

* Fix complex 'n' format and remove locale expectedFailure markers

Rewrite format_complex_locale to reuse format_complex_re_im, matching
formatter_unicode.c: add_parens=0 and skip_re=0 for 'n' type.
Remove @expectedFailure from test_float__format__locale and
test_int__format__locale in test_types.py.

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-06 01:09:43 +09:00
Jeong, YunWon
7620c28482 Tighten specialization guards and add send_none fastpath (#7359)
* vm: align specialization guards with CPython patterns

* vm: tighten call specialization runtime guards

* vm: add send_none fastpath for generator specialization

* vm: restrict method-descriptor specialization to methods

* vm: deopt call specializations on guard misses

* vm: match CPython send/for-iter closed-frame guards

* vm: restrict len/isinstance specialization to builtins

* vm: use exact-type guards for call specializations

* vm: align class-call specialization flow with CPython

* vm: prefer FAST call opcodes for positional builtin calls

* vm: add callable identity guard to CALL_LIST_APPEND

* vm: make CALL_LIST_APPEND runtime guard pointer-based

* vm: align call guard cache and fallback behavior with CPython

* vm: use base vectorcall fallback for EXIT-style call misses

* vm: simplify CALL_LEN/CALL_ISINSTANCE runtime guards

* vm: infer call-convention flags for CPython-style CALL specialization

* vm: check use_tracing in eval_frame_active, add SendGen send_none

- Implement specialization_eval_frame_active to check vm.use_tracing
  so specializations are skipped when tracing/profiling is active
- Add send_none fastpath in SendGen handler for the common None case
2026-03-06 00:59:53 +09:00
dependabot[bot]
9ba155418b Bump aws-lc-fips-sys from 0.13.11 to 0.13.12 (#7338)
Bumps [aws-lc-fips-sys](https://github.com/aws/aws-lc-rs) from 0.13.11 to 0.13.12.
- [Release notes](https://github.com/aws/aws-lc-rs/releases)
- [Commits](https://github.com/aws/aws-lc-rs/commits/aws-lc-fips-sys/v0.13.12)

---
updated-dependencies:
- dependency-name: aws-lc-fips-sys
  dependency-version: 0.13.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-06 00:02:14 +09:00
Lee Dogeon
6f07745600 Fold const bool with unary not (#7357)
* Fold const bool with unary not

* Fold unnecessary TO_BOOL
2026-03-05 23:00:47 +09:00
Jeong, YunWon
73941b2255 Extract InterpreterFrame from Frame with Deref wrapper (#7353)
* Remove cells_frees duplicate storage from Frame

Cell/free variable objects were stored in both a separate
`Box<[PyCellRef]>` (cells_frees field) and in the localsplus
fastlocals array. Remove the redundant cells_frees field and
access cell objects directly through localsplus, eliminating
one Box allocation and N clone operations per frame creation.

* Extract InterpreterFrame from Frame with Deref wrapper

Introduce InterpreterFrame struct containing all execution state
fields previously on Frame. Frame now wraps InterpreterFrame via
FrameUnsafeCell and implements Deref for transparent field access.

localsplus and prev_line are plain fields on InterpreterFrame
(no longer individually wrapped in FrameUnsafeCell) since the
entire InterpreterFrame is wrapped at the Frame level.

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-05 23:00:13 +09:00
Jeong, YunWon
375b5472ed Fix thread-safety in GC, type cache, and instruction cache (#7355)
* Fix thread-safety in GC, type cache, and instruction cache

GC / refcount:
- Add safe_inc() check for strong()==0 in RefCount
- Add try_to_owned() to PyObject for atomic refcount acquire
- Replace strong_count()+to_owned() with try_to_owned() in GC
  collection and weakref callback paths to prevent TOCTOU races

Type cache:
- Add proper SeqLock (sequence counter) to TypeCacheEntry
- Readers spin-wait on odd sequence, validate after read
- Writers bracket updates with begin_write/end_write
- Use try_to_owned + pointer revalidation on read path
- Call modified() BEFORE attribute modification in set_attr

Instruction cache:
- Add pointer_cache (AtomicUsize array) to CodeUnits for
  single atomic pointer load/store (prevents torn reads)
- Add try_read_cached_descriptor with try_to_owned + pointer
  and version revalidation after increment
- Add write_cached_descriptor with version-bracketed writes

RLock:
- Fix release() to check is_owned_by_current_thread
- Add _release_save/_acquire_restore methods

* Fix RLock _acquire_restore tuple handling and unxfail threading test

* Align type cache seqlock writer protocol with CPython

* RLock: use single parking_lot level, track recursion manually

Instead of calling lock()/unlock() N times for recursion depth N,
keep parking_lot at 1 level and manage the count ourselves.
This makes acquire/release O(1) and matches CPython's
_PyRecursiveMutex approach (lock once + set level directly).

* Add try_to_owned_from_ptr to avoid &PyObject on stale ptrs

Use addr_of! to access ref_count directly from a raw pointer
without forming &PyObject first. Applied in type cache and
instruction cache hit paths where the pointer may be stale.

* Fix CI: spelling typo and xfail flaky test_thread_safety

- Fix "minimising" -> "minimizing" for cspell
- xfail test_thread_safety: dict iteration races with
  concurrent GC mutations in _finalizer_registry
2026-03-05 20:33:14 +09:00
Jeong, YunWon
86134e1ca1 Add freelists for PyComplex, PyInt, PyRange (#7345)
* Add freelists for PyComplex, PyInt, PyBoundMethod, PyRange

- PyComplex(100), PyInt(100), PyBoundMethod(20), PyRange(6)
- Use try_with instead of with in all freelist push/pop to prevent
  panic on thread-local access during thread teardown

* Use alloc::dealloc in FreeList::Drop to avoid thread-local access panic

During thread teardown, Box::from_raw triggers cascading destructors
that may access already-destroyed thread-local storage (GC state, other
freelists). Use raw dealloc instead to free memory without running
destructors.

* Auto-format: cargo fmt --all

* Fix clippy: use core::alloc::Layout instead of alloc::alloc::Layout

* Address review: PyBoundMethod clear=false, update FreeList doc comment

- Set clear=false on PyBoundMethod (tp_clear=NULL in classobject.c)
- Update FreeList doc comment to match actual Drop behavior (raw dealloc)

* Remove PyBoundMethod freelist to fix refcount/weakref test failures

Non-Option PyObjectRef fields retain references in freelist, causing
weakref and refcount assertions to fail in test_unittest, test_multiprocessing,
and test_socket.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-05 20:09:26 +09:00
CPython Developers
d1c73ac5b8 Update zipapp from v3.14.3 2026-03-05 17:25:59 +09:00
Copilot
5ab631db9d Enable stdio in barebone example so vm.print() works (#7356) 2026-03-05 12:57:56 +09:00
Jeong, YunWon
62766fd4a9 Stop non-main threads during interpreter finalization (#7349)
Raise SystemExit in check_signals() for non-main threads
once the finalizing flag is set. Without GIL, this serves
as the checkpoint where daemon threads detect shutdown,
analogous to CPython's _PyThreadState_MustExit in take_gil.
2026-03-05 10:21:44 +09:00
Jeong, YunWon
9a0511be0b Align specialization guards and caching with CPython (#7341)
* vm: complete specialized opcode dispatch paths

* vm: cache LOAD_GLOBAL with dict entry hints

* vm: align adaptive specialization counters with CPython backoff

* vm: apply cooldown counter on specialization success paths

* vm: retain LOAD_GLOBAL specializations on misses

* vm: keep attr and call specializations on guard misses

* vm: retain store-attr and store-subscr specializations on misses

* vm: retain specialization opcodes on generic fallback paths

* vm: align jump-backward specialization defaults with CPython

* vm: retain exact-args call specializations on misses

* vm: retain SEND_GEN specialization on non-coroutine sends

* vm: specialize list.append calls like CPython CALL_LIST_APPEND

* vm: set cooldown on LOAD_ATTR_CLASS specialization

* vm: specialize bound method object CALL paths

* vm: specialize CALL_KW for bound method objects

* vm: use current-state function version for CALL_KW specialization

* vm: align CALL/CALL_KW pyfunction specialization with CPython

* vm: drop call-site identity caches in generic CALL specializations

* vm: align builtin type call specializations with CPython guards

* vm: align builtin CALL guards with CPython self_or_null semantics

* vm: require exact list in CALL_LIST_APPEND fast path

* vm: align CALL builtin/class specialization flow with CPython

* vm: tighten len/isinstance CALL specializations to builtin guards

* vm: gate CALL_BUILTIN_CLASS on type vectorcall like CPython

* vm: run non-py CALL specializations via direct vectorcall

* vm: align class-call specialization branching with CPython

* Fix CI: disable ForIterGen, tighten CALL guards

- Disable ForIterGen specialization (falls through to generic
  path) because inline generator frame resumption is needed
  for correct debugger StopIteration visibility (test_bdb)
- Use downcast_ref_if_exact for PyNativeFunction in CALL
  specialization guards
- Add can_specialize_call guard for class __init__ specialization
- Remove expectedFailure for test_bad_newobj_args (now passing)
2026-03-05 01:59:44 +09:00
Jeong, YunWon
5c29074596 Replace GC tracking HashSet with intrusive linked list (#7328)
* Replace GC tracking HashSet with intrusive linked list

Replace per-generation HashSet<GcObjectPtr> with intrusive doubly-linked
lists for GC object tracking. Each PyInner now carries gc_pointers
(prev/next) and gc_generation fields, enabling O(1) track/untrack
without hashing.

- Add gc_pointers (Pointers<PyObject>) and gc_generation (u8) to PyInner
- Implement GcLink trait for intrusive list integration
- Replace generation_objects/permanent_objects/tracked_objects/finalized_objects
  HashSets with generation_lists/permanent_list LinkedLists
- Use GcBits::FINALIZED flag instead of finalized_objects HashSet
- Change default_dealloc to untrack directly before memory free
- Hold both src/dst list locks in promote_survivors to prevent race
  conditions with concurrent untrack_object calls
- Add pop_front to LinkedList for freeze/unfreeze operations
Move unreachable_refs creation before drop(gen_locks) so that raw
pointer dereferences and refcount increments happen while generation
list read locks are held. Previously, after dropping read locks, other
threads could untrack and free objects, causing use-after-free when
creating strong references from the raw GcPtr pointers.
2026-03-05 00:42:18 +09:00
Jeong, YunWon
745efbd8e8 Update collections from v3.14.3 (#7344)
* Update collections from v3.14.3

* Restore defaultdict fallback and update test markers

---------

Co-authored-by: CPython Developers <>
2026-03-05 00:32:53 +09:00
Jeong, YunWon
68ad332833 Remove Frame mutex and use DataStack bump allocator for LocalsPlus (#7333)
* Remove PyMutex<FrameState> from Frame, use UnsafeCell fields directly

Move stack, cells_frees, prev_line out of the mutex-protected FrameState
into Frame as FrameUnsafeCell fields. This eliminates mutex lock/unlock
overhead on every frame execution (with_exec).

Safety relies on the same single-threaded execution guarantee that
FastLocals already uses.

* Add thread-local DataStack for bump-allocating frame data

Introduce DataStack with linked chunks (16KB initial, doubling) and
push/pop bump allocation. Add datastack field to VirtualMachine.
Not yet wired to frame creation.

* Unify FastLocals and BoxVec stack into LocalsPlus

Replace separate FastLocals (Box<[Option<PyObjectRef>]>) and
BoxVec<Option<PyStackRef>> with a single LocalsPlus struct that
stores both in a contiguous Box<[usize]> array. The first
nlocalsplus slots are fastlocals and the rest is the evaluation
stack. Typed access is provided through transmute-based methods.

Remove BoxVec import from frame.rs.

* Use DataStack for LocalsPlus in non-generator function calls

Normal function calls now bump-allocate LocalsPlus data from the
per-thread DataStack instead of a separate heap allocation.
Generator/coroutine frames continue using heap allocation since
they outlive the call.

On frame exit, data is copied to the heap (materialize_to_heap)
to preserve locals for tracebacks, then the DataStack is popped.

VirtualMachine.datastack is wrapped in UnsafeCell for interior
mutability (safe because frame allocation is single-threaded LIFO).

* Fix clippy: import Layout from core::alloc instead of alloc::alloc

* Fix vectorcall compatibility with LocalsPlus API

Update vectorcall dispatch functions to use localsplus stack
accessors instead of direct stack field access. Add
stack_truncate method to LocalsPlus. Update vectorcall fast
path in function.rs to use datastack and fastlocals_mut().

* Add datastack, nlocalsplus, ncells, tstate to cspell dictionary

* Fix DataStack pop() for non-monotonic allocation addresses

Check both bounds of the current chunk when determining if a
pop base is in the current chunk. The previous check (base >=
chunk_start) fails on Windows where newer chunks may be
allocated at lower addresses than older ones.

* Fix stale comments: release_datastack -> materialize_localsplus

* Fix non-threading mode for parallel test execution

Two fixes for Cell-based types used in static items under non-threading
mode, which cause data races when Rust test runner uses parallel threads:

1. LazyLock: use std::sync::LazyLock when std is available instead of
   wrapping core::cell::LazyCell with a false `unsafe impl Sync`.
   The LazyCell wrapper is kept only for no-std (truly single-threaded).

2. gc_state: use static_cell! (thread-local in non-threading mode)
   instead of OnceLock, so each thread gets its own GcState with
   Cell-based PyRwLock/PyMutex that are not accessed concurrently.

* Fix CallAllocAndEnterInit to use LocalsPlus stack API

* Use checked arithmetic in LocalsPlus and DataStack allocators

* Address code review: checked arithmetic, threading feature deps, Send gate

- Use checked arithmetic for nlocalsplus in Frame::new
- Add "std" to threading feature dependencies in rustpython-common
- Gate GcState Send impl with #[cfg(feature = "threading")]

* Clean up comments: remove redundant/stale remarks, fix CPython references
2026-03-04 23:27:36 +09:00
Jeong, YunWon
a98c6469d1 freelist optimization (#7337)
* Add object freelist infrastructure and PyFloat freelist

Add freelist_push/freelist_pop hooks to PyPayload trait with default
no-op implementations. Modify default_dealloc to offer dead objects
to the freelist before freeing memory. Modify PyRef::new_ref to try
popping from the freelist before allocating.

Implement thread-local freelist for PyFloat (max 100 entries).
Float objects are ideal candidates: fixed-size payload (f64),
no GC tracking, no instance dict, trivial Drop.

Benchmark improvement on float-heavy workloads:
  float_arith: ~12% faster
  float_list:  ~17% faster

* Add PyList freelist and GC-safe freelist infrastructure

Add HAS_FREELIST const to PyPayload trait. For freelist types, GC
untracking is done immediately (not deferred) during dealloc to
prevent race conditions when the object is reused.

Restructure new_ref so freelist-popped objects also get GC tracked,
enabling freelist support for GC-tracked types like PyList.

Add drop_in_place for old payload before writing new one, required
for types with non-trivial Drop (e.g. PyList's RwLock<Vec>).

Implement thread-local freelist for PyList (max 80 entries).

* Add PyDict freelist and exact-type guard for freelist push

Add thread-local freelist for PyDict (max 80 entries).

Add heaptype check in default_dealloc to prevent subclass instances
from entering the freelist. Subclass objects have a different Python
type than the base type, so reusing them would return objects with
the wrong __class__.

Skip PyTuple freelist: structseq types (stat_result, struct_time)
are static subtypes sharing the same Rust payload, making type-safe
reuse impractical with heaptype check alone.

* Add PySlice freelist (max 1, matching PySlice_MAXFREELIST)

* Add FreeList<T> wrapper to drain cached objects on thread teardown

Replace Cell<Vec<*mut PyObject>> with Cell<FreeList<T>> which implements
Drop to properly deallocate PyInner<T> when threads exit.
Also fix freelist_pop safety doc to match actual contract.

* Add manual Traverse impl for PySlice with clear() and fix comment

* Deduplicate allocation fallback in PyRef::new_ref
2026-03-04 20:00:18 +09:00
Shahar Naveh
798f9e80cd Update libc to 0.2.182 (#7247)
* Update to libc 0.2.182, refactor update script

* Fix typing
2026-03-04 19:57:53 +09:00
Jeong, YunWon
c55a9ff728 Reinit IO buffer locks after fork to prevent deadlocks (#7339)
* Reinit IO buffer locks after fork to prevent deadlocks

BufferedReader/Writer/TextIOWrapper use PyThreadMutex internally.
If a parent thread held one of these locks during fork(), the child
would deadlock on any IO operation.

Add reinit_after_fork() to RawThreadMutex and call it on sys.stdin/
stdout/stderr in the child process fork handler, analogous to
CPython's _PyIO_Reinit().

* Address review: unsafe fn + decoder lock reinit

- Mark reinit_std_streams_after_fork as unsafe fn to encode
  fork-only precondition, update call site in posix.rs
- Reinit IncrementalNewlineDecoder's PyThreadMutex via
  TextIOWrapper's decoder field to prevent child deadlocks

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-04 19:54:10 +09:00
Jeong, YunWon
5a5ddbbed4 Restore defaultdict fallback and update test markers 2026-03-04 18:44:27 +09:00
CPython Developers
624ea3b13d Update collections from v3.14.3 2026-03-04 18:38:36 +09:00
Jeong, YunWon
3504993f28 Apply vectorcall for more places (#7336)
* Add PyType vectorcall and use vectorcall in all specialized call fallbacks

* Add vectorcall slot for PyMethodDescriptor and PyWrapper

* Add FuncArgs::from_vectorcall_owned and simplify vectorcall fallback paths

Remove has_vectorcall checks from execute_call_vectorcall and
execute_call_kw_vectorcall. The invoke_vectorcall fallback now uses
from_vectorcall_owned to move args instead of cloning.
2026-03-04 18:09:44 +09:00
Shahar Naveh
42628a53b9 Set codegen-untis=1 for some crates in release mode (#7332) 2026-03-04 17:59:59 +09:00
Claude
4c5f6332e8 Add SessionStart hook for dev environment setup
Configures automatic development environment setup for Claude Code web
sessions: upgrades python3 to 3.13+ and clones CPython source if missing.

https://claude.ai/code/session_01VPT2fpdf52qFZLMN8EDNgp
2026-03-04 17:53:05 +09:00
ShaharNaveh
ff5411110c Update smtplib.py from 3.14.3 2026-03-04 17:47:40 +09:00
Jeong, YunWon
c058add095 Specialized ops (#7322)
* Add CALL_ALLOC_AND_ENTER_INIT specialization

Optimizes user-defined class instantiation MyClass(args...)
when tp_new == object.__new__ and __init__ is a simple
PyFunction. Allocates the object directly and calls __init__
via invoke_exact_args, bypassing the generic type.__call__
dispatch path.

* Invalidate JIT cache when __code__ is reassigned

Change jitted_code from OnceCell to PyMutex<Option<CompiledCode>> so
it can be cleared on __code__ assignment. The setter now sets the
cached JIT code to None to prevent executing stale machine code.

* Atomic operations for specialization cache

- range iterator: deduplicate fast_next/next_fast
- Replace raw pointer reads/writes in CodeUnits with atomic
  operations (AtomicU8/AtomicU16) for thread safety
- Add read_op (Acquire), read_arg (Relaxed), compare_exchange_op
- Use Release ordering in replace_op to synchronize cache writes
- Dispatch loop reads opcodes atomically via read_op/read_arg
- Fix adaptive counter access: use read/write_adaptive_counter
  instead of read/write_cache_u16 (was reading wrong bytes)
- Add pre-check guards to all specialize_* functions to prevent
  concurrent specialization races
- Move modified() before attribute changes in type.__setattr__
  to prevent use-after-free of cached descriptors
- Use SeqCst ordering in modified() for version invalidation
- Add Release fence after quicken() initialization

* Fix slot wrapper override for inherited attributes

For __getattribute__: only use getattro_wrapper when the type
itself defines the attribute; otherwise inherit native slot from
base class via MRO.

For __setattr__/__delattr__: only store setattro_wrapper when
the type has its own __setattr__ or __delattr__; otherwise keep
the inherited base slot.

* Fix StoreAttrSlot cache overflow corrupting next instruction

write_cache_u32 at cache_base+3 writes 2 code units (positions 3 and 4),
but STORE_ATTR only has 4 cache entries (positions 0-3). This overwrites
the next instruction with the upper 16 bits of the slot offset.

Changed to write_cache_u16/read_cache_u16 since member descriptor offsets
fit within u16 (max 65535 bytes).

* Exclude method_descriptor from has_python_cmp check

has_python_cmp incorrectly treated method_descriptor as Python-level
comparison methods, causing richcompare slot to use wrapper dispatch
instead of inheriting the native slot.

* Fix CompareOpFloat NaN handling

partial_cmp returns None for NaN comparisons. is_some_and incorrectly
returned false for all NaN comparisons, but NaN != x should be true
per IEEE 754 semantics.

* Fix invoke_exact_args borrow in CallAllocAndEnterInit

* Distinguish Python method vs not-found in slot MRO lookup

Change lookup_slot_in_mro to return a 3-state SlotLookupResult
enum (NativeSlot/PythonMethod/NotFound) instead of Option<T>.

Previously, both "found a Python-level method" and "found nothing"
returned None, causing incorrect slot inheritance. For example,
class Test(Mixin, TestCase) would inherit object.slot_init from
Mixin via inherit_from_mro instead of using init_wrapper to
dispatch TestCase.__init__.

Apply this fix consistently to all slot update sites:
update_main_slot!, update_sub_slot!, TpGetattro, TpSetattro,
TpDescrSet, TpHash, TpRichcompare, SqAssItem, MpAssSubscript.

* Extract specialization helper functions to reduce boilerplate

- deoptimize() / deoptimize_at(): replace specialized op with base op
- adaptive(): decrement warmup counter or call specialize function
- commit_specialization(): replace op on success, backoff on failure
- execute_binary_op_int() / execute_binary_op_float(): typed binary ops

Removes 10 duplicate deoptimize_* functions, consolidates 13 adaptive
counter blocks, 6 binary op handlers, and 7 specialize tail patterns.
Also replaces inline deopt blocks in LoadAttr/StoreAttr handlers.

* Improve specialization guards and fix mark_stacks

- CONTAINS_OP_SET: add frozenset support in handler and specialize
- TO_BOOL_ALWAYS_TRUE: cache type version instead of checking slots
- LOAD_GLOBAL_BUILTIN: cache builtins dict version alongside globals
- mark_stacks: deoptimize specialized opcodes for correct reachability

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-04 15:39:48 +09:00
Shahar Naveh
e9001ec3e5 Bytecode enum named oparg (#7294)
* Align real opcodes

* Align pseudoes

* Add `consti` to known words
2026-03-03 21:33:59 +00:00
Shahar Naveh
bc108dfe86 Update trace.py from 3.14.3 (#7327)
* Update `trace.py` from 3.14.3

* Mark failing tests
2026-03-04 05:38:58 +09:00
Jeong, YunWon
1d106c3411 Add monitoring lock reinit and use raw() accessor (#7335)
Add missing reinit_mutex_after_fork for vm.state.monitoring
PyMutex in reinit_locks_after_fork.

Use Mutex::raw() accessor in reinit_parking_lot_mutex instead
of pointer cast from struct start for layout safety.
2026-03-04 04:35:52 +09:00
Jeong, YunWon
2f810ae0f6 Merge pull request #7330 from ShaharNaveh/update-test-frame
Add `test_frame.py` from 3.14.3
2026-03-04 04:30:54 +09:00
Jeong, YunWon
9b9c74973e make_class -> make_static_type (#7331)
* Remove unsafe ctx cast in make_class

Use Context::genesis() directly in make_class to obtain
&'static Context, eliminating the raw pointer cast.

* make_class -> make_static_type
2026-03-04 04:28:42 +09:00
Jeong, YunWon
be0c3ca3ca Add vectorcall (PEP 590) dispatch for function calls (#7329)
* Add vectorcall (PEP 590) dispatch for function calls

Add VectorCallFunc slot to PyTypeSlots and vectorcall dispatch path
in the interpreter loop for Call and CallKw instructions.

Implement vectorcall for PyFunction (with fast path for simple
positional-only calls that fills fastlocals directly), PyBoundMethod
(avoids prepend_arg O(n) shift), and PyNativeFunction.

Add FuncArgs::from_vectorcall helper for fallback conversion.
Vectorcall slot is inherited with call slot and cleared when
__call__ is overridden in Python subclasses.

* Optimize vectorcall: move args instead of clone, use vectorcall in specialized paths

- invoke_exact_args takes Vec by value and uses drain() to move args
  into fastlocals instead of cloning (eliminates refcount overhead)
- CallPyGeneral and CallBoundMethodGeneral now call vectorcall_function
  directly instead of going through FuncArgs + prepend_arg + invoke
- CallKwPy and CallKwBoundMethod use vectorcall_function with kwnames
- vectorcall_bound_method uses insert(0) on existing Vec instead of
  allocating a second Vec

* Auto-format: cargo fmt --all

* Fix vectorcall_native_function kwarg slice out-of-bounds

When needs_self was true and kwargs were present, pos_args only
contained positional args (self + original positionals) but
from_vectorcall expected kwarg values to follow in the slice.

Build the full args array (self + all original args including kwarg
values) before passing to from_vectorcall.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-03 23:51:56 +09:00
Jeong, YunWon
2f2a7da185 Add method cache (MCACHE) for MRO lookups (#7313)
4096-entry direct-mapped cache keyed by (tp_version_tag, interned_name_ptr)
using lock-free SeqLock pattern for concurrent access.

- find_name_in_mro() checks cache before MRO walk
- has_name_in_mro() avoids cloning on cache hit
- Auto-assigns version tags on cache miss
- Invalidation via modified() (tp_version_tag=0) with value drops
- TYPE_CACHE_CLEARING flag suppresses re-population during GC drops
- Clear method cache during GC collection to break reference cycles
2026-03-03 23:32:15 +09:00
ShaharNaveh
98021543d8 Mark failing test 2026-03-03 15:20:36 +01:00
ShaharNaveh
16066cc0fe Mark failing tests 2026-03-03 15:17:10 +01:00
ShaharNaveh
4e8a945277 Update test_frame.py from 3.14.3 2026-03-03 15:17:10 +01:00
Jeong, YunWon
62ca60ad82 Merge pull request #7318 from youknowone/utf8source
Update test_utf8source from v3.14.3 and implement it
2026-03-03 22:29:14 +09:00
ShaharNaveh
3cbd08f63c Update test_tuple.py from 3.14.3 2026-03-03 21:58:46 +09:00
Jeong, YunWon
53ddc7ef48 Remove expectedFailure for encoding tests now passing
Remove @unittest.expectedFailure decorators from:
- test_source_encoding: test_encoding_on_first_line, test_encoding_on_second_line, test_bom_conflict
- test_runpy: test_encoding
2026-03-03 21:27:11 +09:00
Jeong, YunWon
77eade37f1 Fix PEP 263 encoding detection per review
- Validate '#' is preceded only by whitespace/formfeed (PEP 263)
- Normalize UTF-8 encoding aliases (utf-8, utf_8, utf8, etc.)
2026-03-03 21:27:11 +09:00
CPython Developers
33fca0d77b Update test_utf8source from v3.14.3 2026-03-03 21:27:11 +09:00
ShaharNaveh
257b0c08a7 Update pkgutil.py from 3.14.3 2026-03-03 20:00:50 +09:00
ShaharNaveh
2ca3275097 Update test_xml_etree_c.py from 3.14.3 2026-03-03 20:00:30 +09:00
ShaharNaveh
7b33208455 Update test_raise.py from 3.14.3 2026-03-03 19:57:50 +09:00
Jeong, YunWon
25619aad1d Specialized ops (#7301)
* Add debug_assert to invoke_exact_args, lazy func_version reassignment

- Add debug_assert preconditions in invoke_exact_args
- Add get_version_for_current_state() for lazy version reassignment
  after func_version invalidation
- Document NEXT_TYPE_VERSION overflow policy

* working

* Add COMPARE_OP, TO_BOOL, FOR_ITER, LOAD_GLOBAL specialization

- COMPARE_OP: CompareOpInt, CompareOpFloat, CompareOpStr
- TO_BOOL: ToBoolBool, ToBoolInt, ToBoolNone, ToBoolList, ToBoolStr
- FOR_ITER: ForIterRange, ForIterList, ForIterTuple with fast_next()
- LOAD_GLOBAL: LoadGlobalModule, LoadGlobalBuiltin with dict version guard
- Add version counter to Dict for mutation tracking

* Add BINARY_SUBSCR, CONTAINS_OP, UNPACK_SEQUENCE, STORE_ATTR specialization

- BinaryOpSubscrListInt, BinaryOpSubscrTupleInt, BinaryOpSubscrDict
- ContainsOpDict, ContainsOpSet
- UnpackSequenceTwoTuple, UnpackSequenceTuple, UnpackSequenceList
- StoreAttrInstanceValue with type_version guard
- Deoptimize bytecode for marshal serialization (original_bytes)
- Separate co_code (deoptimized) from _co_code_adaptive (quickened)

* Add STORE_SUBSCR, BinaryOpAddUnicode, ToBoolAlwaysTrue, CallLen, CallIsinstance, CallType1 specialization

* Add BinaryOpSubscrStrInt, CallStr1, CallTuple1 specialization

* Add BinaryOpInplaceAddUnicode specialization

* Add LoadAttrModule, CallBuiltinO, CallPyGeneral, CallBoundMethodGeneral, ForIterGen, CallListAppend specialization

* Add LoadAttrNondescriptor*, CallMethodDescriptor* specialization

- LoadAttrNondescriptorNoDict: plain class attr on objects without dict
- LoadAttrNondescriptorWithValues: plain class attr with dict fallback
- LoadAttrClass: handler for type attribute access (not yet routed)
- CallMethodDescriptorNoargs: method descriptor with 0 args
- CallMethodDescriptorO: method descriptor with 1 arg
- CallMethodDescriptorFast: method descriptor with multiple args
- Use HAS_DICT flag instead of obj.dict().is_some() for method/nondescriptor routing

* Add CallBuiltinFast, CallNonPyGeneral specialization

- CallBuiltinFast: native function calls with arbitrary positional args
- CallNonPyGeneral: fallback for unmatched callables (custom __call__, etc.)
- All builtin function calls now specialize (CallBuiltinFast as default)
- specialize_call now always produces a specialized instruction

* Add SendGen specialization for generator/coroutine send

- SendGen: direct coro.send() for generator/coroutine receivers
- Add adaptive counter to Send instruction
- specialize_send checks builtin_coro for PyGenerator/PyCoroutine

* Add LoadAttrSlot, StoreAttrSlot specialization for __slots__ access

- LoadAttrSlot: direct obj.get_slot(offset) bypassing descriptor protocol
- StoreAttrSlot: direct obj.set_slot(offset, value) bypassing descriptor protocol
- Detect PyMemberDescriptor with MemberGetter::Offset in specialize_load_attr/store_attr
- Cache slot offset in cache_base+3

* Add LoadSuperAttrAttr, LoadSuperAttrMethod, CallBuiltinClass, CallBuiltinFastWithKeywords, CallMethodDescriptorFastWithKeywords specialization

* Add LoadAttrProperty specialization for property descriptor access

* Add LoadAttrClass specialization for class attribute access

* Add BinaryOpSubscrListSlice specialization

* Add CallKwPy, CallKwBoundMethod, CallKwNonPy specialization

Fix LoadSuperAttrMethod to push unbound descriptor + self
instead of bound method + self which caused double self binding.
Fix LoadSuperAttrAttr obj_arg condition for classmethod detection.

* Clean up comments in specialization code

Remove unnecessary CPython references, FIXME→TODO,
redundant Note: prefix, and "Same as" cross-references.

* fix check_signals

* fix import
2026-03-03 19:55:35 +09:00
Jeong, YunWon
7727acc6a7 Reduce per-instruction overhead in interpreter loop (#7315)
* Reduce per-instruction overhead in interpreter loop

- Defer prev_line update to when tracing is active
- Inline skip_caches_if_fallthrough, compute cache_entries once per instruction

* Unmark test_pdb_set_trace EXPECTED_FAILURE

prev_line deferral fix corrects pdb line tracking, making
these doctests pass.
2026-03-03 18:01:18 +09:00
Jeong, YunWon
7eb18210ca reinit-after-fork (#7321)
* Use Mutex::raw() accessor in reinit_mutex_after_fork

Use lock_api's Mutex::raw() to access the underlying RawMutex
instead of casting &PyMutex<T> directly. This avoids layout
assumptions about lock_api::Mutex<R, T> field ordering.

* Replace force_unlock with reinit_*_after_fork

Replace all force_unlock() + try_lock() patterns with zero-based
reinit that bypasses parking_lot internals entirely. After fork(),
the child is single-threaded so reinited locks won't contend.

Add reinit_rwlock_after_fork to common::lock alongside the existing
reinit_mutex_after_fork. Replace force_unlock_after_fork methods in
codecs, intern, and gc_state with reinit_after_fork equivalents.

This fixes after_fork_child silently dropping thread handles when
try_lock() failed on per-handle Arc<Mutex> locks.
2026-03-03 11:25:47 +09:00
Jeong, YunWon
1f6e47ce9c extend types and exceptions only for genesis (#7316) 2026-03-03 11:24:58 +09:00
Jeong, YunWon
43e2df31e0 Replace PyFunction.code PyMutex with PyAtomicRef for lock-free reads (#7317)
Change the code field from PyMutex<PyRef<PyCode>> to PyAtomicRef<PyCode>,
eliminating mutex lock/unlock on every function call. The setter uses
swap_to_temporary_refs for safe deferred drop of the old code object.
2026-03-03 09:18:49 +09:00
Jeong, YunWon
5f08a01cf4 Lazy locals dict for NEWLOCALS frames (#7307)
Defer locals dict allocation for function frames until first access.
Most function frames only use fastlocals and never touch the locals
dict, so this skips one dict heap allocation per function call.

Also remove a redundant code.clone() in invoke_with_locals.

Func call ~23% faster, method call ~15% faster in benchmarks.
2026-03-03 09:17:17 +09:00
Shahar Naveh
903a5f28a0 Update test_posix.py from 3.14.3 and adjust android cfg (#7320)
* Update `test_posix.py` from 3.14.3

* patch

* Align availability

* Fix test_fchmod_file
2026-03-03 09:09:47 +09:00
Jeong, YunWon
9a12a8d532 Fix _at_fork_reinit to write INIT directly instead of calling unlock() (#7312)
* Fix _at_fork_reinit to write INIT directly instead of calling unlock()

unlock() goes through unlock_slow() which accesses parking_lot's
global hash table to unpark waiters. After fork(), this hash table
contains stale entries from dead parent threads, making unlock_slow()
unsafe. Writing INIT directly bypasses parking_lot internals entirely.

* Add import lock (IMP_LOCK) reinit after fork

The import lock is a ReentrantMutex that was never reinit'd after
fork(). If a parent thread held it during fork, the child would
deadlock on any import. Only reset if the owner is a dead thread;
if the surviving thread held it, normal unlock still works.
2026-03-03 09:08:12 +09:00
dependabot[bot]
c71f4ada21 Bump github/gh-aw from 0.51.4 to 0.51.5 (#7319)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.51.4 to 0.51.5.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](e422993ded...88319be75a)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.51.5
  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-03-03 09:04:30 +09:00
Lee Dogeon
0a6a6f8ddb Prevent shell injection (#7310) 2026-03-02 19:45:58 +09:00
Jeong, YunWon
3865fdbf5b Revert class lookup (#7306)
* Revert __class__ lookup skip in object_isinstance

The optimization to skip __class__ lookup based on getattro check
was incorrect: a class can override __class__ as a property while
still using standard __getattribute__. Revert to always performing
the lookup, matching CPython's object_isinstance behavior.

* Collapse nested if in object_isinstance for clippy
2026-03-02 19:41:43 +09:00
Jeong, YunWon
baba1f9447 Fix symbol table sub_table desync for non-simple annotation targets (#7300)
* Fix symbol table sub_table desync for non-simple annotation targets

Non-simple annotations (subscript/attribute/parenthesized targets like
`a[0]: expr`) were scanned in the annotation scope during symbol table
analysis, creating sub_tables for any comprehensions. But codegen only
compiles simple name annotations into __annotate__, so those sub_tables
were never consumed. This caused subsequent simple annotations'
comprehension sub_tables to get the wrong index, resulting in
"the symbol 'X' must be present in the symbol table" errors.

Fix: skip entering annotation scope for non-simple annotations since
they are never compiled into __annotate__.

* Validate forbidden expressions in non-simple annotation targets

Fix cspell "desynchronize" warning and validate yield/await/named/async
comprehension expressions in non-simple annotations without creating
annotation scopes.

* Restore in_annotation flag before propagating error
2026-03-02 16:47:16 +09:00
Jeong, YunWon
c315033091 Implement LOAD_ATTR inline caching with adaptive specialization (#7292)
* Implement LOAD_ATTR inline caching with adaptive specialization

Add type version counter (tp_version_tag) to PyType with subclass
invalidation cascade. Add cache read/write methods (u16/u32/u64)
to CodeUnits. Implement adaptive specialization in load_attr that
replaces the opcode with specialized variants on first execution:

- LoadAttrMethodNoDict: cached method lookup for slotted types
- LoadAttrMethodWithValues: cached method with dict shadow check
- LoadAttrInstanceValue: direct dict lookup skipping descriptors

Specialized opcodes guard on type_version_tag and deoptimize back
to generic LOAD_ATTR with backoff counter on cache miss.

* Add BINARY_OP and CALL adaptive specialization

BINARY_OP: Specialize int add/subtract/multiply and float
add/subtract/multiply with type guards and deoptimization.

CALL: Add func_version to PyFunction, specialize simple
function calls (CallPyExactArgs, CallBoundMethodExactArgs)
with invoke_exact_args fast path that skips FuncArgs
allocation and fill_locals_from_args.

* Lazy quickening for adaptive specialization counters

Move counter initialization from compile-time to RESUME execution,
matching CPython's _PyCode_Quicken pattern. Store counter in CACHE
entry's arg byte to preserve op=Instruction::Cache for dis/JIT.
Add PyCode.quickened flag for one-time initialization.

* Add Instruction::deoptimize() and CodeUnits::original_bytes()

- deoptimize() maps specialized opcodes back to their base adaptive variant
- original_bytes() produces deoptimized bytecode with zeroed CACHE entries
- co_code now returns deoptimized bytes, _co_code_adaptive returns current bytes
- Marshal serialization uses original_bytes() instead of raw transmute

* Fix monitoring and specialization interaction

- cache_entries() returns correct count for instrumented opcodes
- deoptimize() maps instrumented opcodes back to base
- quicken() skips adaptive counter for instrumented opcodes
- instrument_code Phase 3 deoptimizes specialized opcodes and
  clears CACHE entries to prevent stale pointer dereferences

* Address review: bounds checks, UB fix, version overflow, error handling

- Add bounds checks to read_cache_u16/u32/u64
- Fix quicken() aliasing UB by using &mut directly
- Add JumpBackwardJit/JumpBackwardNoJit to deoptimize()
- Guard can_specialize_call with NEWLOCALS flag check
- Use compare_exchange_weak for version tag to prevent wraparound
- Propagate dict lookup errors in LoadAttrMethodWithValues
- Apply adaptive backoff on version tag assignment failure
- Remove duplicate imports in frame.rs
2026-03-02 16:36:57 +09:00
Jeong, YunWon
b1cddc4e0c Merge pull request #7296 from youknowone/sysconfig
Update sysconfig from v3.14.3 and also fix _sysconfig
2026-03-02 13:13:20 +09:00
Jeong, YunWon
3a81f94aa8 Optimize unpack, str.__add__ and fastlocals (#7293)
* Remove intermediate Vec allocation in unpack_sequence fast path

Push elements directly from tuple/list slice in reverse order
instead of cloning into a temporary Vec first.

* Use read-only atomic load before swap in check_signals

Add Relaxed load guard before the Acquire swap to avoid cache-line
invalidation on every instruction dispatch when no signal is pending.

* Cache builtins downcast in ExecutingFrame for LOAD_GLOBAL

Pre-compute builtins.downcast_ref::<PyDict>() at frame entry and reuse
the cached reference in load_global_or_builtin and LoadBuildClass.
Also add get_chain_exact to skip redundant exact_dict type checks.

* Add number Add slot to PyStr for direct str+str dispatch

binary_op1 can now resolve str+str addition directly via the number
slot instead of falling through to the sequence concat path.

* Guard FastLocals access in locals() with try_lock on state mutex

Address CodeRabbit review: f_locals() could access fastlocals without
synchronization when called from another thread. Use try_lock on the
state mutex so concurrent access is properly serialized.

* Use exact type check for builtins_dict cache

downcast_ref::<PyDict>() matches dict subclasses, causing
get_chain_exact to bypass custom __getitem__ overrides.
Use downcast_ref_if_exact to only fast-path exact dict types.

* Consolidate with_recursion in _cmp to single guard

Move the recursion depth check to wrap the entire _cmp body
instead of each individual call_cmp direction, reducing Cell
read/write pairs and scopeguard overhead per comparison.

* Add opcode-level fast paths for FOR_ITER, COMPARE_OP, BINARY_OP

- FOR_ITER: detect PyRangeIterator and bypass generic iterator
  protocol (atomic slot load + indirect call)
- COMPARE_OP: inline int/float comparison for exact types,
  skip rich_compare dispatch and with_recursion overhead
- BINARY_OP: inline int add/sub with i64 checked arithmetic
  to avoid BigInt heap allocation and binary_op1 dispatch

* Also check globals is exact dict for LOAD_GLOBAL fast path

get_chain_exact bypasses __missing__ on dict subclasses.
Move get_chain_exact to PyExact<PyDict> impl with debug_assert,
and have get_chain delegate to it. Store builtins_dict as
Option<&PyExact<PyDict>> to enforce exact type at compile time.

Use PyRangeIterator::next_fast() instead of pub(crate) fields.
Fix comment style issues.
2026-03-02 12:37:43 +09:00
dependabot[bot]
7b89d8252e Bump github/gh-aw from 0.51.2 to 0.51.4 (#7305)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.51.2 to 0.51.4.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](cccf96100f...e422993ded)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.51.4
  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-03-02 11:08:22 +09:00
Jeong, YunWon
888e0eeccb Add str(int) and repr(int) fast path using i64 conversion (#7302)
* Add str(int) and repr(int) fast path using i64 conversion

- Skip __str__ method resolution for exact PyInt in PyObject::str()
- Use i64::to_string() for small integers, BigInt::to_string() for large ones
- ~36% improvement in str(int) benchmarks

* Extract PyInt::to_str_radix_10() to deduplicate i64 fast path logic
2026-03-02 11:02:49 +09:00
Jeong, YunWon
69fc75b22d Skip __class__ lookup in object_isinstance for standard getattro (#7303)
In object_isinstance(), when is_subtype() returns false, the __class__
attribute lookup via get_attribute_opt is redundant for objects using
standard __getattribute__, since __class__ is a data descriptor on
object that always returns obj.class().
2026-03-02 10:18:45 +09:00
fanninpm
f443226e46 Add crypto group to dependabot config (#7304) 2026-03-02 10:18:01 +09:00
CPython Developers
5a11f33751 Update sysconfig from v3.14.3 2026-03-02 01:24:15 +09:00
Jeong, YunWon
d0b5a5af50 [update_lib] fast date lookup for todo (#7299)
* [update_lib] fast date lookup for todo

* add deps

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-02 01:23:52 +09:00
Shahar Naveh
ca390dc5fb io.TextIoWrapper.reconfigure to always flush (#7281)
* Always flush on reconfigure (io.TextWrapper)

* Add test
2026-03-02 01:03:44 +09:00
dependabot[bot]
a6b3a5b6bc Bump github/gh-aw from 0.50.7 to 0.51.2 (#7295)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.50.7 to 0.51.2.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](9cbca3cd9b...cccf96100f)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.51.2
  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-03-01 23:39:56 +09:00
Jeong, YunWon
5eadae8c2e Implement PyStackRef borrowed stack references for LOAD_FAST_BORROW (#7290)
* Implement PyStackRef borrowed stack references for LOAD_FAST_BORROW

* Fix PyStackRef: use NonZeroUsize for niche optimization, revert borrowed refs

- Change PyStackRef.bits from usize to NonZeroUsize so Option<PyStackRef>
  has the same size as Option<PyObjectRef> via niche optimization
- Revert LoadFastBorrow to use clone instead of actual borrowed refs
  to avoid borrowed refs remaining on stack at yield points
- Add static size assertions for Option<PyStackRef>
- Add stackref, fastlocal to cspell dictionary
- Remove debug eprintln statements
- Fix clippy warning for unused push_borrowed

* Add borrowed ref debug_assert to InstrumentedYieldValue, clean up comments
2026-03-01 23:32:56 +09:00
dependabot[bot]
58a0f74c76 Bump aws-lc-rs from 1.15.4 to 1.16.0 (#7298)
Bumps [aws-lc-rs](https://github.com/aws/aws-lc-rs) from 1.15.4 to 1.16.0.
- [Release notes](https://github.com/aws/aws-lc-rs/releases)
- [Commits](https://github.com/aws/aws-lc-rs/compare/v1.15.4...v1.16.0)

---
updated-dependencies:
- dependency-name: aws-lc-rs
  dependency-version: 1.16.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-03-01 23:27:40 +09:00
Shahar Naveh
707135f851 Add npm to dependabot (#7291) 2026-03-01 21:55:34 +09:00
Jeong, YunWon
d23eb4af6c Add sysconfig build vars and fix value types
- Add prefix, exec_prefix, BINDIR to sysconfigdata build_time_vars
- Add Py_DEBUG and ABIFLAGS to sysconfigdata
- Fix Py_GIL_DISABLED/Py_DEBUG to use int (1/0) instead of bool
- Gitignore generated _sysconfig_vars*.json
2026-03-01 21:53:42 +09:00
Jeong, YunWon
2b084457ef Optimize fast_locals and atomic ordering (#7289)
* Relax RefCount atomic ordering from SeqCst to Arc pattern

- inc/inc_by/get: SeqCst → Relaxed
- safe_inc CAS: SeqCst → Relaxed + compare_exchange_weak
- dec: SeqCst → Release + Acquire fence when count drops to 0
- leak CAS: SeqCst → AcqRel/Relaxed + compare_exchange_weak

* Reuse existing Vec via prepend_arg in execute_call

Replace vec![self_val] + extend(args.args) with
FuncArgs::prepend_arg() to avoid a second heap allocation
on every method call.

* Skip downcast_ref checks in invoke when tracing is disabled

Early return in PyCallable::invoke() when use_tracing is false,
avoiding two downcast_ref type checks on every function call.

* Replace fastlocals PyMutex with UnsafeCell-based FastLocals

Eliminate per-instruction mutex lock/unlock overhead for local
variable access. FastLocals uses UnsafeCell with safety guaranteed
by the frame's state mutex and sequential same-thread execution.

Affects 14+ lock() call sites in hot instruction paths (LoadFast,
StoreFast, DeleteFast, and their paired variants).

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-01 20:53:12 +09:00
CPython Developers
333ce69374 Update zoneinfo from v3.14.3 2026-03-01 20:51:10 +09:00
CPython Developers
e68fac8655 Update tempfile from v3.14.3 2026-03-01 20:50:21 +09:00
ShaharNaveh
367aff93f0 Update test_dynamicclassattribute.py from 3.14.3 2026-03-01 19:27:33 +09:00
Jeong, YunWon
ae8c0b3a93 skip flaky weakref test (#7286) 2026-03-01 15:28:23 +09:00
Jeong, YunWon
ccd377cc47 instruction CACHE (#7251)
* Emit CACHE code units in bytecode to match CPython 3.14

- Add cache_entries() method to Instruction enum
- Emit CACHE code units after opcodes in finalize_code
- Handle NO_LOCATION (line=-1) in linetable for CACHE entries
- Account for CACHE entries in exception table generation
- Skip CACHE entries in VM execution loop (with jump detection)
- Handle CACHE in InstrumentedLine/InstrumentedInstruction/InstrumentedForIter/InstrumentedNotTaken
- Skip CACHE in monitoring instrumentation phases
- Update co_branches() for cache-adjusted offsets
- Restore _cache_format in Lib/opcode.py
- Remove expectedFailure from test_c_call, test_start_offset

* Use relative jump offsets and fix bytecode layout

- Convert jump arguments from absolute to relative offsets
  in frame.rs, monitoring.rs, and stack_analysis
- Add jump_relative_forward/backward helpers to ExecutingFrame
- Resolve pseudo jump instructions before offset fixpoint loop
- Emit NOP for break, continue, pass to match line-tracing
- Fix async for: emit EndAsyncFor with correct target, add NotTaken
- Fix comprehension if-cleanup to use separate block
- Fix super() source range for multi-line calls
- Fix NOP removal to preserve line-marker NOPs
- Fix InstrumentedLine cache skipping after re-dispatch
- Match InstrumentedResume/YieldValue in yield_from_target
- Remove CALL_FUNCTION_EX cache entry from opcode.py
- Remove resolved expectedFailure markers

* Align CPython 3.14 LOAD_GLOBAL null-bit and RERAISE semantics

* Remove redundant CPython-referencing comments

Clean up comments that unnecessarily mention CPython per project
convention. Replace with concise descriptions of the behavior itself.
2026-03-01 14:51:54 +09:00
Jeong, YunWon
e4938cf61e Merge pull request #7283 from youknowone/ssl
Update ssl from v3.14.3 and add _ssl.HAS_PHA
2026-03-01 14:49:08 +09:00
Jeong, YunWon
130c771538 Update random from v3.14.3 and fix getrandbits (#7270)
* Update random from v3.14.3

* getrandbits and itertools.repeat now accept __index__ protocol.

---------

Co-authored-by: CPython Developers <>
2026-03-01 13:20:32 +09:00
Jeong, YunWon
dd6cf74fd7 Implement trace/profile events and opcode tracing for generators (#7268)
- Add TraceEvent::Exception and Opcode variants with profile filtering
- Extract dispatch_traced_frame helper for Call/Return trace events
- Fire exception trace on new raises, SEND StopIteration, FOR_ITER StopIteration
- Fire opcode trace events gated by f_trace_opcodes
- Move prev_line to FrameState for persistence across generator suspend/resume
- Reset prev_line in gen_throw for correct LINE monitoring after yield
- Add per-code event mask (events_for_code) to prevent unrelated code instrumentation
- Remove expectedFailure markers from test_bdb (5) and test_sys_setprofile (14)
2026-03-01 13:18:37 +09:00
github-actions[bot]
3e3165bc42 Auto-format: cargo fmt --all 2026-03-01 02:38:15 +00:00
Jeong, YunWon
c29b96d09e Fix ssl options setter to raise ValueError for negative values and mark still-failing tests 2026-03-01 11:37:19 +09:00
CPython Developers
4d0f52adc9 Update ssl from v3.14.3 2026-03-01 11:36:46 +09:00
fanninpm
919a14623d Remove test_importlib from polluters (#7279) 2026-03-01 10:58:47 +09:00
Shahar Naveh
5a56a76fad Update test_set.py from 3.14.3 (#7282)
* Update `test_set.py` from 3.14.3

* Cleanup code a bit

* Mark failing tests
2026-03-01 10:58:23 +09:00
Jeong, YunWon
28299e3136 Add missing HAS_PHA constant to _ssl module 2026-03-01 10:57:44 +09:00
Jiseok CHOI
be29462087 Fix _hashlib.compare_digest to reject non-ASCII strings (#7280)
Add non-ASCII string check to _hashlib.compare_digest, matching the
behavior of _operator._compare_digest. When both arguments are strings,
non-ASCII characters now correctly raise TypeError.

Also replace the non-constant-time == comparison with constant_time_eq
for proper timing-attack resistance, and return PyResult<bool> instead
of PyResult<PyObjectRef>.
2026-03-01 10:51:39 +09:00
Jiseok CHOI
7b7c7a1b6e sqlite3: add Cursor.row_factory pygetset and fix Connection.cursor() propagation (#7278)
Add #[pygetset] getter/setter for Cursor.row_factory so that Python-level
attribute access reads/writes the Rust struct field instead of the
instance dict.

Fix Connection.cursor() to only propagate the connection's row_factory
to the cursor when the connection's row_factory is not None, matching
CPython behavior. Previously it unconditionally overwrote the cursor's
row_factory, discarding any factory set by a cursor subclass __init__.
2026-03-01 10:44:50 +09:00
Jiseok CHOI
be39a67f27 Handle reentrant detach in TextIOWrapper.close() (#7277)
When a custom `closed` property on the underlying buffer calls
`detach()` during the `file_closed()` check in `close()`, the
wrapper's internal buffer becomes None. Subsequent flush/close
operations then fail with AttributeError on NoneType.

Add a guard after the `file_closed()` check to detect if the buffer
was detached reentrantly, and return early in that case (detach has
already flushed the stream).

This mirrors the fix applied in CPython
(https://github.com/python/cpython/issues/142594\).
2026-03-01 10:44:13 +09:00
Shahar Naveh
bef46f17d5 Fix list.__repr__ deadlock (#7275)
* Fix list repr deadlock

* Unmark apssing test
2026-03-01 10:43:23 +09:00
ShaharNaveh
11ddf6e065 Mark failing tests 2026-03-01 10:42:37 +09:00
ShaharNaveh
9fad2c0b1d Update test_format.py from 3.14.3 2026-03-01 10:42:37 +09:00
ShaharNaveh
48d574f90f Update test_strtod.py 2026-03-01 10:42:37 +09:00
ShaharNaveh
3b0be19b10 Update test_string_literals.py from 3.14.3 2026-03-01 10:42:37 +09:00
ShaharNaveh
1731984393 Update test_str.py to 3.14.3 2026-03-01 10:42:37 +09:00
ShaharNaveh
d6adfb0b59 Update test_dynamic.py to 3.14.3 2026-03-01 10:39:06 +09:00
Shahar Naveh
b05e74b78e Don't return frozenset for modules (#7276) 2026-03-01 09:43:34 +09:00
Jiseok CHOI
2833cd8896 add isatty() method in BytesIO (#7243) 2026-02-28 15:55:12 +00:00
dependabot[bot]
a4a9b29687 Bump env_logger from 0.11.8 to 0.11.9 (#7267)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.8 to 0.11.9.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.8...v0.11.9)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-version: 0.11.9
  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-03-01 00:36:06 +09:00
dependabot[bot]
cc5f9edd43 Bump junction from 1.4.1 to 1.4.2 (#7266)
Bumps [junction](https://github.com/tesuji/junction) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/tesuji/junction/releases)
- [Changelog](https://github.com/tesuji/junction/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tesuji/junction/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: junction
  dependency-version: 1.4.2
  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-03-01 00:35:48 +09:00
dependabot[bot]
85d4c42a6b Bump actions/upload-artifact from 6.0.0 to 7.0.0 (#7264)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.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-02-28 23:18:04 +09:00
dependabot[bot]
dd84a19b5b Bump github/gh-aw from 0.49.4 to 0.50.7 (#7262)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.49.4 to 0.50.7.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](bf34f99475...9cbca3cd9b)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.50.7
  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-02-28 23:17:53 +09:00
dependabot[bot]
5861393ad8 Bump actions/download-artifact from 7.0.0 to 8.0.0 (#7263)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.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-02-28 23:17:38 +09:00
Jeong, YunWon
8676ba9128 getrandbits and itertools.repeat now accept __index__ protocol. 2026-02-28 22:58:56 +09:00
CPython Developers
c4de2a7239 Update random from v3.14.3 2026-02-28 22:50:51 +09:00
Jeong, YunWon
cc4a7bbbe5 Merge pull request #7255 from youknowone/bdb
Update bdb from v3.14.3 and fix trace_event
2026-02-28 22:35:36 +09:00
Shahar Naveh
f3cf86c8c2 More accurate test dependencies (#7261) 2026-02-28 22:11:07 +09:00
dependabot[bot]
cdc7842bc4 Bump uuid from 1.20.0 to 1.21.0 (#7258)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.20.0 to 1.21.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.20.0...v1.21.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.21.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-02-28 22:07:49 +09:00
fanninpm
f5a071d52d Edit dependency groups (#7257)
* Add random dependency group

See #7064 for why.

* Fold cranelift group into wasmtime group
2026-02-28 22:07:29 +09:00
dependabot[bot]
5c726e1c55 Bump chrono from 0.4.43 to 0.4.44 (#7256)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.43 to 0.4.44.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.43...v0.4.44)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.44
  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-02-28 03:34:08 +00:00
dependabot[bot]
32684f9d45 Bump the cranelift group with 3 updates (#7252)
Bumps the cranelift group with 3 updates: [cranelift](https://github.com/bytecodealliance/wasmtime), [cranelift-jit](https://github.com/bytecodealliance/wasmtime) and [cranelift-module](https://github.com/bytecodealliance/wasmtime).


Updates `cranelift` from 0.128.3 to 0.129.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-jit` from 0.128.3 to 0.129.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.128.3 to 0.129.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
  dependency-version: 0.129.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cranelift
- dependency-name: cranelift-jit
  dependency-version: 0.129.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cranelift
- dependency-name: cranelift-module
  dependency-version: 0.129.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cranelift
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-28 03:27:28 +00:00
Jeong, YunWon
754fc85fb8 Fix trace_event to return trace function result
- Return trace function's return value from trace_event()
  to support per-frame f_trace assignment
- Match CPython's trace_trampoline: set f_trace from call
  event return value, clear on error
- Fire return event only when frame is traced or profiled
- Remove expectedFailure from passing bdb/settrace tests
2026-02-28 12:09:59 +09:00
CPython Developers
78c5a2ec28 Update bdb from v3.14.3 2026-02-28 12:09:59 +09:00
Jiseok CHOI
81acb9b7c1 sqlite3: handle int type in Connection.timeout parameter (#7237) 2026-02-28 10:38:30 +09:00
Jiseok CHOI
5a1b3355c2 fix: add readinto and write methods to RawIOBase (#7234) 2026-02-28 10:37:26 +09:00
Shahar Naveh
e0eb6d5e8e Reapply "ast.NodeVisitor for import tracking" (#7241)
* Reapply "`ast.NodeVisitor` for import tracking (#7229)" (#7230)

This reverts commit a47572c89e.
2026-02-28 10:37:00 +09:00
Shahar Naveh
4092346f95 Update subprocess.py to 3.14.3 (#7244) 2026-02-28 10:36:09 +09:00
Jeong, YunWon
0e48ca95f2 extend timeout 2026-02-28 10:35:38 +09:00
Jeong, YunWon
211823e498 Fix dict race condition 2026-02-28 10:35:38 +09:00
Shahar Naveh
72570c521c Update CI ruff to 0.15.4 (#7250)
* Update CI ruff to 0.15.4

* Format
2026-02-28 10:34:38 +09:00
Shahar Naveh
2447d99b12 Specify minor version for pyo3 (#7248)
* Specify minor version for pyo3

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-28 10:34:04 +09:00
Shahar Naveh
e51d6f0df7 Disallow warnings patches (#7249)
* Apply patches

* Remove struct cfg_attr

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-27 23:48:09 +09:00
Shahar Naveh
4f577e5f99 Update ruff to 0.15.4 (#7246)
* Update ruff to 0.15.4

* Unmark passing test
2026-02-27 22:59:41 +09:00
Jiseok CHOI
221dfc2e02 Fix IOBase.isatty() to raise ValueError on closed files (#7233)
* Fix IOBase.isatty() to raise ValueError on closed files

* Add snippet test for IOBase.isatty() closed check
2026-02-27 21:04:47 +09:00
Jiseok CHOI
c5472e2a4e sqlite3: reject negative fetchmany values (#7240) 2026-02-27 21:03:40 +09:00
Jiseok CHOI
f7fe9c53cb sqlite3: reject negative arraysize values (#7238) 2026-02-27 20:59:15 +09:00
Jiseok CHOI
f8d8e25de4 Fix IOBase.fileno() incorrect error message (#7232)
fileno() was calling _unsupported with "truncate" instead of "fileno",
causing UnsupportedOperation('truncate') to be raised instead of
UnsupportedOperation('fileno').

CPython 3.14 reference: Modules/_io/iobase.c calls iobase_unsupported(state, "fileno")
Fixes: ever0de/RustPython#1
2026-02-27 20:46:42 +09:00
Jeong, YunWon
89fb3494f4 Merge pull request #7223 from youknowone/tarfile
Update tarfile from v3.14.3 and fix bugs
2026-02-27 20:44:16 +09:00
Shahar Naveh
8b27eaf8f1 Update test_atexit.py to 3.14.3 (#7217) 2026-02-27 19:29:59 +09:00
Jeong, YunWon
f1511c4be0 Merge pull request #7242 from ShaharNaveh/update-xml
Update `xml` to 3.14.3
2026-02-27 19:25:25 +09:00
ShaharNaveh
eed53cbe9d Patch test_sax.py 2026-02-27 10:05:21 +01:00
ShaharNaveh
fa68faa2e5 Mark failing tests at test_pyexpat.py 2026-02-27 09:58:46 +01:00
ShaharNaveh
b7589a752b Mark failing tests on test_minidom.py 2026-02-27 09:55:31 +01:00
ShaharNaveh
c6562efce5 Patch test_xml_etree.py 2026-02-27 09:49:35 +01:00
ShaharNaveh
fd5e91dbff Update xml to 3.14.3 2026-02-27 09:44:41 +01:00
Jeong, YunWon
36cffabeda xz2->liblzma and remove liblzma-sys 2026-02-27 17:39:12 +09:00
Jeong, YunWon
ff02c266ac Unmark test_lzma 2026-02-27 16:53:55 +09:00
Jeong, YunWon
3be78c6545 new_lzma_error 2026-02-27 16:53:55 +09:00
CPython Developers
df9fb69de5 Update tarfile from v3.14.3 2026-02-27 16:53:55 +09:00
Jiseok CHOI
09cf49204b fix: add flush method to BytesIO (#7235) 2026-02-27 16:53:17 +09:00
Shahar Naveh
4c2537010d Update http from 3.14.3 (#7137)
* Update `http` from 3.14.3

* Reapply patch

* Update `test/certdata` from 3.14.3

* Revert "Update `test/certdata` from 3.14.3"

This reverts commit fa8fb388b3.

* Update `test_httpservers.py`

* Reapply long test patch

* Mark failing tests

* Skip flaky test

* Allow password to be None

* Unmark passing test

* Fix error message

* Clippy

---------

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2026-02-27 05:44:37 +00:00
Jeong, YunWon
684001ffa2 Instrumented instruction (#7212)
* Update test_monitoring from CPython 3.14.3

* patch test_monitoring not to entirely skipped

* impl sys.monitoring

* Update test_compile from v3.14.3

* instrumented instructions

* Implement side-table instrumented opcode execution

Add CoMonitoringData with line_opcodes and per_instruction_opcodes
side-tables. INSTRUMENTED_LINE and INSTRUMENTED_INSTRUCTION read
original opcodes from side-tables and re-dispatch after firing events.

- Add decode_exception_table() and CodeUnits::replace_op()
- Add Instruction::to_instrumented/to_base/is_instrumented mappings
- Three-phase instrument_code: de-instrument, re-instrument regular,
  then layer INSTRUCTION and LINE with side-table storage
- Mark exception handler targets as line starts in LINE placement
- InstrumentedLine resolves side-table chain atomically when wrapping
  InstrumentedInstruction
- InstrumentedForIter fires both BRANCH_LEFT and BRANCH_RIGHT
- Remove callback on DISABLE return for non-local events

* mark failing tests

* set_f_lineno, set_f_lasti, PyAtomic refactor

- Implement set_f_lineno with stack analysis and deferred unwinding
- Add Frame::set_lasti() for trace callback line jumps
- Implement co_branches() on code objects
- Clear _cache_format in opcode.py (no inline caches)
- Fix getattro slot inheritance: preserve native slot from inherit_slots
- Fix BRANCH_RIGHT src_offset in InstrumentedPopJumpIf*
- Move lasti increment before line event for correct f_lineno
- Skip RESUME instruction from generating line events
- Defer stack pops via pending_stack_pops/pending_unwind_from_stack
  to avoid deadlock with state mutex
- Fix ForIter exhaust target in mark_stacks to skip END_FOR
- Prevent exception handler paths from overwriting normal-flow stacks
- Replace #[cfg(feature = "threading")] duplication with PyAtomic<T>
  from rustpython_common::atomic (Radium-based unified API)
- Remove expectedFailure from 31 now-passing jump tests

* Unmark successful tests

test_peepholer
test_bdb

---------

Co-authored-by: CPython Developers <>
2026-02-27 13:23:33 +09:00
Jeong, YunWon
1fdd4fd35c Fix dict concurrency stability (#7231) 2026-02-27 13:23:12 +09:00
Jeong, YunWon
4567c689ce Unmark successful tests
test_peepholer
test_bdb
2026-02-27 09:07:23 +09:00
Jeong, YunWon
20a93c54c4 set_f_lineno, set_f_lasti, PyAtomic refactor
- Implement set_f_lineno with stack analysis and deferred unwinding
- Add Frame::set_lasti() for trace callback line jumps
- Implement co_branches() on code objects
- Clear _cache_format in opcode.py (no inline caches)
- Fix getattro slot inheritance: preserve native slot from inherit_slots
- Fix BRANCH_RIGHT src_offset in InstrumentedPopJumpIf*
- Move lasti increment before line event for correct f_lineno
- Skip RESUME instruction from generating line events
- Defer stack pops via pending_stack_pops/pending_unwind_from_stack
  to avoid deadlock with state mutex
- Fix ForIter exhaust target in mark_stacks to skip END_FOR
- Prevent exception handler paths from overwriting normal-flow stacks
- Replace #[cfg(feature = "threading")] duplication with PyAtomic<T>
  from rustpython_common::atomic (Radium-based unified API)
- Remove expectedFailure from 31 now-passing jump tests
2026-02-27 09:07:23 +09:00
Jeong, YunWon
fa1e75a809 mark failing tests 2026-02-27 09:07:23 +09:00
Jeong, YunWon
9c329bd62f Implement side-table instrumented opcode execution
Add CoMonitoringData with line_opcodes and per_instruction_opcodes
side-tables. INSTRUMENTED_LINE and INSTRUMENTED_INSTRUCTION read
original opcodes from side-tables and re-dispatch after firing events.

- Add decode_exception_table() and CodeUnits::replace_op()
- Add Instruction::to_instrumented/to_base/is_instrumented mappings
- Three-phase instrument_code: de-instrument, re-instrument regular,
  then layer INSTRUCTION and LINE with side-table storage
- Mark exception handler targets as line starts in LINE placement
- InstrumentedLine resolves side-table chain atomically when wrapping
  InstrumentedInstruction
- InstrumentedForIter fires both BRANCH_LEFT and BRANCH_RIGHT
- Remove callback on DISABLE return for non-local events
2026-02-27 09:07:22 +09:00
Jeong, YunWon
4ef1b48155 instrumented instructions 2026-02-27 09:06:56 +09:00
CPython Developers
3c7b25d0fa Update test_compile from v3.14.3 2026-02-27 09:06:56 +09:00
Jeong, YunWon
739f92e872 impl sys.monitoring 2026-02-27 09:06:55 +09:00
Jeong, YunWon
f35d0fde0b patch test_monitoring not to entirely skipped 2026-02-27 04:36:46 +09:00
CPython Developers
abc7d6dfe6 Update test_monitoring from CPython 3.14.3 2026-02-27 04:36:46 +09:00
Jeong, YunWon
93099e35e7 Remove PyStr::as_str, use as_wtf8/PyUtf8Str instead (#7218)
- Remove as_str() from PyStr/Py<PyStr> (was panicking on surrogates)
- Add Wtf8Concat trait and concat! macro for WTF-8 formatting
- Add impl From<&str> for &Wtf8 conversion
- Add AsPyStr/DictKey impls for PyUtf8Str types
- Migrate all call sites to as_wtf8(), to_str(), or PyUtf8Str
- Fix exception message APIs to accept Wtf8Buf
- Deduplicate inner-scope imports across modules
2026-02-27 04:20:11 +09:00
Jeong, YunWon
a47572c89e Revert "ast.NodeVisitor for import tracking (#7229)" (#7230)
This reverts commit 804a7e42fc.
2026-02-27 03:55:13 +09:00
Shahar Naveh
804a7e42fc ast.NodeVisitor for import tracking (#7229) 2026-02-27 03:07:57 +09:00
Jeong, YunWon
c98215ab3a Clear frame locals and stack on generator close + Add dir_fd support for rmdir, remove/unlink, scandir (#7222)
* Clear frame locals and stack on generator close

Add Frame::clear_locals_and_stack() to release references held by
closed generators/coroutines, matching _PyFrame_ClearLocals behavior.
Call it from Coro::close() after marking the coroutine as closed.

Update test_generators.py expectedFailure markers accordingly.

* Add dir_fd support for rmdir, remove/unlink, scandir

- rmdir: use unlinkat(fd, path, AT_REMOVEDIR) when dir_fd given
- remove/unlink: use unlinkat(fd, path, 0) when dir_fd given
- scandir: accept fd via fdopendir, add ScandirIteratorFd
- listdir: rewrite fd path to use raw readdir instead of nix::dir::Dir
- DirEntry: add d_type and dir_fd fields for fd-based scandir
- Update supports_fd/supports_dir_fd entries accordingly

* cells_free
2026-02-27 01:58:33 +09:00
Shahar Naveh
457d328294 update_lib test format fix + deps for atexit & eintr (#7227)
* update_lib deps for atexit & eintr

* Fix test format
2026-02-26 12:03:47 +09:00
Shahar Naveh
8cf8cb561a Update compileall.py to 3.14.3 (#7225) 2026-02-25 21:15:55 +09:00
Shahar Naveh
56de2cd76c Don't skip test_glob on windows (#7224) 2026-02-25 21:01:51 +09:00
Copilot
9f250a0f6e Fix upgrade-pylib workflow to use Python 3.14 (#7216) 2026-02-24 16:22:44 +09:00
CPython Developers
1df878e5d3 Update annotationlib from v3.14.3 2026-02-24 16:22:44 +09:00
CPython Developers
000025e09c Update zipfile from v3.14.3 2026-02-24 14:23:19 +09:00
Jeong, YunWon
3ec7b5c375 Implement Windows SemLock in _multiprocessing module (#7199)
* Implement Windows SemLock in _multiprocessing module

Add SemLock class using Windows semaphore APIs (CreateSemaphoreW,
WaitForSingleObjectEx, ReleaseSemaphore) so test_multiprocessing
suites are no longer skipped with "lacks a functioning sem_open".
Also add sem_unlink as no-op and flags dict for Windows.

* Fix _multiprocessing recv to return bytes and improve SemLock reliability

- recv() now returns bytes instead of int (matching CPython)
- Remove spurious GetLastError() check after CreateSemaphoreW
- Add signal checking during blocking SemLock acquire

* Include winerror in OSError.__reduce__ on Windows

* Remove expectedFailure for tests now passing with Windows SemLock

* Fix OSError.__reduce__ to preserve winerror when filename is None

When filename is None, __reduce__ was reconstructing a 2-element
(errno, msg) tuple, dropping the winerror at position 3 in the
original args. Use the original args tuple instead, matching CPython.

* Validate maxvalue > 0 in SemLock and document winerror __reduce__ divergence

* Reject embedded null characters in mmap tagname and _winapi file mapping names
2026-02-24 10:12:29 +09:00
Shahar Naveh
f784a562f8 Update datetime to 3.14.3 (#7084) 2026-02-24 01:07:30 +00:00
Jeong, YunWon
dc7cd26c3c cold block reordering and jump normalization (#7210)
* cold block reordering and jump normalization

Add mark_cold, push_cold_blocks_to_end, and normalize_jumps
passes to the codegen CFG pipeline. Use JumpNoInterrupt for
exception handler exit paths in try-except-finally compilation.

* mark test_peepholer
2026-02-24 08:53:07 +09:00
Jeong, YunWon
0244657770 [workflow] fix upgrade-pylib (#7211) 2026-02-24 00:15:55 +09:00
dependabot[bot]
f478ace1a4 Bump github/gh-aw from 0.45.0 to 0.49.4 (#7209)
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.45.0 to 0.49.4.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](58d1d157fb...bf34f99475)

---
updated-dependencies:
- dependency-name: github/gh-aw
  dependency-version: 0.49.4
  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-02-23 23:36:55 +09:00
Jeong, YunWon
490a17f641 Implement Windows named mmap via CreateFileMappingW (#7207)
Use Win32 CreateFileMappingW + MapViewOfFile when tagname is
provided, enabling inter-process shared memory for
multiprocessing.heap.Arena. Anonymous mmaps without tagname
still use memmap2.

Remove expectedFailure from test_tagname in test_mmap.py.
2026-02-23 23:32:52 +09:00
Jeong, YunWon
39a5d39c7c Merge pull request #7203 from youknowone/pymodule
#[pymodule] submodule support and implicit module_name inheritance
2026-02-23 22:17:06 +09:00
Jeong, YunWon
649a2bf4de implicit module_name under #[pymodule] 2026-02-23 22:16:44 +09:00
Jeong, YunWon
eb07113d91 support sub-module from #[pymodule] 2026-02-23 22:16:40 +09:00
Jeong, YunWon
7dc76c8df1 skip flaky test (#7208) 2026-02-23 22:08:50 +09:00
Jeong, YunWon
f096bb7dd0 Fix deadlock after fork (#7205)ㅐ 2026-02-23 16:22:26 +09:00
Jeong, YunWon
a67079909e Fix EINTR handling (#7204) 2026-02-23 16:13:24 +09:00
CPython Developers
ccfa938f67 Update test_gc from v3.14.3 2026-02-23 08:48:18 +09:00
Jeong, YunWon
b48f72d0ad Fix Overlapped I/O by boxing OVERLAPPED struct and accept overlapped arg positionally (#7198)
Two fixes in _winapi:

1. Box the OVERLAPPED struct in OverlappedInner to ensure it stays at
   a stable heap address. Previously, into_pyobject() moved the struct
   after ReadFile/WriteFile had given the OS a pointer to it, causing
   GetOverlappedResult to read stale data (returning err=0 instead of
   ERROR_MORE_DATA for zero-byte reads on message pipes).

2. Change ReadFile, WriteFile, ConnectNamedPipe overlapped parameter
   from #[pyarg(named)] to #[pyarg(any)] so it can be passed both
   positionally and as keyword argument.
2026-02-22 22:14:06 +09:00
Jeong, YunWon
6950baf687 more algorithm-independent GC infra (#7194)
* mark poluting tests

* GC-infra independent to EBR

* trashcan

* add overflow guard to inc(), #[must_use] on dec()/safe_inc(), trashcan debug_assert, weakref generic re-check
2026-02-22 21:31:42 +09:00
Jeong, YunWon
f9ca638936 Remove recreate:true from lib-deps-check (#7201) 2026-02-22 20:27:14 +09:00
Jeong, YunWon
7b866f66e9 Add missing _winapi functions (#7185)
* Add missing _winapi functions and fix WinHandle int conversion

Add 13 functions: ReadFile, SetNamedPipeHandleState, CreateFileMapping,
OpenFileMapping, MapViewOfFile, UnmapViewOfFile, VirtualQuerySize,
CopyFile2, ResetEvent, CreateMutex, OpenEventW, LoadLibrary,
_mimetypes_read_windows_registry.

Add constants: INVALID_HANDLE_VALUE, FILE_MAP_READ/WRITE/COPY/EXECUTE.

Change WinHandle integer type from usize to isize so negative values
like INVALID_HANDLE_VALUE (-1) can be passed from Python.

* Align _winapi module with CPython

- Rename winapi.rs to _winapi.rs with #[path] attribute
- Rename CreateMutex to CreateMutexW
- Add missing constants: ERROR_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD,
  PROCESS_ALL_ACCESS, 10 STARTF_ constants, LOCALE_NAME_SYSTEM_DEFAULT,
  LOCALE_NAME_USER_DEFAULT, COPY_FILE_DIRECTORY
- Fix OpenMutexW return type and ReleaseMutex param type to use WinHandle

* Fix ReadFile/WriteFile overlapped keyword argument

Use FromArgs structs so overlapped parameter can be passed as
a keyword argument (overlapped=True), matching the CPython API.

* Remove extra constants and LoadLibrary not in CPython _winapi

Remove 19 constants (WAIT_ABANDONED, CREATE_ALWAYS, CREATE_NEW,
OPEN_ALWAYS, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 8 FILE_FLAG_*,
3 FILE_SHARE_*, NMPWAIT_NOWAIT, NMPWAIT_USE_DEFAULT_WAIT) and
LoadLibrary function that are not present in CPython's _winapi module.

* Fix utf8_mode default to 0 and add PYTHONUTF8 env var support

Default utf8_mode was incorrectly set to 1, causing text-mode
subprocess to always decode as UTF-8 instead of locale encoding.
Changed default to 0 to match CPython 3.13 behavior on Windows.
Added PYTHONUTF8 environment variable handling with -X utf8 override.

* Fix CopyFile2 to raise proper OSError subclass

Use std::io::Error::from_raw_os_error instead of vm.new_os_error so
that winerror attribute is set and errno-to-exception mapping works
(e.g. ERROR_ACCESS_DENIED → PermissionError).

* Fix syntax_non_utf8 test to not depend on locale encoding

Use explicit encoding='latin-1' so the test works regardless of
the system locale (e.g. C/POSIX locale uses ASCII by default).
2026-02-22 19:54:27 +09:00
Jeong, YunWon
2981010a90 Merge pull request #7195 from youknowone/sys
StatelessIncrementalEncoder, sys attributes
2026-02-22 00:30:56 +09:00
Jeong, YunWon
d19d523c8d Move sys.flags thread_inherit_context/context_aware_warnings to getset 2026-02-21 22:58:12 +09:00
Jeong, YunWon
ea8e25cee5 Register latin-1 codec and fix stdio bootstrap 2026-02-21 22:58:12 +09:00
Jeong, YunWon
6a02392b57 Fix WindowsVersionData n_sequence_fields 2026-02-21 22:58:12 +09:00
Jeong, YunWon
f5eadae767 Merge pull request #7075 from youknowone/sys
Update test_sys from v3.14.3 and impl more sys module
2026-02-21 19:26:58 +09:00
Jeong, YunWon
5cc589980c Infer struct sequence module name from __module__ attribute in repr
slot_repr now checks the __module__ attribute set by #[pymodule]
at runtime, so explicit module = "..." in #[pystruct_sequence] is
no longer needed for types defined inside a #[pymodule].
2026-02-21 14:23:49 +09:00
Jeong, YunWon
add253912e Implement sys.call_tracing, sys._current_exceptions
- Add sys.call_tracing as func(*args) dispatch
- Add sys._current_exceptions with per-thread exception tracking
  via thread_exceptions registry synced on push/pop/set_exception
- Fix f_back to use previous_frame pointer and cross-thread lookup
- Add module="sys" to float_info struct sequence
- Fix sys.exit to unpack tuple args
- Set default stdio_errors to surrogateescape
- Remove noisy __del__ warning in object core
2026-02-21 14:12:18 +09:00
CPython Developers
edca2dddf1 Update test_sys from v3.14.3 2026-02-21 14:12:18 +09:00
dependabot[bot]
9611b88788 Bump keccak from 0.1.5 to 0.1.6 (#7192)
Bumps [keccak](https://github.com/RustCrypto/sponges) from 0.1.5 to 0.1.6.
- [Commits](https://github.com/RustCrypto/sponges/compare/keccak-v0.1.5...keccak-v0.1.6)

---
updated-dependencies:
- dependency-name: keccak
  dependency-version: 0.1.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-20 23:14:21 +09:00
Jeong, YunWon
6ced4409ac force_unlock_after_fork (#7186) 2026-02-20 09:53:01 +09:00
wvmscs
dfc5de701b Fix re.fullmatch POSSESSIVE_REPEAT (#7187) 2026-02-20 09:51:34 +09:00
Shahar Naveh
eddb8f271a Newtype oparg for StoreFastLoadFast (#7191) 2026-02-20 08:19:12 +09:00
Lee Dogeon
41ea698f6a Update test_collections from v3.14.3 (#7189) 2026-02-19 08:48:33 +09:00
Jeong, YunWon
80d7850866 Merge pull request #7188 from moreal/update-3.14.3-types
Update types from v3.14.3
2026-02-18 21:49:19 +09:00
Lee Dogeon
3d33bea285 Remove expectedFailure from test_types_coroutine_wrapper_state
The types.py upgrade added gi_suspended to _GeneratorWrapper,
fixing this test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:39:56 +09:00
Lee Dogeon
24e51ee98c Mark failing tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 18:38:44 +09:00
CPython Developers
8cbf870238 Update types from v3.14.3 2026-02-18 17:19:49 +09:00
Jeong, YunWon
8e70048891 Merge pull request #7165 from youknowone/ctypes
Update ctypes from v3.14.3 and make _ctypes compatible
2026-02-18 08:21:27 +09:00
github-actions[bot]
8b50737914 Auto-format: cargo fmt --all 2026-02-17 18:11:21 +00:00
Jeong, YunWon
38d54fa0cd impl more ctypes
- Add __pointer_type__ getter/setter and StgInfo cache
- Add kept_refs (HashMap-keyed) for c_char_p lifetime
- Simplify ensure_z_null_terminated to 2-value return
- Add RawMemoryBuffer for zero-copy memoryview_at_addr
- Add bitfield support in PyCField with validation
- Add CArgObject offset handling in extract_ptr_from_arg
- Add deprecation warning for _pack_ without _layout_
- Fix comparison TypeError message format
- Fix unsupported binop error message format
- Mark failing CI tests as expectedFailure/skip
2026-02-18 03:10:40 +09:00
Jeong, YunWon
36a53ddf66 fix buffer whitespace skip 2026-02-18 03:10:14 +09:00
CPython Developers
4b7f1f8751 Update ctypes from v3.14.3 2026-02-18 03:09:55 +09:00
Jeong, YunWon
e81a0fc765 Use try_lock in py_os_after_fork_child (#7178)
after_forkers_child.lock() can deadlock in the forked child
if another thread held the mutex at the time of fork.
Use try_lock and skip at-fork callbacks when the lock is
unavailable, matching the pattern used in after_fork_child
for thread_handles.
2026-02-18 02:24:06 +09:00
Jeong, YunWon
b5785e2777 Use _print_exception_bltin in excepthook, register source in linecache (#7177)
- excepthook: call traceback._print_exception_bltin instead of
  traceback.print_exception to match PyErr_Display behavior
- run_string: register compiled code in linecache._interactive_cache
  so traceback can display source lines and caret indicators
- Remove test_sys_tracebacklimit expectedFailure
2026-02-18 01:50:27 +09:00
Jeong, YunWon
ccccaaf06f Merge pull request #7184 from youknowone/winsound
Update test_winsound from v3.14.3
2026-02-18 00:47:16 +09:00
Lee Dogeon
d07d52224e Optimize redundant bool check (#7176)
* Add compile_bool_op_inner and optimize nested opposite-operator BoolOps to avoid redundant __bool__ calls

When a nested BoolOp has the opposite operator (e.g., `And` inside `Or`),
the inner BoolOp's short-circuit exits are redirected to skip the outer
BoolOp's redundant truth test. This avoids calling `__bool__()` twice on
the same value (e.g., `Test() and False or False` previously called
`Test().__bool__()` twice instead of once).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add snapshot test for nested BoolOp bytecode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add runtime test for redundant __bool__ check (issue #3567)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Apply clippy and rustfmt

* Apply ruff format

* Refactor compile_bool_op: extract emit_short_circuit_test and unify with compile_bool_op_inner

Reduce code duplication by:
- Extracting the repeated Copy + conditional jump pattern into emit_short_circuit_test
- Merging compile_bool_op and compile_bool_op_inner into a single
  compile_bool_op_with_target with an optional short_circuit_target parameter
- Keeping compile_bool_op as a thin wrapper for the public interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Relocate redundant __bool__ check test snippet

* Update extra_tests/snippets/syntax_short_circuit_bool.py

* Fix assertion in syntax_short_circuit_bool

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2026-02-18 00:45:17 +09:00
Shahar Naveh
f0f3c9c8cf Update csv.py from 3.14.3 (#7181)
* Update `csv.py` from 3.14.3

* Don't hardcode error message for `int` class
2026-02-18 00:43:49 +09:00
Jeong, YunWon
d4d010ec21 Fix unpack_sequence to match CPython behavior (#7175)
Add fast path for exact tuple/list (direct length check without
iterator). General path now iterates only size+1 elements,
fixing hang on infinite sequences like __getitem__ without
raising IndexError.

Error messages now include "got N" for tuple, list, and dict.
Remove 7 expectedFailure/skip markers from test_unpack.
2026-02-18 00:41:53 +09:00
Jeong, YunWon
8fa0c66bc4 Optimize coroutine exception handling and fix gen_throw traceback (#7166)
- Replace ExceptionStack linked list with Vec for O(1) push/pop
- Introduce FramePtr (NonNull<Py<Frame>>) to avoid Arc clone in frame stack
- Use FramePtr in ThreadSlot for lock-protected cross-thread frame access
- Add resume_gen_frame for lightweight generator/coroutine resume
- Replace Coro.exception PyMutex with PyAtomicRef for lock-free swap
- Add with_frame_exc to support initial exception state for generators
- Use scopeguard for panic-safe cleanup in with_frame_exc/resume_gen_frame
- Add traceback entries in gen_throw delegate/close error paths
- Treat EndAsyncFor and CleanupThrow as reraise in handle_exception
- Update current_frame()/current_globals() to return owned values
- Add ThreadSlot with atomic exception field for sys._current_exceptions()
- Push/pop thread frames in resume_gen_frame for sys._current_frames()
2026-02-17 21:35:05 +09:00
Jeong, YunWon
705dd7dddd Implement winsound module
Add PlaySound, Beep, MessageBeep functions and all SND_*/MB_*
constants. Uses Windows APIs via winmm.dll, kernel32, user32.
2026-02-17 21:34:27 +09:00
CPython Developers
b7643c08cf Update test_winsound from v3.14.3 2026-02-17 21:33:54 +09:00
Jeong, YunWon
6b4605280e cap mt19937 version (#7182) 2026-02-17 20:02:28 +09:00
Jeong, YunWon
b87386f4fc Update test_fstring from v3.14.3 and impl more (#7164)
* Update test_fstring from v3.14.3

* Fix 6 test_fstring expectedFailure tests

- Add Unknown(char) variant to FormatType for proper error messages
  on unrecognized format codes (test_errors)
- Strip comments from f-string debug text in compile.rs
  (test_debug_conversion)
- Map ruff SyntaxError messages to match CPython in vm_new.rs:
  InvalidDeleteTarget, LineContinuationError, UnclosedStringError,
  OtherError(bytes mixing), OtherError(keyword identifier),
  FStringError(UnterminatedString/UnterminatedTripleQuotedString),
  and backtick-to-quote replacement for FStringError messages

* Fix clippy::sliced_string_as_bytes warning

---------

Co-authored-by: CPython Developers <>
2026-02-17 16:49:59 +09:00
dependabot[bot]
e3c533a53c Bump github/gh-aw from 0.43.22 to 0.45.0
Bumps [github/gh-aw](https://github.com/github/gh-aw) from 0.43.22 to 0.45.0.
- [Release notes](https://github.com/github/gh-aw/releases)
- [Changelog](https://github.com/github/gh-aw/blob/main/CHANGELOG.md)
- [Commits](fe858c3e14...58d1d157fb)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 10:31:13 +09:00
dependabot[bot]
ee6ff22947 Bump actions/setup-python from 5.6.0 to 6.2.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.6.0 to 6.2.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5.6.0...v6.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:44:06 +09:00
dependabot[bot]
5ca3c2894b Bump actions/download-artifact from 6.0.0 to 7.0.0
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:43:43 +09:00
dependabot[bot]
3d9480acf9 Bump actions/cache from 4.3.0 to 5.0.3
Bumps [actions/cache](https://github.com/actions/cache) from 4.3.0 to 5.0.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0057852bfa...cdf6c1fa76)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: 5.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:43:25 +09:00
dependabot[bot]
530e4db6ec Bump bitflags from 2.10.0 to 2.11.0
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.10.0 to 2.11.0.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/2.10.0...2.11.0)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-version: 2.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:42:42 +09:00
dependabot[bot]
1f338ce201 Bump pyo3 from 0.28.0 to 0.28.1 in the pyo3 group
Bumps the pyo3 group with 1 update: [pyo3](https://github.com/pyo3/pyo3).


Updates `pyo3` from 0.28.0 to 0.28.1
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-version: 0.28.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pyo3
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 09:42:28 +09:00
ShaharNaveh
d71ffa6c47 Use shared doctest checker 2026-02-17 01:31:29 +09:00
ShaharNaveh
0832828953 Update test_unpack.py from 3.14.3 2026-02-16 23:34:17 +09:00
Jeong, YunWon
79453c3e6d Merge pull request #7141 from youknowone/execve
Accept any mapping for os.execve env argument
2026-02-16 03:22:51 +09:00
Jeong, YunWon
243beb692b Merge pull request #7151 from youknowone/winconsoleio
_WindowsConsoleIO
2026-02-16 01:51:24 +09:00
ShaharNaveh
c76fb0145b Update test_richcmp.py from 3.14.3 2026-02-16 01:19:57 +09:00
Jeong, YunWon
72b4ef31a2 Accept any mapping for os.execve env argument
Change env parameter from PyDictRef to ArgMapping so that
EnvironmentVarGuard and other mapping types are accepted.
2026-02-16 01:03:18 +09:00
Jeong, YunWon
7b1db4868a Merge pull request #7162 from youknowone/wmi
impl _wmi module
2026-02-16 00:52:05 +09:00
CPython Developers
8552e42d94 Update test_winconsoleio from v3.14.3 2026-02-16 00:31:57 +09:00
Jeong, YunWon
8e06707fc6 Implement _WindowsConsoleIO and _testconsole module
Add _WindowsConsoleIO class to _io module with ReadConsoleW/WriteConsoleW support.
Add _testconsole stdlib module with write_input for console input simulation.
Integrate console detection in io_open to use _WindowsConsoleIO for console handles.
2026-02-16 00:28:51 +09:00
Jeong, YunWon
4897e0322d Add _wmi module
Implement _wmi.exec_query via COM/WMI using pipe+thread
pattern matching _wmimodule.cpp. Uses raw COM vtable calls
for IWbemLocator, IWbemServices, IEnumWbemClassObject, and
IWbemClassObject interfaces.
2026-02-15 23:44:23 +09:00
CPython Developers
09b528168e Update test_wmi from v3.14.3 2026-02-15 23:44:23 +09:00
Jeong, YunWon
4f366d7937 Skip flaky test_main_thread_after_fork_from_nonmain_thread 2026-02-15 23:42:09 +09:00
ShaharNaveh
3047a63941 Update test_scope.py from 3.14.3 2026-02-15 23:40:14 +09:00
Jeong, YunWon
b4edd547dc Merge pull request #7159 from youknowone/hashlib
_hashlib.HMAC and Update hmac from v3.14.3
2026-02-15 23:38:55 +09:00
Shahar Naveh
e1db19c892 Update test_with.py from 3.14.3 (#7158) 2026-02-15 23:38:22 +09:00
Jeong, YunWon
5530855436 Make __hello_only__ a frozen-only alias of __hello__ (#7160)
Remove __hello_only__.py source file and its symlink. Register
__hello_only__ as a frozen module alias reusing __hello__ bytecode,
matching the behavior where origname is None (no source file).
2026-02-15 22:25:28 +09:00
Jeong, YunWon
f805fa7c8b Fix winreg and Update test_launcher from v3.14.3 (#7155)
* Fix winreg SetValueEx and OSError winerror attribute

- SetValueEx: accept None for value_name (default registry value)
- os_error_from_windows_code: use std::io::Error to properly
  set winerror attribute on OSError
- EnumKey: use os_error_from_windows_code for proper error

* Update test_launcher from v3.14.3

* Update test_winreg from v3.14.3

* Update crates/vm/src/stdlib/winreg.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: CPython Developers <>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-15 22:11:48 +09:00
Jeong, YunWon
10c393f063 _hashlib.HMAC 2026-02-15 22:09:29 +09:00
CPython Developers
2a611c992d Update hmac from v3.14.3 2026-02-15 21:04:32 +09:00
Shahar Naveh
64088bb624 Add CI step for checking redundant test patches (#7126) 2026-02-15 20:39:57 +09:00
Lee Dogeon
2567355a99 Emit deprecation warning for array typecode u (#7157) 2026-02-15 20:34:19 +09:00
Lee Dogeon
914634e4df Align lzma and os exports for test_all expected failure (#7156) 2026-02-15 20:33:58 +09:00
Shahar Naveh
fbbd020e07 Update test_ast.py from 3.14.3 (#7147) 2026-02-15 20:29:05 +09:00
Jeong, YunWon
1ead81f11e Remove Windows skip markers from test_urllib2_localnet (#7154)
The "illegal environment variable name" issue has been resolved.
All 21 tests now pass on Windows.
2026-02-15 16:30:58 +09:00
Jeong, YunWon
5610a7c2f4 Merge pull request #7153 from youknowone/unskip1
Remove Windows skip from test_configparser test_parse_errors
2026-02-15 16:09:33 +09:00
Jeong, YunWon
9afd031e1d Merge pull request #7152 from moreal/codecs-related-tests-3.14.3
Add untracked tests related codecs from v3.14.3
2026-02-15 16:07:02 +09:00
Jeong, YunWon
563abea251 Require exact n_sequence_fields length in TryFromObject (#7150)
Unmark test_asctime expectedFailure for Windows.
2026-02-15 14:37:48 +09:00
Lee Dogeon
4e801b6ff8 Use @unittest.skip instead of @unittest.expectedFailure for codec encoding tests
The encoding errors occur in setUp() which causes ERRORs, not FAILUREs.
@unittest.expectedFailure only catches test failures, not setUp errors.
Using class-level @unittest.skip properly handles this case.
2026-02-15 14:37:06 +09:00
Lee Dogeon
6115ec703d Mark failing tests for test_codecmaps_tw 2026-02-15 14:33:37 +09:00
CPython Developers
89b31f72d6 Update test_codecmaps_tw from v3.14.3 2026-02-15 14:33:31 +09:00
Lee Dogeon
d75ae19888 Mark failing tests for test_codecmaps_kr 2026-02-15 14:33:26 +09:00
CPython Developers
4c9ef79fa5 Update test_codecmaps_kr from v3.14.3 2026-02-15 14:33:20 +09:00
Lee Dogeon
d504b28389 Mark failing tests for test_codecmaps_jp 2026-02-15 14:33:16 +09:00
CPython Developers
5138581e12 Update test_codecmaps_jp from v3.14.3 2026-02-15 14:33:09 +09:00
Lee Dogeon
5caa9009b7 Mark failing tests for test_codecmaps_hk 2026-02-15 14:33:05 +09:00
CPython Developers
20737e3bde Update test_codecmaps_hk from v3.14.3 2026-02-15 14:32:58 +09:00
Lee Dogeon
61825382ed Mark failing tests for test_codecmaps_cn 2026-02-15 14:32:53 +09:00
CPython Developers
2a29fa6e8f Update test_codecmaps_cn from v3.14.3 2026-02-15 14:32:31 +09:00
Lee Dogeon
04f12d2fc9 Mark failing tests for test_codecencodings_tw 2026-02-15 14:32:10 +09:00
CPython Developers
b500957da1 Update test_codecencodings_tw from v3.14.3 2026-02-15 14:32:03 +09:00
Lee Dogeon
2d7c54796f Mark failing tests for test_codecencodings_kr 2026-02-15 14:31:59 +09:00
CPython Developers
ccf6395c51 Update test_codecencodings_kr from v3.14.3 2026-02-15 14:31:51 +09:00
Lee Dogeon
5ea5d7864e Mark failing tests for test_codecencodings_jp 2026-02-15 14:31:45 +09:00
CPython Developers
68256fb877 Update test_codecencodings_jp from v3.14.3 2026-02-15 14:31:36 +09:00
Lee Dogeon
f8be47d758 Mark failing tests for test_codecencodings_iso2022 2026-02-15 14:31:31 +09:00
CPython Developers
a1b34eda22 Update test_codecencodings_iso2022 from v3.14.3 2026-02-15 14:31:22 +09:00
Lee Dogeon
7918d42815 Mark failing tests for test_codecencodings_hk 2026-02-15 14:31:18 +09:00
CPython Developers
fb6a5bbce2 Update test_codecencodings_hk from v3.14.3 2026-02-15 14:31:11 +09:00
Lee Dogeon
d2be8c0f79 Mark failing tests for test_codecencodings_cn 2026-02-15 14:31:06 +09:00
CPython Developers
5c280d070d Update test_codecencodings_cn from v3.14.3 2026-02-15 14:30:45 +09:00
Lee Dogeon
cf21e2da10 Include SHAKE algorithm names in HASHXOF repr (#7132) 2026-02-15 09:05:42 +09:00
Shahar Naveh
c1a327d38b Update test_descrtut.py from 3.14.3 (#7149)
Co-authored-by: CPython Developers <>
2026-02-15 09:03:56 +09:00
Jeong, YunWon
1adf9258fc Merge pull request #7148 from ShaharNaveh/update-libs-1
Update some libs & tests to 3.14.3
2026-02-15 09:03:00 +09:00
CPython Developers
8578114a5e Update shutil from v3.14.3 2026-02-15 09:02:24 +09:00
github-actions[bot]
15555d42e7 Auto-format: cargo fmt --all 2026-02-14 16:32:00 +00:00
ShaharNaveh
5dfeee25bb Update test_binascii.py 2026-02-14 18:27:31 +02:00
ShaharNaveh
11a8655d0d Update mailbox.py 2026-02-14 18:19:57 +02:00
ShaharNaveh
9a40f9adec Update xmlrpc 2026-02-14 18:18:53 +02:00
ShaharNaveh
b722ece6af Update test_pyclbr.py from 3.14.3 2026-02-14 18:16:15 +02:00
CPython Developers
f7d8a554cf Update enum from v3.14.3 2026-02-15 00:38:01 +09:00
Shahar Naveh
c30deb21f7 Update _markupbase.py from 3.14.3 (#7129) 2026-02-15 00:37:14 +09:00
Lee Dogeon
e8698aa19f Match SymbolTable repr with CPython format (#7139) 2026-02-14 22:59:42 +09:00
Jeong, YunWon
93d83c1ce9 Merge pull request #7114 from youknowone/unicode
Update test_unicodedata from v3.14.2 and implement more
2026-02-14 22:39:05 +09:00
ShaharNaveh
516ced9a57 Mark failing tests 2026-02-14 22:02:24 +09:00
ShaharNaveh
e94459d194 Update test_struct.py from 3.14.3 2026-02-14 22:02:24 +09:00
Jeong, YunWon
ec0f3b4da8 Merge pull request #7120 from youknowone/hashlib
Update hashlib from v3.14.3 and align _hashlib to CPython
2026-02-14 21:40:27 +09:00
Jeong, YunWon
12db02eaf0 Fix _hashlib 2026-02-14 21:40:04 +09:00
CPython Developers
1f290dccfe Update hashlib from v3.14.3 2026-02-14 21:40:04 +09:00
Lee Dogeon
16c7439c35 Add SHA3 private attribute parity to hashlib hash objects (#7134) 2026-02-14 21:36:57 +09:00
Jeong, YunWon
9e61458772 Merge pull request #7136 from RustPython/agent-wf
upgrade python workflow fix again
2026-02-14 21:36:24 +09:00
github-actions[bot]
16bb1040b4 Update collections from v3.14.3 (#7135)
- Updated Lib/collections from CPython v3.14.3
- Preserved RustPython's fallback implementation for defaultdict
- Added test for update_reentrant_add_clears_counter
- Minor formatting fixes in test file

Co-authored-by: CPython Developers <>
2026-02-14 21:35:25 +09:00
Lee Dogeon
2a2086014c Make HASH/HASHXOF types immutable (#7131) 2026-02-14 20:37:47 +09:00
Jeong, YunWon
4a897e57df copilot fix again 2026-02-14 20:10:39 +09:00
Shahar Naveh
e3edba398e Update test_itertools.py from 3.14.3 (#7130) 2026-02-14 20:00:17 +09:00
Shahar Naveh
29e7f97ef9 Remove redundant patches from test_pickle.py (#7128) 2026-02-14 19:59:47 +09:00
Shahar Naveh
36df9eb288 Remove redundant patches from test_codecs.py (#7127) 2026-02-14 19:58:02 +09:00
Lee Dogeon
799ce31270 Fix SHAKE digest length overflow in hashlib (#7125) 2026-02-14 19:57:27 +09:00
Lee Dogeon
adf9aea274 Fix object.__init__ excess-arg checks for heap types (#7116) 2026-02-14 19:56:49 +09:00
Jeong, YunWon
fe39ec184a Fix module selection (#7123) 2026-02-14 14:56:55 +09:00
Jeong, YunWon
dd422a8c26 Fix copilot upgrade pylib (#7121)
* Use copilot for upgrade workflow

* cache
2026-02-14 13:47:41 +09:00
Jeong, YunWon
f960a0fbad Add gh-aw agentic workflow for upgrading Python libraries (#7117)
* Add gh-aw agentic workflow for upgrading Python libraries

- upgrade-pylib.md: Claude-based workflow triggered via workflow_dispatch
- Runs `scripts/update_lib quick <name>` to sync a library from CPython
- Creates a PR with the result, referencing issue #6839
2026-02-14 11:26:03 +09:00
github-actions[bot]
2e4517971f Auto-format: cargo fmt --all 2026-02-14 00:33:13 +00:00
Jeong, YunWon
334936045d Implement missing unicodedata functions and fix lookup error type
Add combining, decomposition, digit, decimal, numeric methods to Ucd.
Change lookup() to raise KeyError instead of LookupError.
Remove expectedFailure markers from 9 passing tests.
Add unicodedata.is_normalized() method.
Rename decomp_chars to chars to fix spell check.
Remove expectedFailure from test_named_unicode_escapes and
test_urlsplit_normalization.
2026-02-14 09:32:17 +09:00
CPython Developers
9ec6d6c261 Update test_unicodedata from v3.14.2 2026-02-14 09:32:04 +09:00
ShaharNaveh
b9f693004a Update stat.py from 3.14.3 2026-02-14 09:18:09 +09:00
Lee Dogeon
abfa19fc42 Fix inspect.signature introspection for attrgetter/itemgetter/methodcaller (#7111)
* Fix attrgetter signature introspection in operator

* Fix itemgetter signature introspection in operator

* Fix methodcaller signature introspection in operator
2026-02-14 09:17:41 +09:00
Jeong, YunWon
38283becaa Fix typing, typevar, genericalias, and symboltable (#7091)
- TypeVar/ParamSpec repr: use infer_variance flag
- ParamSpec: add type_check on bound argument
- ParamSpecArgs/Kwargs: use equality instead of identity
- NoDefault: change to IMMUTABLETYPE flag
- subscript_generic: wrap TypeVarTuple in Unpack
- symboltable: selective name mangling in type param scopes
- symboltable: fix double default scanning for non-generic fns
- Unmark 4 passing tests in test_type_params
2026-02-14 09:15:57 +09:00
Jeong, YunWon
ad29b43ca6 Use CRT functions for time module on Windows (#7090)
- Replace chrono with CRT functions (wcsftime, _gmtime64_s,
  _localtime64_s, _mktime64) on Windows for correct behavior
- Fix timezone name corruption: use take_while instead of filter
  for null-terminated wide strings in TIME_ZONE_INFORMATION
- Fix wcsftime symbol name (_wcsftime -> wcsftime)
- Fix CString import cfg to unix-only
- Remove 13 expectedFailure markers from test_time for Windows
2026-02-14 09:13:06 +09:00
Jeong, YunWon
a5309f636e Merge pull request #7113 from ShaharNaveh/update-tests-2
Update some tests from 3.14.3
2026-02-13 22:16:18 +09:00
ShaharNaveh
0c85306068 Update test_abc.py from 3.14.3 2026-02-13 22:15:52 +09:00
ShaharNaveh
e9582d1c4e Update pathlib, glob, fnmatch from 3.14.3 2026-02-13 22:15:19 +09:00
Lee Dogeon
4b1ff4b003 Add float.from_number classmethod (#7107) 2026-02-13 11:47:12 +00:00
ShaharNaveh
23a26d2d13 Update test_print 2026-02-13 13:36:33 +02:00
ShaharNaveh
1499607df3 Add test_flufl.py 2026-02-13 13:36:33 +02:00
ShaharNaveh
0dd14d0bdf Update test_dictcomps, test_userdict 2026-02-13 13:36:33 +02:00
ShaharNaveh
a9687f01ee Update test_buffer.py 2026-02-13 13:36:33 +02:00
ShaharNaveh
8b99358339 Update test_bool.py from 3.14.3 2026-02-13 13:36:33 +02:00
ShaharNaveh
9c72e32874 Update posixpath.py from 3.14.3 2026-02-13 20:24:57 +09:00
Jeong, YunWon
578bc625fc Merge pull request #7106 from moreal/test_float
Update test_float from v3.14.3
2026-02-13 19:35:28 +09:00
Shahar Naveh
a058c4f73d Update ruff to 0.15.1 (#7105) 2026-02-13 19:34:49 +09:00
Shahar Naveh
d50cc5e842 Update pydoc.py from 3.14.3 (#7104) 2026-02-13 18:07:25 +09:00
ShaharNaveh
5124f8aca7 Mark failing test 2026-02-13 18:06:47 +09:00
ShaharNaveh
9d7387798c Update test_defaultdict.py from 3.14.3 2026-02-13 18:06:47 +09:00
Lee Dogeon
fb1514b6e8 Mark failing tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:05:39 +09:00
CPython Developers
18a264882b Update test_float from v3.14.3 2026-02-13 18:03:21 +09:00
Jeong, YunWon
743780f868 Merge pull request #7089 from youknowone/io
Update io from v3.14.3 and fix
2026-02-13 15:20:32 +09:00
Jeong, YunWon
718e74d5d4 Update scripts/update_lib/cmd_quick.py 2026-02-13 15:20:19 +09:00
Jeong, YunWon
8f0c6e9508 mark typing 2026-02-13 14:13:30 +09:00
Jeong, YunWon
18114a8b6e Fix BytesIO and add bytearray.resize
- Add bytearray.resize() method
- BytesIO: support keyword arg for initial_bytes
- BytesIO.seek: clamp negative positions to 0
- BytesIO.close: raise BufferError on active exports
- BytesIO.getbuffer: check closed state
- Set UnsupportedOperation.__module__ to "io"
2026-02-13 14:13:30 +09:00
CPython Developers
9924e2978a Update io from v3.14.3 2026-02-13 14:13:30 +09:00
Jeong, YunWon
190cecd7c8 [update_lib] _pyio deps 2026-02-13 14:00:00 +09:00
ShaharNaveh
96a78a9f43 Update test_long.py from 3.14.3 2026-02-13 13:55:05 +09:00
ShaharNaveh
e02e813bf8 Update _pylong.py from 3.14.3 2026-02-13 13:55:05 +09:00
ShaharNaveh
979b22f8d9 Update urllib from 3.14.3 2026-02-13 13:44:28 +09:00
ShaharNaveh
29133131fb Update test_functools.py from 3.14.3 2026-02-13 13:44:08 +09:00
ShaharNaveh
33921c2ac0 Update test_deque.py from 3.14.3 2026-02-13 13:43:46 +09:00
ShaharNaveh
8939cb059f Update test_future_stmt from 3.14.3 2026-02-13 13:43:29 +09:00
ShaharNaveh
44e1b22f4d Update test_positional_only_arg.py from 3.14.3 2026-02-13 13:43:18 +09:00
ShaharNaveh
87cf6848a5 Update test_funcattrs.py from 3.14.3 2026-02-13 13:43:04 +09:00
ShaharNaveh
4ab7c38c90 Update test_property.py from 3.14.3 2026-02-13 13:42:52 +09:00
ShaharNaveh
be43458579 Update test_sundry.py from 3.14.3 2026-02-13 13:42:38 +09:00
ShaharNaveh
d96c1f0a22 Update wave.py from 3.14.3 2026-02-13 13:42:22 +09:00
Jeong, YunWon
7f64acdd59 Merge pull request #7085 from youknowone/extcall
Fix dict unpacking order and improve error messages
2026-02-13 00:19:33 +09:00
Shahar Naveh
8459e9b053 fix miri ci (#7093) 2026-02-13 00:17:53 +09:00
Jeong, YunWon
8303c7a48b Add non-iterability guard, deduplicate dict flush and missing args format 2026-02-12 14:14:38 +09:00
Jeong, YunWon
cc8d9a2c53 Remove expectedFailure from test_bad_newobj_ex_args 2026-02-12 01:49:31 +09:00
Jeong, YunWon
542055580b fix 2026-02-12 01:01:35 +09:00
CPython Developers
b6d08fe371 Update test_unpack_ex from v3.14.2 2026-02-12 01:01:35 +09:00
CPython Developers
a144882948 Update test_extcall from v3.14.2 2026-02-12 01:01:35 +09:00
Jeong, YunWon
6e41e97468 Fix dict unpacking order and improve error messages
- Rewrite compile_dict to preserve insertion order with ** unpacking
- Fix function call error messages to include function name and
keyword-only argument counts
- Fix missing keyword-only argument error to list all missing args
- Fix starred expression error messages to match CPython
- Fix "cannot unpack non-iterable" error message
- Remove resolved expected failure markers in test_extcall and
test_unpack_ex
2026-02-12 01:01:35 +09:00
Jeong, YunWon
426019efc4 Fix annotation scope, deadlock, MRO, and HEAPTYPE issues (#7087)
* Fix annotation scope, deadlock, MRO, and HEAPTYPE issues

Annotation scope:
- Remove module-level annotation re-scan that created phantom
  sub_tables, breaking annotation closure for comprehensions
- Add async comprehension check in symbol table with
  is_in_async_context(); annotation/type-params scopes are
  always non-async
- Save/restore CompileContext in enter/exit_annotation_scope
  to reset in_async_scope

Deadlock prevention:
- Fix TypeVar/ParamSpec/TypeVarTuple __default__ and
  evaluate_default by cloning lock contents before acquiring
  a second lock or calling Python

Other fixes:
- Add HEAPTYPE flag to Generic for correct pickle behavior
- Guard heaptype_ext access in name_inner/set___name__/
  set___qualname__ with safe checks instead of unwrap
- Fix MRO error message to include base class names
- Add "format" to varnames in TypeAlias annotation scopes
- Fix single-element tuple repr to include trailing comma

* Unmark failing markers
2026-02-12 00:30:18 +09:00
Shahar Naveh
151a11ec36 Update ruff to 0.15.0 (#7086) 2026-02-12 00:11:11 +09:00
Jeong, YunWon
ba6d7b5404 Unmark failing markers 2026-02-11 18:38:10 +09:00
Jeong, YunWon
34180d8248 Fix annotation scope, deadlock, MRO, and HEAPTYPE issues
Annotation scope:
- Remove module-level annotation re-scan that created phantom
  sub_tables, breaking annotation closure for comprehensions
- Add async comprehension check in symbol table with
  is_in_async_context(); annotation/type-params scopes are
  always non-async
- Save/restore CompileContext in enter/exit_annotation_scope
  to reset in_async_scope

Deadlock prevention:
- Fix TypeVar/ParamSpec/TypeVarTuple __default__ and
  evaluate_default by cloning lock contents before acquiring
  a second lock or calling Python

Other fixes:
- Add HEAPTYPE flag to Generic for correct pickle behavior
- Guard heaptype_ext access in name_inner/set___name__/
  set___qualname__ with safe checks instead of unwrap
- Fix MRO error message to include base class names
- Add "format" to varnames in TypeAlias annotation scopes
- Fix single-element tuple repr to include trailing comma
2026-02-11 18:37:42 +09:00
Jeong, YunWon
0d1115464a Merge pull request #7078 from youknowone/typing
Implement more typing
2026-02-11 18:33:26 +09:00
Jeong, YunWon
bc35aa8f01 Merge pull request #7073 from youknowone/time
update test_time and implement more time module
2026-02-11 18:29:35 +09:00
ShaharNaveh
a9c98d2666 Update statistics.py from 3.14.3 2026-02-11 18:01:41 +09:00
Jeong, YunWon
fa59402def Merge pull request #7067 from youknowone/doctest
Update doctest,test_generators from v3.14.2 and fix generator bugs
2026-02-11 17:49:59 +09:00
Copilot
a9995a7d61 Sync test_abstract_numbers with CPython v3.14.3 (#7080) 2026-02-11 17:42:16 +09:00
Shahar Naveh
c904a91bef Update test_tabnanny.py from 3.14.3 (#7083) 2026-02-11 17:41:57 +09:00
Jeong, YunWon
5242dd42e1 Rename constevaluator_alloc to const_evaluator_alloc, add to cspell dict 2026-02-11 17:00:30 +09:00
Jeong, YunWon
6a6e3755c7 Propagate can_see_class_scope to annotation scopes in type param blocks
Annotation scopes now inherit can_see_class_scope from parent scopes
(not just direct Class parents), allowing annotations in generic
methods inside classes to access class variables via __classdict__.

Remove 3 expectedFailure markers from test_type_params.
2026-02-11 17:00:30 +09:00
Jeong, YunWon
c91f27c2fa Fix nonlocal type param check, __class_getitem__, __set_name__ note, sre module names
- Reject nonlocal binding for type parameters in symboltable
- Enable __class_getitem__ on memoryview, Template, Interpolation
- Fix __set_name__ error note to quote attribute name
- Set re.Match and re.Pattern module to "re" instead of "_sre"
- Migrate parking_lot::Mutex to PyMutex in typevar.rs
- Collapse nested if-let chains in typing_type_repr (clippy)
- Remove 5 expectedFailure markers from test files
2026-02-11 17:00:29 +09:00
Jeong, YunWon
d1e81225bc Add _ConstEvaluator, evaluate_* getters, format validation
- Implement _ConstEvaluator type in _typing module with STRING
  format support via typing_type_repr (port of _Py_typing_type_repr)
- Add evaluate_bound, evaluate_constraints, evaluate_default
  pygetset properties to TypeVar, ParamSpec, TypeVarTuple
- Emit format validation in evaluator scopes
  (compile_type_param_bound_or_default, TypeAlias value scopes)
  so evaluators raise NotImplementedError for unsupported formats
- Add non-default-after-default SyntaxError in scan_type_params
- Fix ParamSpec default_value to use Mutex for proper caching
- Fix TypeVar constructor: evaluate_constraints set to None
  instead of constraints tuple for eager-constructed TypeVars
- Pass format=1 (FORMAT_VALUE) to all lazy evaluator calls
- Remove 6 expectedFailure markers from test_type_params
2026-02-11 16:59:33 +09:00
CPython Developers
c4f23295bf Update test_time from v3.14.3 2026-02-11 16:57:51 +09:00
Shahar Naveh
a41d4c5029 Updated libs status show datetime utc (#7082) 2026-02-11 16:57:36 +09:00
Jeong, YunWon
d55a2261fa Use libc time functions on unix for time module
- Replace chrono-based gmtime/localtime/mktime/strftime/asctime/ctime
  with direct libc calls (gmtime_r, localtime_r, mktime, strftime)
  on unix, keeping chrono as fallback for non-unix platforms
- Add checked_tm_from_struct_time for struct_time field validation
- Accept None in gmtime/localtime/ctime (OptionalArg<Option<>>)
- Fix StructTimeData field order: tm_zone before tm_gmtoff
- Fix PyStructTime slot_new to accept optional dict argument
2026-02-11 16:54:13 +09:00
Jeong, YunWon
ad6e965ad3 apply tests 2026-02-11 16:50:23 +09:00
CPython Developers
cafde31502 Update test_generators from v3.14.2 2026-02-11 16:47:11 +09:00
CPython Developers
e616f250af Update doctest from v3.14.2 2026-02-11 16:47:03 +09:00
Jeong, YunWon
5c341efdf0 Implement generator/coroutine lifecycle, tracing, and error handling
- Add interactive REPL mode: auto-print expression results in single mode
- Implement Destructor for PyGenerator and PyCoroutine
- Add locals_dirty tracking and locals_to_fast() for frame sync
- Add per-line tracing with prev_line tracking in execution loop
- Fix gen_throw to close sub-iterator on GeneratorExit (gen_close_iter)
- Pass callable object as arg in c_call/c_return/c_exception trace events
- Distinguish [Errno] vs [WinError] for CRT vs Win32 API errors
- Fix tee thread safety with AtomicBool running flag
- Fix division error messages to match expected format
2026-02-11 16:46:36 +09:00
Jeong, YunWon
ccd3d4f964 Replace std::sync::LazyLock with common::lock::LazyLock (#7079) 2026-02-11 16:09:42 +09:00
Jeong, YunWon
c06cf56c60 Replace once_cell with std::sync::OnceLock/core::cell::OnceCell (#7077)
* Replace `once_cell` with `std::sync::OnceLock`/`core::cell::OnceCell`

- Replace `once_cell::sync::{Lazy, OnceCell}` with
  `std::sync::{LazyLock, OnceLock}`
- Replace `once_cell::unsync::{Lazy, OnceCell}` with
  `core::cell::{LazyCell, OnceCell}`
- Inline `get_or_try_init` at call sites (unstable in std as of 1.93)
- Replace `OnceCell::with_value()` with `OnceCell::from()` in codecs.rs
- Remove `once_cell` direct dependency from common and vm crates

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-11 11:08:16 +09:00
Jeong, YunWon
36ff461d65 Add import_ensure_initialized to import_module_level (#7076)
Wait for module initialization to complete when a module is
already in sys.modules but still being initialized by another
thread. Check __spec__._initializing and call
_lock_unlock_module to block until the import finishes.
2026-02-11 10:57:10 +09:00
Jeong, YunWon
6bfdfb1bea Upgrade typing from 3.14.2 and more impl (#7057)
* Implement TypeAliasType, type annotations, and genericalias fixes

- TypeAliasType: lazy value evaluation via closures, __module__,
  __parameters__, __iter__, evaluate_value, check_type_params,
  IMMUTABLETYPE flag, Hashable/AsMapping/Iterable traits
- TypeAliasType constructor: positional-or-keyword arg validation,
  duplicate/unexpected kwarg rejection
- type.__annotations__ setter: distinguish None assignment from deletion
- Annotation scope: name as __annotate__, format as positional-only,
  __conditional_annotations__ uses Cell for both load and store
- Compiler: proper TypeParams/TypeAlias scope with closures,
  find_ann covers match/try-except handlers
- symboltable: deduplicate TypeAlias value scope code
- GenericAlias repr: handle list args, avoid deadlock in repr_arg
  by cloning items before calling repr
- AST types: remove IMMUTABLETYPE (heap types, mutable)
- pymodule macro: preserve existing __module__ getset descriptors

* Update typing from v3.14.3

* Implement TypeAliasType, type annotations, and genericalias fixes

- TypeAliasType: lazy value evaluation via closures, __module__,
  __parameters__, __iter__, evaluate_value, check_type_params,
  IMMUTABLETYPE flag, Hashable/AsMapping/Iterable traits
- TypeAliasType constructor: positional-or-keyword arg validation,
  duplicate/unexpected kwarg rejection
- type.__annotations__ setter: distinguish None assignment from deletion
- Annotation scope: name as __annotate__, format as positional-only,
  __conditional_annotations__ uses Cell for both load and store
- Compiler: proper TypeParams/TypeAlias scope with closures,
  find_ann covers match/try-except handlers
- symboltable: deduplicate TypeAlias value scope code
- GenericAlias: implement gaiterobject (generic_alias_iterator),
  starred equality comparison, starred pickle via iterator reduce,
  split attr_exceptions/attr_blocked for correct __dir__,
  make_parameters/subs_parameters handle list/tuple args recursively,
  repr_arg indexed access for mutation safety
- AST types: remove IMMUTABLETYPE (heap types, mutable)
- pymodule macro: preserve existing __module__ getset descriptors

---------

Co-authored-by: CPython Developers <>
2026-02-11 09:39:04 +09:00
Shahar Naveh
2edab987fd Auto update lib status (#7074) 2026-02-11 01:48:50 +09:00
Jeong, YunWon
f594b0a400 Replace unsafe pointer cast with AtomicPtr in PyCode (#7063)
Add `source_path: AtomicPtr<PyStrInterned>` field to `PyCode`
for interior mutability, replacing the UB-inducing
`#[allow(invalid_reference_casting)]` + `write_volatile` pattern
in `update_code_filenames`. Use atomic load/store instead of a
mutex since the operation is a simple pointer swap on a 'static
reference. Update all read sites to use `source_path()` accessor.
2026-02-11 01:28:17 +09:00
Jeong, YunWon
65d54e8d51 Merge pull request #7071 from youknowone/memoryview
Update test_memoryview from v3.14.3 and impl count/index
2026-02-11 00:13:48 +09:00
Elmir
fde808e663 handle type annotations in nested functions correctly (#7072)
For example in the following code:

    def foo(x: int, y: float):
        def bar(q: int):
            return q
        pass

Make sure that `foo` type annotations are correctly propogated to
it's `__annotate__` and `__annotations__` attributes.

With this chage, we'll get:

    >>>>> foo.__annotations__
    {'x': <class 'int'>, 'y': <class 'float'>}

Previously annotations where 'lost', and we would get:

    >>>>> foo.__annotations__
    {}
2026-02-11 00:08:57 +09:00
Jeong, YunWon
b521f76ca4 impl more memory 2026-02-10 22:27:46 +09:00
CPython Developers
91b128a466 Update test_memoryview from v3.14.3 2026-02-10 22:27:46 +09:00
dependabot[bot]
23cf16a283 Bump psm from 0.1.29 to 0.1.30
Bumps [psm](https://github.com/rust-lang/stacker) from 0.1.29 to 0.1.30.
- [Commits](https://github.com/rust-lang/stacker/commits)

---
updated-dependencies:
- dependency-name: psm
  dependency-version: 0.1.30
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 22:25:09 +09:00
dependabot[bot]
ac5f77a9a8 Bump memchr from 2.7.6 to 2.8.0
Bumps [memchr](https://github.com/BurntSushi/memchr) from 2.7.6 to 2.8.0.
- [Commits](https://github.com/BurntSushi/memchr/compare/2.7.6...2.8.0)

---
updated-dependencies:
- dependency-name: memchr
  dependency-version: 2.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-10 22:23:59 +09:00
Jeong, YunWon
d79b41ba28 Merge pull request #7070 from ShaharNaveh/update-array
Update `test_array.py` from 3.14.3
2026-02-10 21:08:55 +09:00
ShaharNaveh
f73df6a102 Update test_json from 3.14.3 2026-02-10 21:00:40 +09:00
Jeong, YunWon
3bd061ef5a Merge pull request #7068 from ShaharNaveh/update-support-init
Update `support.__init__.py` from 3.14.3
2026-02-10 20:59:55 +09:00
ShaharNaveh
28ed23f7ca Update test_array.py from 3.14.3 2026-02-10 13:16:46 +02:00
ShaharNaveh
b6ebbfd365 impl array.clear 2026-02-10 12:58:29 +02:00
ShaharNaveh
4babbf3f97 Update test_support.py from 3.14.3 2026-02-10 11:45:49 +02:00
ShaharNaveh
c4e3a804cc Update test_descr.py from 3.14.3 2026-02-10 11:44:47 +02:00
ShaharNaveh
bb39d2e1ce Update support.__init__.py from 3.14.3 2026-02-10 11:40:38 +02:00
Jeong, YunWon
1a54267aa7 Merge pull request #7060 from youknowone/test_iter
Update test_errno,test_iter from v3.14.3
2026-02-10 00:50:54 +09:00
Jeong, YunWon
6f1edf3b26 Remove static cache for builtins iter/reversed reduce
- Replace static_cell! cached builtins_iter/builtins_reversed with
  per-call vm.builtins.get_attr lookups to support runtime builtins
  mutation (CPython issue #101765)
- Refactor PositionIterInternal::reduce to take func, active, and
  empty callbacks; resolve builtins outside the lock to prevent
  deadlock from reentrant iterator access
- Add __reduce__ and fix reentrant next() in PyCallableIterator
- Remove expectedFailure from test_reverse_iterator_picking and
  test_reduce_mutating_builtins_iter
2026-02-10 00:49:12 +09:00
CPython Developers
177837de6b Update test_iter from v3.14.3 2026-02-10 00:48:22 +09:00
CPython Developers
48d63a6dae Update test_errno from v3.14.3 2026-02-10 00:48:22 +09:00
Jeong, YunWon
c16f6f8b68 Fix signal handler after fork (#7012)
* temp regrtest save_env patch

* Refactor signal_handlers from Option to OnceCell

- Change `signal_handlers` type from `Option<Box<...>>` to
  `OnceCell<Box<...>>` so fork children can lazily initialize it
- Add `VirtualMachine::is_main_thread()` using runtime thread ID
  comparison instead of structural `signal_handlers.is_none()` check
- Initialize signal handlers in `py_os_after_fork_child()` via
  `get_or_init()` for workers that fork
- Use `get_or_init()` in `signal()` and `getsignal()` functions
- Remove `set_wakeup_fd(-1)` special-case workaround (d6d0303)
- Extract `signal::new_signal_handlers()` to deduplicate init expr
- Allow `getsignal()` from any thread (matches CPython behavior)
- Fix `set_wakeup_fd` error message to name the correct function
2026-02-10 00:38:46 +09:00
Copilot
20ad988585 Update copy module and test_copy from CPython v3.14.3 (#7062) 2026-02-10 00:05:15 +09:00
Jeong, YunWon
570d50c67f no_std for common, pylib, codegen (#7056)
* `std` feature for common

- Gate OS-dependent modules behind `#[cfg(feature = "std")]`
- Replace `std::f64` with `core::f64` in float_ops
- Replace `std::process::abort` with panic in refcount
- Remove `thread_local` from levenshtein (stack buffer)
- Split static_cell into threading/non_threading/no_std

* `std` for codegen

* `no_std` for pylib
2026-02-09 23:28:20 +09:00
Jeong, YunWon
c974b77127 [update_lib] fix hard_deps resolution and fix commit not to miss test data (#7058)
* [update_lib] fix hard_dep not to unclude other tests

* [update_lib] commit test data
2026-02-09 14:41:15 +09:00
Jeong, YunWon
dc2d235e59 Replace unsafe pointer cast with PyMutex in PyCode (#7055)
Add `source_path: PyMutex<&'static PyStrInterned>` field to
`PyCode` for interior mutability, replacing the UB-inducing
`#[allow(invalid_reference_casting)]` + `write_volatile` pattern
in `update_code_filenames`. Update all read sites across the
codebase to use the new `source_path()` accessor method.
2026-02-09 14:01:39 +09:00
CPython Developers
d1c19bef38 Update linecache from v3.14.3 2026-02-09 12:56:53 +09:00
Jeong, YunWon
d2a9937ad6 [update_lib] todo shows last updated date (#7053) 2026-02-09 09:39:01 +09:00
Jeong, YunWon
4daac232a4 no_std for doc, replace HashSet in codegen (#7054) 2026-02-09 08:14:53 +09:00
Jeong, YunWon
470bd5990b Fix test_import: import machinery, circular imports, and script shadowing (#7034)
* Fix test_import: import machinery and errors

- Emit IMPORT_FROM instead of LOAD_ATTR for `import a.b.c as m`
- Add "partially initialized module" error for circular imports
- Add "cannot access submodule" error for initializing submodules
- Implement script shadowing detection with "consider renaming" hint
  - Detect user scripts shadowing stdlib/third-party modules
  - Compute original sys.path[0] from sys.argv[0]
  - Check sys.stdlib_module_names for stdlib detection
  - Respect safe_path setting
- Implement _imp._fix_co_filename for code source_path rewriting
- Add data parameter to _imp.get_frozen_object with marshal deser
- Fix import_from: check __spec__.has_location before using origin
- Set ImportError.path attribute on import failures
- Fix import_star error messages for non-str __all__/__dict__ items
- Always call builtins.__import__ in import_inner

* Propagate __bool__ errors from fromlist in import

Previously, try_to_bool errors were silently swallowed via .ok(),
causing fromlist with broken __bool__ to default to false.
2026-02-09 01:40:26 +09:00
Jeong, YunWon
cf83008d33 no_std for wtf8, sre_engine, compiler-core, literal (#7051)
* `no_std` for wtf8

* `no_std` for sre_engine, compiler-core, literal
2026-02-09 01:04:54 +09:00
Shahar Naveh
ea352ccdae Make inner oparg values private (#7050) 2026-02-08 14:56:56 +00:00
Jeong, YunWon
f777416870 initial sandbox (#7035)
* Add host_env feature for sandbox isolation

Introduce a `host_env` feature flag that gates all host environment
access (filesystem, network, signals, processes). When disabled,
the VM operates in sandbox mode:

- _io module always available; FileIO gated by host_env
- SandboxStdio provides lightweight stdin/stdout/stderr via Rust std::io
- BytesIO/StringIO/BufferedIO/TextIOWrapper work without host_env
- open() returns UnsupportedOperation in sandbox
- stdlib modules (os, socket, signal, etc.) gated by host_env
- CI checks both host_env ON and OFF builds

* Auto-format: ruff check --select I --fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-08 13:58:30 +00:00
Jeong, YunWon
5dabad6702 reason inside #[allow] (#7049) 2026-02-08 13:11:50 +00:00
Shahar Naveh
23a89663e0 Update tomllib from 3.14.3 (#7048) 2026-02-08 12:51:38 +00:00
Shahar Naveh
c8b4d6308f Newtype for LoadAttr oparg (#7047) 2026-02-08 11:47:14 +00:00
ShaharNaveh
d54cf8f12e Update difflib.py from 3.14.3 2026-02-08 20:36:35 +09:00
ShaharNaveh
32b57785c3 Update dataclasses.py from 3.14.3 2026-02-08 19:38:26 +09:00
Jeong, YunWon
d2d8eeea2f Add _types module (#6807)
* _types

* builtin_function_or_method

* PyCapsule

* function_or_method

* Remove expectedFailure for test_builtin_function and test_join_nondaemon_on_shutdown
2026-02-08 19:17:03 +09:00
Jeong, YunWon
07fc6ee3c7 no_std clippy (#7043) 2026-02-08 16:49:18 +09:00
ShaharNaveh
c29d2d9a1c Update get{opt,pass}.py from 3.14.3 2026-02-08 16:35:56 +09:00
Shahar Naveh
bbe5412aea Update test_{binop,contains}.py from 3.14.3 (#7038) 2026-02-08 10:00:50 +09:00
Shahar Naveh
87289fd904 Update tests with deprecated get_c_recursion_limit (#7039) 2026-02-08 09:59:14 +09:00
ShaharNaveh
bff70f957f Remove redundunt test_pickle tests 2026-02-08 08:54:38 +09:00
ShaharNaveh
c00ccf7a6c Update test_sort.py from 3.14.3 2026-02-08 08:33:45 +09:00
dependabot[bot]
ae260c13fa Bump webpack from 5.94.0 to 5.104.1 in /wasm/example (#7036)
Bumps [webpack](https://github.com/webpack/webpack) from 5.94.0 to 5.104.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.94.0...v5.104.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.104.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-08 00:10:53 +09:00
Shahar Naveh
47f54f19c5 test__all__.py (#7037) 2026-02-07 22:42:10 +09:00
Shahar Naveh
f817ab8d07 Bytecode oparg optimization (#7032)
* Generate optimized oparg enums

* No need to match on 255

* Remove `num_enum` crate from `compiler-core`

* Update `Cargo.lock`

* macro fmt

* Rename macro vars

* Match without `,`

* Support alternative values

* Fix alternatives

* Improve docs

* Add const assert

* Don't use `as u32`

* Make only ComparisonOperator unoptimized

* Fix test

* cleanup

* All opargs are optimized

* Remove comment
2026-02-07 19:15:16 +09:00
Shahar Naveh
3d283bb91f Update some libs&tests to 3.14.3 (#7006)
* Update some libs&tests to 3.14.3

* Update argparse from 3.14.3

* Update inspect

* Update `configparser.py` from 3.14.3
2026-02-07 19:09:42 +09:00
CPython Developers
75137f7cdd Update test_import from v3.14.3 2026-02-07 15:13:43 +09:00
Jeong, YunWon
6b870d62ad Fix traceback, syntax errors, and exception handling (#7015)
* Update codeop from v3.14.3

* Fix traceback, syntax errors, and exception handling

- Improve unclosed bracket detection with "'(' was never closed" message
- Fix IndentationError location to point to end of line
- Implement frame.clear() with proper checks for executing/suspended frames
- Fix exception context chaining for propagated exceptions
- Add traceback.__dir__() and prevent tb_next deletion
- Fix subscript operation source range restoration in compiler
- Change "duplicate parameter" to "duplicate argument" error message
- Refactor duplicate code in asyncgenerator.rs and frame.rs

---------

Co-authored-by: CPython Developers <>
2026-02-07 13:41:45 +09:00
Jeong, YunWon
234bdda40d Refactor warn.rs and _warnings module (#7023)
- Add already_warned() with filter version tracking
- Add type validation for _defaultaction and _onceregistry
- Use direct function names for _warnings pyattr/pyfunction
- Route stdlib::warnings::warn through warn::warn
- Remove spurious EncodingWarning from TextIOWrapper
- Clean up comments and simplify check_matched error handling
- Remove expectedFailure from 3 passing test_warnings tests
2026-02-06 20:46:51 +09:00
Jeong, YunWon
8127000080 Impl more codecs and _codecs._unregister_error (#7025)
- Rewrite _pycodecs.py: escape_decode, charmap_decode,
  unicode_escape_decode/encode, raw_unicode_escape_decode/encode
- Add _codecs._unregister_error with built-in handler protection
  and null byte/surrogate validation
- Normalize encoding name in register_manual
- Add codec error notes via add_note
- Fix cp65001 encoding constant in StandardEncoding::parse
- Remove 35 expectedFailure markers from test_codecs,
  test_pickle, test_datetime, pickletools
2026-02-06 20:37:18 +09:00
dependabot[bot]
ea6ab124f4 Bump x509-parser from 0.18.0 to 0.18.1 (#7029)
Bumps [x509-parser](https://github.com/rusticata/x509-parser) from 0.18.0 to 0.18.1.
- [Changelog](https://github.com/rusticata/x509-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rusticata/x509-parser/commits)

---
updated-dependencies:
- dependency-name: x509-parser
  dependency-version: 0.18.1
  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-02-06 18:34:59 +09:00
Jeong, YunWon
6d29b7f684 Refactor _ast module for clarity (#7031)
- Extract singleton_node_to_object() helper for operator/context nodes
- Rename PY_COMPILE_FLAG_AST_ONLY to PY_CF_ONLY_AST
- Rename populate_match_args_and_attributes to populate_repr
- Set _attributes in impl_base_node! no-args variant
- Simplify ast_reduce with .is_some() instead of drop(value)
- Remove _ prefix from used parameters across ast/ files
- Fix slot_new comment to explain why slot_init is called
2026-02-06 17:45:11 +09:00
dependabot[bot]
9596ae3b76 Bump webpki-roots from 1.0.5 to 1.0.6 in the webpki-root group
Bumps the webpki-root group with 1 update: [webpki-roots](https://github.com/rustls/webpki-roots).


Updates `webpki-roots` from 1.0.5 to 1.0.6
- [Release notes](https://github.com/rustls/webpki-roots/releases)
- [Commits](https://github.com/rustls/webpki-roots/compare/v/1.0.5...v/1.0.6)

---
updated-dependencies:
- dependency-name: webpki-roots
  dependency-version: 1.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: webpki-root
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 17:31:46 +09:00
dependabot[bot]
1d5857bf5a Bump criterion from 0.8.1 to 0.8.2 in the criterion group
Bumps the criterion group with 1 update: [criterion](https://github.com/criterion-rs/criterion.rs).


Updates `criterion` from 0.8.1 to 0.8.2
- [Release notes](https://github.com/criterion-rs/criterion.rs/releases)
- [Changelog](https://github.com/criterion-rs/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/criterion-rs/criterion.rs/compare/criterion-v0.8.1...criterion-v0.8.2)

---
updated-dependencies:
- dependency-name: criterion
  dependency-version: 0.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: criterion
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 17:31:33 +09:00
Jeong, YunWon
4f14738a74 [AGENTS] guideline for comments 2026-02-06 14:30:15 +09:00
Padraic Fanning
f9686296a9 Add dependency groups to dependabot.yml 2026-02-06 14:20:31 +09:00
dependabot[bot]
40d418e2fc Bump webpack from 5.98.0 to 5.105.0 in /wasm/demo
Bumps [webpack](https://github.com/webpack/webpack) from 5.98.0 to 5.105.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.98.0...v5.105.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:08:41 +09:00
github-actions[bot]
008ec892c8 Update doc DB for CPython 3.14.3 2026-02-06 12:08:22 +09:00
CPython Developers
951a97e32d Update encodings from v3.14.3 2026-02-06 12:05:17 +09:00
Jeong, YunWon
a49ab1911c [doc] fix workflow (#7021) 2026-02-06 11:41:08 +09:00
dependabot[bot]
b993312365 Bump cranelift-module from 0.128.1 to 0.128.2 (#6992)
* Bump cranelift-module from 0.128.1 to 0.128.2

Bumps [cranelift-module](https://github.com/bytecodealliance/wasmtime) from 0.128.1 to 0.128.2.
- [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-module
  dependency-version: 0.128.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Upgrade all cranelift-* packages to 0.128.3 in Cargo.toml (#7017)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2026-02-06 11:39:50 +09:00
Jeong, YunWon
ba462e1223 [AGNETS.md] guide control flow (#7018) 2026-02-06 09:31:55 +09:00
Jeong, YunWon
1e1b423fc0 Merge pull request #6997 from youknowone/test_io
Better codecs and fix lots of test_io and other expectedFailures
2026-02-06 09:04:47 +09:00
dependabot[bot]
ef077319c8 Bump time from 0.3.46 to 0.3.47 (#7014)
Bumps [time](https://github.com/time-rs/time) from 0.3.46 to 0.3.47.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.46...v0.3.47)

---
updated-dependencies:
- dependency-name: time
  dependency-version: 0.3.47
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-06 09:00:59 +09:00
Shahar Naveh
956f471013 Use num_enum crate for oparg types (#6980)
* Use `num_enum` crate for oparg types

* Fix doctest

* Make opargs `#[repr(u8)]`

* BuildSliceArgCount optimized
2026-02-06 09:00:38 +09:00
Jeong, YunWon
19db8d0b9f Merge pull request #7008 from youknowone/warnings 2026-02-06 07:45:48 +09:00
CPython Developers
7ccab4a4c8 Update traceback from v3.14.3 2026-02-06 07:45:17 +09:00
ShaharNaveh
c3212e7cd0 Update pprint.py from 3.14.3 2026-02-06 01:14:55 +09:00
CPython Developers
cc23a67493 Update test_dict from v3.14.3 2026-02-06 00:35:00 +09:00
Jeong, YunWon
b880c33a8b Move pickletester.py to the correct place (#7003)
* Move `pickletester.py` to the correct place

* Mark failing tests

* Update `test_pickle.py` from 3.14.3
2026-02-06 00:21:15 +09:00
Jeong, YunWon
6e09d1b123 win codecs 2026-02-06 00:15:18 +09:00
Jeong, YunWon
258ac74384 fix io 2026-02-06 00:15:18 +09:00
CPython Developers
e948314a3e Update test_io from v3.14.3 2026-02-06 00:15:18 +09:00
Jeong, YunWon
afea16569b Fix test_io expectedFailures 2026-02-06 00:15:18 +09:00
Jeong, YunWon
2e62cac72b Implement more warnings 2026-02-05 23:50:08 +09:00
Jeong, YunWon
c0af6eb5c0 skip flaky test_threaded_weak_key_dict_copy (#7010) 2026-02-05 23:47:09 +09:00
fanninpm
2da6c34547 RustPython version to 3.14.3 (#6999)
* RustPython version to 3.14.3

* Refactor version number to variable

* Fix CPython clone version statement

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-05 23:42:33 +09:00
Jeong, YunWon
6de6a92717 Merge pull request #7001 from youknowone/annotationlib
Add test_annotationlib to v3.14.2 and fix related bugs
2026-02-05 23:39:47 +09:00
Jeong, YunWon
cdb7b0d5ca align overlapped to CPython 3.14.2 (#7005) 2026-02-05 23:38:35 +09:00
ShaharNaveh
906113f499 Merge remote-tracking branch 'upstream/main' into move-pickletester 2026-02-05 15:52:30 +02:00
ShaharNaveh
d4c268c834 Update test_pickle.py from 3.14.3 2026-02-05 15:52:00 +02:00
Jeong, YunWon
535638e1e7 Fix annotationlib parsing 2026-02-05 22:46:23 +09:00
CPython Developers
2fac506b35 Update warnings from v3.14.2 2026-02-05 21:54:56 +09:00
ShaharNaveh
00ea4636a1 Mark failing tests 2026-02-05 14:19:41 +02:00
Shahar Naveh
5b7db1d2d2 Newtype LoadSuperAttr oparg (#7002) 2026-02-05 21:14:38 +09:00
ShaharNaveh
0258e8d10a Move pickletester.py to the correct place 2026-02-05 13:32:40 +02:00
Jeong, YunWon
d7c259c8c9 Update list-related CPython tests to v3.14.2 (#7000) 2026-02-05 20:30:17 +09:00
CPython Developers
144dc7e5e2 Update annotationlib from v3.14.2 2026-02-05 19:31:58 +09:00
Shahar Naveh
e2ee2067f8 Update pickle.py from 3.14.2 (#6982)
* Update `_compat_pickle.py` from 3.14.2

* Update `pickle.py` from 3.14.2

* Update pickletools and tests

* Update all other pickle related files

* Make `test_extcall` to use modified doctest checker
2026-02-05 19:27:10 +09:00
Jeong, YunWon
684e880689 Implement more ast features (#6986) 2026-02-05 16:20:22 +09:00
Jeong, YunWon
0919b2cb3d command /apple-container (#6998) 2026-02-05 10:20:48 +09:00
Jeong, YunWon
f260677dbd Merge pull request #6996 from youknowone/import
Update importlib from 3.14.2
2026-02-05 08:12:43 +09:00
Jeong, YunWon
bcc5cf30ac impl more importlib 2026-02-05 00:21:34 +09:00
CPython Developers
afdf8cefe8 Update importlib from v3.14.2 2026-02-05 00:21:34 +09:00
Jeong, YunWon
648223ade2 non-code migration 2026-02-05 00:21:34 +09:00
Elmir
ffc4622896 support | operation between typing.Union and strings (#6983)
* remove duplicated _call_typing_func_object() functions

Move _call_typing_func_object() code to
stdlib::typing::call_typing_func_object().
Use that function everywhere.

* support | operation between typing.Union and strings

Adds support for performing '|' operation between Union objects and
strings, e.g. forward type references.

For example following code:

    from typing import Union

    U1 = Union[int, str]
    U1 | "float"

The result of the operation above becomes:

   int | str | ForwardRef('float')
2026-02-04 23:49:36 +09:00
ShaharNaveh
a037bda44b Update queue from 3.14.2 2026-02-04 18:32:45 +09:00
Jeong, YunWon
0dcc975304 Add GC infrastructure: object tracking, tp_clear, and helper methods (#6994) 2026-02-04 18:29:09 +09:00
Jeong, YunWon
5662fa0751 Align winapi with CPython behavior (#6988) 2026-02-04 15:02:21 +09:00
dependabot[bot]
f709386ead Bump flate2 from 1.1.8 to 1.1.9 (#6991)
* Bump flate2 from 1.1.8 to 1.1.9

Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.8 to 1.1.9.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.8...1.1.9)

---
updated-dependencies:
- dependency-name: flate2
  dependency-version: 1.1.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...
2026-02-04 14:30:59 +09:00
Jeong, YunWon
e9c6617687 Merge pull request #6985 from youknowone/subprocess
Update subprocess from v3.14.2
2026-02-04 13:17:14 +09:00
dependabot[bot]
5e0fb7a6ee Bump bytes from 1.11.0 to 1.11.1 (#6987)
* Bump bytes from 1.11.0 to 1.11.1

Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.11.1
  dependency-type: indirect
...
2026-02-04 13:07:51 +09:00
Jeong, YunWon
2c417d5bb1 Fix Context.new_bytes (#6989) 2026-02-04 13:07:16 +09:00
CPython Developers
79c428e465 Update subprocess from v3.14.2 2026-02-04 11:16:15 +09:00
Jeong, YunWon
e56705455a Remove _use_vfork 2026-02-04 11:15:58 +09:00
Jeong, YunWon
c045593e4e impl more nt (#6984)
* mpl new features

* windows encodings

* impl nt functions

* revert

* codecs

* fix codecs
2026-02-04 09:53:02 +09:00
Jeong, YunWon
9f0cd323b6 Merge pull request #6958 from youknowone/os-posix
Update os from v3.14.2 and fix nt,posix,_os
2026-02-03 23:00:25 +09:00
Jeong, YunWon
674d7dbb3a rework SchedParam 2026-02-03 22:10:31 +09:00
Jeong, YunWon
c0f3a09c2b more windows impl 2026-02-03 22:10:31 +09:00
Jeong, YunWon
cb2be65a8b fix timeout 2026-02-03 22:10:31 +09:00
Jeong, YunWon
9c29b0c411 Fix windows link 2026-02-03 22:10:31 +09:00
CPython Developers
dd6e947122 Update os from v3.14.2 2026-02-03 22:10:31 +09:00
CPython Developers
ac1dcf7d4b Update test_posix from v3.14.2 2026-02-03 22:10:31 +09:00
Jeong, YunWon
f939a06aa9 fix durartion to round 2026-02-03 22:10:31 +09:00
Jeong, YunWon
8d07c45483 Populate _field_types with real type objects (#6981)
- Add FieldType enum and FIELD_TYPES static table mapping all
  AST node classes to their ASDL field types
- Resolve markers to real Python type objects (GenericAlias,
  Union, plain types) at module init in populate_field_types()
- Set class-level None defaults for optional fields
- Replace hardcoded LIST_FIELDS and class-name checks in
  slot_init with _field_types-based lookup
- Add expr_context default to Load(), fix ImportFrom.level
  default (now None instead of 0)
- Fix __class_getitem__ None check, compile() formatting
- Remove 14 @expectedFailure decorators from test_ast
2026-02-03 22:05:05 +09:00
Shahar Naveh
27d70fd58e Update test_builtin.py from 3.14.2 (#6979)
* Update `test_builtin.py` from 3.14.2

* Mark haning/segfault tests

* Patch failing tests
2026-02-03 21:55:55 +09:00
Jeong, YunWon
f71fe9bf3a Add GC infrastructure: tracking bits, tp_clear (#6977)
GC bit operations (_PyObject_GC_TRACK/UNTRACK equivalent):
- Add set_gc_bit() helper for atomic GC bit manipulation
- Add set_gc_tracked() / clear_gc_tracked() methods
- Update is_gc_tracked() to use GcBits::TRACKED flag
- Call set_gc_tracked() in track_object()
- Call clear_gc_tracked() for static types (they are immortal)

tp_clear infrastructure (for breaking reference cycles):
- Add try_clear_obj() function to call payload's try_clear
- Add clear field to PyObjVTable
- Add clear() method to PyType's Traverse impl
2026-02-03 20:00:34 +09:00
Jeong, YunWon
023b3b261d Add __replace__ and fix __reduce__ for structseq (#6978)
* Add __replace__ and fix __reduce__ for structseq

- Add __replace__ method to PyStructSequence trait
- Move __reduce__ from #[pymethod] to extend_pyclass with
  contains_key guard, allowing per-type overrides
- Fix repr: remove trailing comma for single-field sequences

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-03 19:09:47 +09:00
Jeong, YunWon
477e20a7a9 Support #[cfg] in with (#6975) 2026-02-03 18:55:59 +09:00
Jeong, YunWon
d6aab01424 Improve object traversal for heap types and mro (#6976)
- Fix heap type instance traversal to include type reference
  (enables correct cycle detection for instance ↔ type references)
- Enable mro traversal in PyType (was previously disabled)
2026-02-03 17:54:11 +09:00
Jeong, YunWon
182157291a Merge pull request #6961 from youknowone/unparse
Update _ast_unparse and fix unparse, type_comments
2026-02-03 15:41:01 +09:00
github-actions[bot]
9e7faba7f3 Auto-format: cargo fmt --all 2026-02-03 05:59:58 +00:00
Jeong, YunWon
00cdb307e3 Fix AST field defaults and compile() type check
- Extract empty_arguments_object helper from expression.rs
- Fix LIST_FIELDS ambiguity: "args" and "body" have different
  ASDL types per node (e.g. Lambda.args is `arguments`, not
  `expr*`; Lambda.body is `expr`, not `stmt*`)
- Replace class name string comparison in compile() with
  fast_isinstance to accept AST subclasses
2026-02-03 14:58:43 +09:00
Jeong, YunWon
ff49bfe3a7 mark ast & test_genericalias 2026-02-03 14:16:10 +09:00
CPython Developers
69c19f7cd1 Update _ast_unparse from v3.14.2 2026-02-03 14:16:10 +09:00
Jeong, YunWon
63bbb3c804 fix ast fields including type_comments 2026-02-03 14:16:10 +09:00
Jeong, YunWon
1876ac88e0 Fix compiler panics 2026-02-03 14:16:10 +09:00
Jeong, YunWon
0da5931353 fix unparse 2026-02-03 14:16:10 +09:00
Jeong, YunWon
400696c0fd --exclude rustpython-venvlauncher 2026-02-03 14:16:10 +09:00
Noa
5bf13e8642 Switch to Cell::update, slice::{split_first_chunk,split_off}, where appropriate (#6974)
* Use Cell::update, slice::{split_first_chunk,split_off}

* Use more array -> slice methods
2026-02-03 13:45:03 +09:00
Jeong, YunWon
ec34befc3c Merge pull request #6949 from youknowone/faulthandler
Update faulthandler to 3.14.2
2026-02-03 13:43:25 +09:00
Jeong, YunWon
cdadde55ef Update faulthandler to match CPython 3.14.2
- Rewrite faulthandler with live frame walking via
  Frame.previous AtomicPtr chain and thread-local
  CURRENT_FRAME (AtomicPtr) instead of frame snapshots
- Add signal-safe traceback dumping (dump_live_frames,
  dump_frame_from_raw) walking the Frame.previous chain
- Add safe_truncate/dump_ascii for UTF-8 safe string
  truncation in signal handlers
- Refactor write_thread_id to accept thread_id parameter
- Add SA_RESTART for user signal registration, SA_NODEFER
  only when chaining
- Save/restore errno in faulthandler_user_signal
- Add signal re-entrancy guard in trigger_signals to
  prevent recursive handler invocation
- Add thread frame tracking (push/pop/cleanup/reinit)
  with force_unlock fallback for post-fork recovery
- Remove expectedFailure markers for now-passing tests
2026-02-03 13:41:58 +09:00
CPython Developers
b90530cb87 Update test_faulthandler from v3.14.2 2026-02-03 13:41:15 +09:00
dependabot[bot]
169a422ade Bump pyo3 from 0.27.2 to 0.28.0 (#6966)
* Bump pyo3 from 0.27.2 to 0.28.0

Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.27.2 to 0.28.0.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.27.2...v0.28.0)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...
2026-02-03 13:33:25 +09:00
dependabot[bot]
fda12b2fce Bump malachite-base from 0.9.0 to 0.9.1 (#6965)
* Bump malachite-base from 0.9.0 to 0.9.1

Bumps [malachite-base](https://github.com/mhogrefe/malachite) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/mhogrefe/malachite/releases)
- [Commits](https://github.com/mhogrefe/malachite/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: malachite-base
  dependency-version: 0.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Align all malachite dependencies to version 0.9.1 (#6970)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
2026-02-03 09:42:26 +09:00
dependabot[bot]
20a58cbe3e Bump astral-sh/ruff-action from 3.5.1 to 3.6.1 (#6969)
Bumps [astral-sh/ruff-action](https://github.com/astral-sh/ruff-action) from 3.5.1 to 3.6.1.
- [Release notes](https://github.com/astral-sh/ruff-action/releases)
- [Commits](57714a7c8a...4919ec5cf1)

---
updated-dependencies:
- dependency-name: astral-sh/ruff-action
  dependency-version: 3.6.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-02-03 08:17:01 +09:00
Jeong, YunWon
0a39c66817 skip fork test (#6964) 2026-02-02 17:26:31 +09:00
Jeong, YunWon
2f034130b7 [update_lib] auto-mark original contents recovery (#6960) 2026-02-02 16:03:26 +09:00
Jeong, YunWon
f7b2660882 Fix test_asyncio for windows (#6959) 2026-02-02 15:58:16 +09:00
Jeong, YunWon
c6499797ea AGENTS.md 2026-02-02 15:57:36 +09:00
CPython Developers
15efc4a808 Update uuid from v3.14.2-288-g06f9c8ca1c 2026-02-02 14:01:59 +09:00
Jeong, YunWon
babc3c634f Make auto-mark output deterministic and fix blank line leak (#6957)
* Make auto-mark output deterministic and fix blank line leak

Sort set iteration in build_patches and dict iteration in
_iter_patch_lines Phase 2 so expectedFailure markers are
always added in alphabetical order.

Include preceding blank line in _method_removal_range so
removing a super-call override doesn't leave behind the
blank line that was added with the method.

* deps code

* auto-mark handles crash better

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-02 13:53:01 +09:00
Jeong, YunWon
100b870175 Implement UTF-32 encode/decode and fix UTF-16 empty encode
- Add UTF-32, UTF-32-LE, UTF-32-BE encode/decode in _pycodecs.py
- Register utf_32 codec functions in codecs.rs via delegate_pycodecs
- Fix PyUnicode_EncodeUTF16 returning "" instead of [] for empty input
- Remove resolved expectedFailure decorators in test_codecs.py
- Add failure reasons to remaining expectedFailure comments
2026-02-02 12:50:34 +09:00
CPython Developers
db347b344d Update tabnanny from v3.14.2-288-g06f9c8ca1c 2026-02-02 11:51:53 +09:00
Jeong, YunWon
95624ce818 Merge pull request #6956 from fanninpm/3.14-socket
Update `socket` from v3.14.2
2026-02-02 11:32:39 +09:00
Jeong, YunWon
4541cbab72 Merge pull request #6954 from fanninpm/3.14-imaplib 2026-02-02 11:31:03 +09:00
CPython Developers
453ff6dc98 Update test_runpy from v3.14.2-288-g06f9c8ca1c 2026-02-02 10:45:01 +09:00
Padraic Fanning
92a5cf0ac8 Mark erroring tests 2026-02-01 20:27:22 -05:00
CPython Developers
02537b56fd Update socket from v3.14.2-288-g06f9c8ca1c 2026-02-01 20:23:50 -05:00
Jeong, YunWon
7004502951 dealloc and finalize_modules (#6934)
* rewrite finalize_modules with phased algorithm

Replace the absence of module finalization during interpreter shutdown
with a 5-phase algorithm matching pylifecycle.c finalize_modules():

1. Set special sys attributes to None, restore stdio
2. Set all sys.modules values to None, collect module dicts
3. Clear sys.modules dict
4. Clear module dicts in reverse import order (2-pass _PyModule_ClearDict)
5. Clear sys and builtins dicts last

This ensures __del__ methods are called during shutdown and modules are
cleaned up in reverse import order without hardcoded module names.

* dealloc the rigth way

* fix finalize_modules: only clear __main__ dict, mark daemon thread tests as expected failure

Without GC, clearing all module dicts during finalization causes __del__
handlers to fail (globals are None). Restrict Phase 4 to only clear
__main__ dict — other modules' globals stay intact for their __del__
handlers.

Mark test_daemon_threads_shutdown_{stdout,stderr}_deadlock as expected
failures — without GC+GIL, finalize_modules clears __main__ globals
while daemon threads are still running.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-02 10:10:58 +09:00
CPython Developers
52d23158ed Update test_asyncgen from v3.14.2 2026-02-02 10:03:42 +09:00
Jeong, YunWon
7f46112984 Merge pull request #6951 from fanninpm/3.14-code
Update `code` from v3.14.2
2026-02-02 09:58:38 +09:00
Padraic Fanning
359920f749 Mark erroring test(s) 2026-02-01 19:45:06 -05:00
CPython Developers
a8e93bd8b1 Update imaplib from v3.14.2-288-g06f9c8ca1c 2026-02-01 19:40:43 -05:00
CPython Developers
1079161270 Update test_signal from v3.14.2-288-g06f9c8ca1c 2026-02-02 09:09:35 +09:00
CPython Developers
7ec1f33b60 Update ftplib from v3.14.2-288-g06f9c8ca1c 2026-02-02 09:09:19 +09:00
Padraic Fanning
26a8ef9370 Mark failing tests 2026-02-01 18:57:43 -05:00
CPython Developers
1022eeebdd Update test_code_module from v3.14.2-288-g06f9c8ca1c 2026-02-01 18:55:40 -05:00
CPython Developers
f26ec68657 Update code from v3.14.2-288-g06f9c8ca1c 2026-02-01 18:55:19 -05:00
Jeong, YunWon
ab6114d5a2 Merge pull request #6942 from youknowone/os-fix
posix.link, termios.error, warn() error, closefd=False for std in/out
2026-02-02 08:11:46 +09:00
Jeong, YunWon
019e754055 Fix __set_name__ error handling to match Python 3.12+ (#6937)
Changed type.rs to add notes to original exceptions instead of
wrapping them in RuntimeError, following PEP 678 (gh-77757).

This allows enum.py's exception handling to work correctly when
super().__new__() is misused in Enum subclasses, enabling the
proper TypeError to propagate instead of being hidden behind
a RuntimeError wrapper.

Fixes test_bad_new_super test case.
2026-02-02 08:11:26 +09:00
CPython Developers
d767b5fce6 Update shlex from v3.14.2-288-g06f9c8ca1c 2026-02-02 08:10:15 +09:00
Jeong, YunWon
89dbd42da2 Merge pull request #6946 from fanninpm/3.14-test_tstring
Update `test_tstring` from v3.14.2
2026-02-02 08:09:48 +09:00
Jeong, YunWon
d46a3b4ca9 Merge pull request #6945 from fanninpm/3.14-test_unpack_ex
Update `test_unpack_ex` from v3.14.2
2026-02-02 08:09:11 +09:00
CPython Developers
4ef6120c01 Update test_threadsignals from v3.14.2-288-g06f9c8ca1c 2026-02-02 08:08:29 +09:00
Jeong, YunWon
d1b55f584e Merge pull request #6902 from youknowone/asyncio
Update asyncio to 3.14.2
2026-02-02 08:04:52 +09:00
Padraic Fanning
491d230dec Make test_tstring platform-independent 2026-02-01 15:08:10 -05:00
Padraic Fanning
946075fe12 Mark failing tests 2026-02-01 15:06:33 -05:00
CPython Developers
10d7498c14 Update test_tstring from v3.14.2-288-g06f9c8ca1c 2026-02-01 15:03:06 -05:00
Padraic Fanning
b805008510 Make test_unpack_ex platform-independent 2026-02-01 14:38:26 -05:00
Padraic Fanning
784e203b93 Mark failing examples
This caused me to revise the custom output checker so it can handle
other option flags.
2026-02-01 14:35:48 -05:00
CPython Developers
6f490d2481 Update test_unpack_ex from v3.14.2-288-g06f9c8ca1c 2026-02-01 14:21:52 -05:00
Jeong, YunWon
d8e582ef6e _locale.localeconv (#6941) 2026-02-02 02:02:12 +09:00
Jeong, YunWon
1132f66325 Set closefd=false for stdio file objects in VM init
- Prevent closing underlying fd when stdio wrappers are dropped
- Remove expectedFailure from test_fdopen in test_os.py
2026-02-02 01:57:03 +09:00
Jeong, YunWon
fce2d7824f Propagate errors from warnings.warn() calls 2026-02-02 01:55:22 +09:00
Jeong, YunWon
188d438e00 Use new_os_subtype_error for termios error construction
- Replace manual exception creation with new_os_subtype_error in termios
- Remove redundant .to_owned() calls in os.link() error messages
2026-02-02 01:54:58 +09:00
Jeong, YunWon
24b6f48841 Add follow_symlinks support to os.link()
- Use linkat() with AT_SYMLINK_FOLLOW on Unix
- Raise NotImplementedError on non-Unix when follow_symlinks=False
- Register link in supports_follow_symlinks
2026-02-02 01:53:46 +09:00
Jeong, YunWon
5e732c5e2a Fix wasip2 build (#6935) 2026-02-02 01:26:17 +09:00
CPython Developers
5997507216 Update ast from v3.14.2 2026-02-02 01:20:23 +09:00
Jeong, YunWon
68ebb61f7b fix: Include hard_deps in git commit (#6940)
Bug: update_lib quick ast was not committing _ast_unparse.py
because git_commit() only added lib_path and test_paths,
but not hard_deps.

Fixed by:
- Add hard_deps parameter to git_commit()
- Collect hard_deps from DEPENDENCIES in main()
- Add hard_deps to paths_to_add in git_commit()
2026-02-02 00:47:29 +09:00
Jeong, YunWon
7258a4ae92 generator is borrowed 2026-02-02 00:43:07 +09:00
Jeong, YunWon
80929f44d4 PyAtomicBorrow 2026-02-02 00:43:07 +09:00
Jeong, YunWon
abd1daac83 Fix asyncgen close 2026-02-02 00:43:07 +09:00
Jeong, YunWon
9a9426ee73 mark test failures 2026-02-02 00:43:07 +09:00
Jeong, YunWon
963261c40f mark failures 2026-02-02 00:43:07 +09:00
Jeong, YunWon
085f517a3f keycert3 2026-02-02 00:43:07 +09:00
CPython Developers
b83d155235 Update asyncio from v3.14.2 2026-02-02 00:43:07 +09:00
Jeong, YunWon
fe40dd37c0 impl more _asyncio 2026-02-02 00:43:07 +09:00
Jeong, YunWon
d90792258d import .gitattributes from CPython (#6939) 2026-02-02 00:27:21 +09:00
Jeong, YunWon
153b0da60d automark add reasons to reason-less expectedFailures and handles parent better (#6936) 2026-02-01 22:58:07 +09:00
Jeong, YunWon
dd09f41dcc clear_after_fork (#6933) 2026-02-01 19:42:30 +09:00
Jeong, YunWon
0b9c90fcc4 Update enum from v3.14.2 (#6932)
---------

Co-authored-by: CPython Developers <>
2026-02-01 19:14:55 +09:00
Jeong, YunWon
97861c6809 Rework exception compiler to use CFG-based handler analysis (#6909)
Move exception handler tracking from compile-time fblock metadata to
a post-codegen CFG analysis pass, matching flowgraph.c's pipeline.

Compiler changes (compile.rs):
- Remove fb_handler, fb_stack_depth, fb_preserve_lasti from FBlockInfo
- Remove handler_stack_depth() and current_except_handler() helpers
- Emit SetupFinally/SetupCleanup/SetupWith/PopBlock pseudo instructions
  instead of manually tracking handlers in fblocks
- Add dead blocks after raise/break/continue/return to prevent
  dead code from corrupting the except stack
- Add missing PopTop after CallIntrinsic1 ImportStar
- Fix async comprehension SetupFinally/GetANext emission order
- Fix try-except* handler: move BUILD_LIST/COPY before handler loop
- Rework unwind_fblock for HandlerCleanup, TryExcept, FinallyTry,
  FinallyEnd, and With/AsyncWith to emit proper PopBlock sequences

New CFG analysis passes (ir.rs):
- mark_except_handlers(): mark blocks targeted by SETUP_* instructions
- label_exception_targets(): walk CFG with except stack to set
  per-instruction handler info and convert POP_BLOCK to NOP
- convert_pseudo_ops(): lower remaining pseudo ops after analysis
- Compute handler entry depth from SETUP_* type in max_stackdepth()
  (SETUP_CLEANUP=+2, SETUP_FINALLY/SETUP_WITH=+1)
- Fix SEND jump_effect from -1 to 0 (receiver stays until END_SEND)
- Add underflow guard for handler stack_depth calculation

Instruction metadata (instruction.rs):
- SetupCleanup/SetupFinally/SetupWith now carry target: Arg<Label>
- Add is_block_push()/is_pop_block() to PseudoInstruction/AnyInstruction
- Fix LoadSpecial stack_effect from (2,2) to (1,1)
- Set Setup* stack_effect_info to (0,0) for fall-through consistency

Fixes 3 test_coroutines expectedFailures:
- test_with_8, test_for_assign_raising_stop_async_iteration{,_2}
2026-02-01 17:53:57 +09:00
Jeong, YunWon
25bf682006 Fix number __format__ (#6930) 2026-02-01 17:17:09 +09:00
Jeong, YunWon
0546bb0410 more deps grouping (#6931) 2026-02-01 17:01:56 +09:00
Lee Dogeon
c6f7c6e273 Remove RustPython-specific workaround in timeit module (#6923) 2026-02-01 14:52:36 +09:00
Jeong, YunWon
5e4c3b07fb gc is vm/stdlib (#6929) 2026-02-01 13:21:24 +09:00
Jeong, YunWon
fdf93c5a19 Merge pull request #6928 from fanninpm/3.14-test-pep646-syntax
Update `test_pep646_syntax` from v3.14.2
2026-02-01 13:18:29 +09:00
Jeong, YunWon
380500c92d Merge pull request #6927 from fanninpm/3.14-test-termios
Update `test_termios` from v3.14.2
2026-02-01 13:18:04 +09:00
Jeong, YunWon
db4a2d5a9f Merge pull request #6921 from fanninpm/3.14-test-frozen
Update `test_frozen` from v3.14.2
2026-02-01 13:17:39 +09:00
Jeong, YunWon
294eb2c904 Merge pull request #6926 from fanninpm/3.14-test-string
Update `test_string` from v3.14.2
2026-02-01 13:16:57 +09:00
Jeong, YunWon
80e9172de0 Merge pull request #6922 from fanninpm/3.14-test-osx-env
Update `test_osx_env` from v3.14.2
2026-02-01 12:13:05 +09:00
Jeong, YunWon
60bec8a561 rework weakref (#6916)
* Replace WeakListInner with inline atomic weakref list and stripe locks

Remove heap-allocated WeakListInner (OncePtr<PyMutex<WeakListInner>>).
WeakRefList now holds two inline atomic pointers (head, generic).
PyWeak.parent replaced with wr_object pointing directly to referent.
Add weakref_lock module with AtomicU8 spinlock array for thread safety.
Rewrite upgrade/clear/drop_inner/count/get_weak_references with stripe lock.
Make Pointers methods public in linked_list.rs.
Remove expectedFailure from test_subclass_refs_dont_replace_standard_refs.

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-01 12:12:33 +09:00
Jeong, YunWon
3e629287da Merge pull request #6925 from fanninpm/3.14-test-select
Update `test_select` from v3.14.2
2026-02-01 12:10:39 +09:00
Padraic Fanning
f6d1fe4aa1 Add test_pep646_syntax to platform-independent tests 2026-01-31 21:48:51 -05:00
Padraic Fanning
deb517d346 Mark failing tests 2026-01-31 21:34:47 -05:00
CPython Developers
17e810e6ed Update test_frozen from v3.14.2-288-g06f9c8ca1c 2026-01-31 21:34:47 -05:00
Padraic Fanning
2bb027ac6a Mark failing tests 2026-01-31 21:23:23 -05:00
CPython Developers
553be2af33 Update test_pep646_syntax from v3.14.2-288-g06f9c8ca1c 2026-01-31 21:18:15 -05:00
Padraic Fanning
331c0bd613 Mark failing and erroring tests 2026-01-31 21:12:03 -05:00
Padraic Fanning
539f79b0b7 Skip crashing tests 2026-01-31 21:11:46 -05:00
CPython Developers
b220bddd06 Update test_termios from v3.14.2-288-g06f9c8ca1c 2026-01-31 21:05:57 -05:00
CPython Developers
771d563171 Update test_string from v3.14.2-288-g06f9c8ca1c 2026-01-31 21:00:06 -05:00
Padraic Fanning
04ee6b8adc Mark failing tests 2026-01-31 20:51:53 -05:00
Padraic Fanning
023f5efc43 Skip test that took too long 2026-01-31 20:51:34 -05:00
CPython Developers
fe3d273bfd Update test_select from v3.14.2-288-g06f9c8ca1c 2026-01-31 20:48:48 -05:00
CPython Developers
bb8f759725 Update test_osx_env from v3.14.2-288-g06f9c8ca1c 2026-01-31 19:56:55 -05:00
Jeong, YunWon
1a437fc818 Merge pull request #6920 from fanninpm/3.14-test-fork1
Update `test_fork1` from v3.14.2
2026-02-01 09:42:04 +09:00
Jeong, YunWon
111ced08e4 Fix member_descriptor to match CPython behavior (#6915)
descriptor.rs:
- Add __objclass__, __name__ (pymember) and __reduce__ (pymethod)
- Add type check (descr_check) in descr_get; simplify None branch
- Remove incorrect BASETYPE flag
- Add MemberKind::Object (_Py_T_OBJECT = 6)
- Prevent Bool slot deletion (TypeError)
- Raise AttributeError on ObjectEx deletion when already None

pyclass.rs (derive-impl):
- Remove duplicate MemberKind enum; use MemberKindStr (Option<String>)
- Simplify MemberNursery map key from (String, MemberKind) to String
- Support #[pymember(type="object")] for _Py_T_OBJECT semantics

test_inspect.py:
- Remove expectedFailure from test_getdoc (now passing)
2026-02-01 09:41:38 +09:00
Jeong, YunWon
714d1ce58b gc module internal structure and API (#6910)
* gc module internal structure and API

Add gc_state module with GcState, GcGeneration, GcDebugFlags, GcStats.
Replace gc module stubs with working API backed by gc_state.
Add gc_callbacks and gc_garbage to Context.
Add is_gc_tracked, gc_finalized, gc_get_referents to PyObject.
Collection is stubbed (returns 0) — actual algorithm to follow.

* fix dict/weakref/generators

* unmark test_asyncio

* apply review
2026-02-01 08:51:39 +09:00
Shahar Naveh
988b8b865e Make to_oparg to return a Result<Self, MarshalError> (#6914) 2026-02-01 08:50:16 +09:00
Jeong, YunWon
7ea22806d4 Merge pull request #6912 from fanninpm/3.14-test-_locale
Update `test__locale` from v3.14.2
2026-02-01 08:49:12 +09:00
ShaharNaveh
07ba834381 Update os constant to libc 0.2.180 & target cpython 3.14 2026-02-01 08:48:20 +09:00
Padraic Fanning
d04a50e835 Mark expected failures 2026-02-01 08:47:12 +09:00
CPython Developers
379e285e03 Update test_extcall from v3.14.2-288-g06f9c8ca1c 2026-02-01 08:47:12 +09:00
fanninpm
c6307d3a52 Update test_coroutines from v3.14.2 (#6918)
* Update test_coroutines from v3.14.2-288-g06f9c8ca1c

* Mark expected failures on Linux

---------

Co-authored-by: CPython Developers <>
2026-02-01 08:46:00 +09:00
Padraic Fanning
6f9320bb11 Mark failing test 2026-01-31 15:21:00 -05:00
CPython Developers
a8b0ffbfcb Update test_fork1 from v3.14.2-288-g06f9c8ca1c 2026-01-31 15:17:44 -05:00
Padraic Fanning
4d537c7b09 Skip tests that panic on macOS (and windows) 2026-01-31 15:13:23 -05:00
CPython Developers
7675e10236 Update test__locale from v3.14.2-288-g06f9c8ca1c 2026-01-31 15:13:23 -05:00
Shahar Naveh
c771427c38 Use stack_effect_info for getting pop&push count (#6913) 2026-01-31 23:45:46 +09:00
Jeong, YunWon
ba8749b792 Align del behavior (#6772)
* slot_del

* refcount inc_by for atomicity

* temp patch multithreading

* apply review
2026-01-31 23:09:10 +09:00
Copilot
cdd47b6a59 Upgrade hmac to Python 3.14.2 (#6863)
---------

Co-authored-by: moreal <26626194+moreal@users.noreply.github.com>
2026-01-31 18:40:34 +09:00
fanninpm
10cbfa9361 Add f_trace_opcodes (#6911)
* Add f_trace_opcodes

Needed for bdb, and, by extension, pdb, to work.

* Fix compile error
2026-01-31 17:03:17 +09:00
Shahar Naveh
743d6b83d3 Split stack_effect into pushed & popped (#6893) 2026-01-31 16:58:14 +09:00
Lee Dogeon
b1f158e5a8 Mark tests failing only in Windows 2026-01-31 10:11:58 +09:00
CPython Developers
31ee8b593f Update codecs from v3.14.2 2026-01-31 10:11:58 +09:00
Lee Dogeon
0c174624b4 Introduce /upgrade-pylib-next (#6827)
* Simplify upgrade-pylib.md by removing manual copy steps handled by quick command

* Let upgrade-pylib run auto-mark instead marking manually

* Let upgrade-pylib a separate commit from updating module

* Correct commands' permissions

* Add open PR check to upgrade-pylib-next command

* Fix dependency pattern example in upgrade-pylib-next command

* Let upgrade-pylib command to invetigate failing tests

* Add pre-commit review to invetigate-test-failure command

* Use deps command to get dependent tests in upgrade-pylib workflow
2026-01-31 07:40:39 +09:00
CPython Developers
f6d562e2ee Update test_baseexception from v3.14.2 2026-01-31 07:14:37 +09:00
Jeong, YunWon
050faa312a Fix _overlapped segfault and missing _winapi constants
- Fix from_windows_err using new_exception_empty on OSError subclasses
  (ConnectionRefusedError, ConnectionAbortedError), which caused segfault
  in release builds due to debug_assert on type size mismatch
- Move ConnectPipe from instance method to module-level function
- Add Destructor for Overlapped to cancel pending I/O on object cleanup
- Add NMPWAIT_NOWAIT, NMPWAIT_USE_DEFAULT_WAIT, NMPWAIT_WAIT_FOREVER
  constants to _winapi
2026-01-31 06:39:32 +09:00
Jeong, YunWon
adb7a91ec6 Merge pull request #6906 from ShaharNaveh/update-symtable
Update `symtable` from 3.14.2
2026-01-31 06:34:44 +09:00
Jeong, YunWon
5df67028ff Update test_exceptions from v3.14.2 (#6904)
* [update_lib] fix async detection

* Update test_exceptions from v3.14.2

* Update test_except_star from v3.14.2

* Update test_exception_hierarchy from v3.14.2

* Update test_exception_variations from v3.14.2

* Auto-format: ruff format

---------

Co-authored-by: CPython Developers <>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-31 06:30:06 +09:00
github-actions[bot]
9338509a71 Auto-format: ruff format 2026-01-30 20:50:37 +00:00
CPython Developers
980e76ea1f Update test_exception_variations from v3.14.2 2026-01-31 05:49:16 +09:00
CPython Developers
cb138c8f37 Update test_exception_hierarchy from v3.14.2 2026-01-31 05:49:16 +09:00
CPython Developers
7cd55802f6 Update test_except_star from v3.14.2 2026-01-31 05:49:16 +09:00
ShaharNaveh
ec906679b9 Update symtable from 3.14.2 2026-01-30 19:13:59 +02:00
CPython Developers
8265279411 Update test_exceptions from v3.14.2 2026-01-31 00:59:29 +09:00
Jeong, YunWon
922b644116 [update_lib] fix async detection 2026-01-31 00:59:29 +09:00
Jeong, YunWon
528d6573e0 [update_lib] Fix async func auto-mark (#6903) 2026-01-31 00:00:41 +09:00
Jeong, YunWon
bb0a201502 Merge pull request #6901 from ShaharNaveh/update-decimal
Update `decimal` from 3.14.2
2026-01-30 23:41:34 +09:00
ShaharNaveh
6af0af2b3c Unmark passing test 2026-01-30 16:39:33 +02:00
ShaharNaveh
a1b41ad2ad Update decimal from 3.14.2 2026-01-30 16:38:36 +02:00
Copilot
568f24cc84 Upgrade timeit module from Python 3.14.2 (#6854)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-30 22:40:48 +09:00
Shahar Naveh
51701ab2a4 Update class related tests from 3.14.2 (#6900)
* Update `test_class` from 3.14.2

* Update test_genericclass

* Update test_subclassinit

* Add test_metaclass

* Patch `test_class`

* Patch test_subclassinit

* Patch `test_metaclass`

----

Co-authored-by: CPython Developers <>
2026-01-30 22:38:49 +09:00
Noa
276a4bdc80 Make paths in macro arguments relative to the source file, not MANIFEST_DIR (#6873) 2026-01-30 22:37:18 +09:00
Shahar Naveh
62c67546ac Update optparse,gettext,textwrap from 3.14.2 (#6896)
* Update optparse from 3.14.2

* Update `gettext`

* Update `textwrap`
2026-01-30 20:48:57 +09:00
Jeong, YunWon
9183661d88 Merge pull request #6899 from youknowone/weakref
Update weakref from v3.14.2
2026-01-30 20:47:53 +09:00
Shahar Naveh
75bcd24918 Update py_compile from 3.14.2 (#6897) 2026-01-30 19:24:33 +09:00
Jeong, YunWon
3daabbd2be Fix weakref 2026-01-30 19:21:10 +09:00
CPython Developers
0446bc71a0 Update weakref from v3.14.2 2026-01-30 19:21:10 +09:00
Jeong, YunWon
8e9d591369 Align GetAwaitable to Python 3.14.2 (#6895) 2026-01-30 12:13:39 +09:00
ShaharNaveh
e4eb0a7116 Update fractions from 3.14.2 2026-01-30 07:48:29 +09:00
Jeong, YunWon
4ca2da4987 Merge pull request #6838 from youknowone/asyncio
Add _asyncio and add test_asyncio
2026-01-29 22:35:30 +09:00
Jeong, YunWon
d0003f85c5 skip test_asyncio on windows 2026-01-29 22:13:26 +09:00
Jeong, YunWon
95abec4c21 More Load instructions including LoadLocals, LoadFastBorrow (#6886)
* fix stdlib_io

* more scpell dict

* LoadLocals

* LoadFastBorrow

* more ops
2026-01-29 19:33:46 +09:00
Jeong YunWon
d8fa1b4366 _asyncio rust impl 2026-01-29 16:48:33 +09:00
CPython Developers
850a0a7847 test_asyncio from Python 3.14.2 2026-01-29 16:48:33 +09:00
Jeong YunWon
aaa86584e5 Add _asyncio 2026-01-29 16:48:33 +09:00
Jeong YunWon
2e0b76556a Add more socket consts 2026-01-29 16:48:33 +09:00
Jeong, YunWon
fcca0feb70 Add missing windows APIs 2026-01-29 16:48:33 +09:00
Jeong, YunWon
e363b14e81 fix ssl MSG_PEEK (#6892) 2026-01-29 16:48:02 +09:00
Jeong, YunWon
e2fda95f9a Merge pull request #6885 from youknowone/overlapped
impl more overlapped
2026-01-28 17:01:06 +09:00
Jeong, YunWon
c3a5f9d5b2 cspell dict 2026-01-28 16:57:34 +09:00
Jeong, YunWon
0cc43e192c Implement Windows overlapped I/O for asyncio support
Add comprehensive Windows async I/O support to the overlapped module:

Winsock Extensions:
- Initialize AcceptEx, ConnectEx, DisconnectEx, TransmitFile via WSAIoctl
- Proper cleanup with all four extension locks checked

Overlapped Methods:
- ReadFile, ReadFileInto: Async file reading
- WriteFile: Async file writing
- WSARecv, WSARecvInto: Async socket receive
- WSASend: Async socket send
- WSARecvFrom, WSARecvFromInto: Async UDP receive with address
- WSASendTo: Async UDP send to address
- AcceptEx: Async socket accept
- ConnectEx: Async socket connect
- DisconnectEx: Async socket disconnect
- TransmitFile: Async file transmission over socket
- ConnectNamedPipe, ConnectPipe: Named pipe support

Module Functions:
- CreateIoCompletionPort: IOCP creation/association
- GetQueuedCompletionStatus: Dequeue completion packets
- PostQueuedCompletionStatus: Post completion packets
- RegisterWaitWithQueue: Register wait with completion port callback
- UnregisterWait, UnregisterWaitEx: Unregister waits with proper cleanup
- BindLocal: Bind socket to local address
- CreateEvent, SetEvent, ResetEvent: Event object management
- FormatMessage: Windows error message formatting

Safety & Correctness:
- Validate buffer contiguity before async operations
- Validate size parameters to prevent buffer overflow
- Use Arc for RegisterWaitWithQueue callback data to prevent UAF
  (callback may run after UnregisterWait returns per Windows API docs)
2026-01-28 16:56:47 +09:00
Jeong, YunWon
d257d95362 set encodings for opcode_metadata scripts 2026-01-28 16:56:47 +09:00
Jeong, YunWon
d2166dd93a Fix sorted() to use __lt__ instead of __gt__ (#6887)
* test

* Fix sorted() to use __lt__ instead of __gt__

CPython's sort uses __lt__ for comparisons, but RustPython was using
__gt__. This caused issues when only __lt__ was overridden on a
subclass (e.g., NamedTuple with custom __lt__), as it would fall back
to the parent class's comparison instead of using the overridden method.
2026-01-28 16:55:51 +09:00
Elmir
44c3179ae0 Docs: add reference to rustpython_derive close to example (#6882)
Include a link for native module macros documentation, after an
example where they are used.
2026-01-28 10:25:48 +09:00
fanninpm
4c1c704bb1 Add ExpatError and error to pyexpat (#6889)
* Stub out xml.parsers.expat.ExpatError

* Alias `ExpatError` to `error`
2026-01-28 10:19:00 +09:00
fanninpm
601b9d8984 Make issue_assign job run on ubuntu-slim runner (#6890) 2026-01-28 08:28:57 +09:00
Jeong, YunWon
5b7f93f6b6 Fix CI: cargo doc --lock (#6888)
* cargo doc --locked

* Update Cargo.lock
2026-01-27 21:51:48 +09:00
Jeong, YunWon
aa99c05231 Merge pull request #6870 from ShaharNaveh/update-dis-3-14-2 2026-01-27 13:20:39 +09:00
dependabot[bot]
364ddaab54 Bump cranelift from 0.127.2 to 0.128.0 (#6877)
* Bump cranelift from 0.127.2 to 0.128.0

Bumps [cranelift](https://github.com/bytecodealliance/wasmtime) from 0.127.2 to 0.128.0.
- [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
  dependency-version: 0.128.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>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-27 13:20:06 +09:00
ShaharNaveh
253414f168 Update test_compiler_codegen and test_compiler_assemble 2026-01-27 10:49:05 +09:00
ShaharNaveh
7cc4f43779 Update test_peepholer from 3.14.2 2026-01-27 10:49:04 +09:00
dependabot[bot]
5a02051d68 Bump actions/checkout from 6.0.1 to 6.0.2 (#6881)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v6.0.1...v6.0.2)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  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-01-27 09:58:52 +09:00
dependabot[bot]
4eb11ba940 Bump actions/setup-python from 6.1.0 to 6.2.0 (#6880)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6.1.0...v6.2.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.2.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-01-27 09:58:33 +09:00
Jeong, YunWon
edca32a194 Fix ssl shutdown (#6871)
* Fix ssl shutdown

* Fix thread
2026-01-27 02:50:15 +09:00
dependabot[bot]
93274d3888 Bump quote from 1.0.43 to 1.0.44
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.43 to 1.0.44.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.43...1.0.44)

---
updated-dependencies:
- dependency-name: quote
  dependency-version: 1.0.44
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-27 00:04:20 +09:00
dependabot[bot]
f548b3d71a Bump thiserror from 2.0.17 to 2.0.18
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 2.0.17 to 2.0.18.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0.17...2.0.18)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-version: 2.0.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-26 23:48:29 +09:00
dependabot[bot]
0dd73170e5 Bump openssl-probe from 0.2.0 to 0.2.1
Bumps [openssl-probe](https://github.com/rustls/openssl-probe) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/rustls/openssl-probe/releases)
- [Commits](https://github.com/rustls/openssl-probe/compare/0.2.0...0.2.1)

---
updated-dependencies:
- dependency-name: openssl-probe
  dependency-version: 0.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-26 23:47:39 +09:00
Jeong, YunWon
6a3643cdde Improve deps output (#6874)
* Improve deps output: [x]/[ ] sync status, TODO counts

- Replace ambiguous [+] with [x] (synced) / [ ] (not synced)
- Add (TODO: n) suffix for test files with expectedFailure/skip markers

* Refactor update_lib: extract shared utilities
2026-01-26 18:35:35 +09:00
Jeong, YunWon
93b26bf595 Merge pull request #6869 from youknowone/opcode
Update opcode, dis from 3.14.2
2026-01-26 11:39:23 +09:00
Jeong YunWon
4b823ebaf5 mark test_dis failures 2026-01-26 10:40:23 +09:00
CPython Developers
131c296bb4 Update dis from v3.14.2 2026-01-26 10:40:22 +09:00
CPython Developers
0b806b9131 Update opcode from v3.14.2 2026-01-26 10:40:22 +09:00
Jeong YunWon
8c73f3cb30 emit RESUME 2026-01-26 10:40:22 +09:00
Jeong YunWon
9216500355 reimpl smallint 2026-01-26 10:40:22 +09:00
Jeong YunWon
b4d09e081d get_common_constants 2026-01-26 10:40:22 +09:00
Noa
617cdc9724 Make validate_downcastable_from unsafe (#6851) 2026-01-26 10:39:08 +09:00
Noa
0a9d41d3dd Depend on serde_core instead of serde (#6872) 2026-01-26 10:23:19 +09:00
Jeong, YunWon
7eceb145b1 more optimization (#6860) 2026-01-25 10:18:47 +02:00
CPython Developers
6595d50d18 Update test_apple from v3.14.2 2026-01-25 15:20:05 +09:00
Jeong, YunWon
9071147db5 [update_lib] Apply test grouping to deps (#6867)
* quick resolve test path too

* Fix migrate resolve

* Remove test/ from deps data

* resolve test to lib
2026-01-24 18:40:57 +02:00
Jeong, YunWon
4963cc659f Merge pull request #6858 from youknowone/win-lib
Update urllib and windows libraries from v3.14.2
2026-01-25 00:19:44 +09:00
Jeong, YunWon
26d64b9fda impl more msvcrt 2026-01-25 00:18:55 +09:00
CPython Developers
1251fbf0ba Update test_msvcrt from v3.14.2 2026-01-25 00:18:55 +09:00
CPython Developers
3e39990503 Update test_winapi from v3.14.2 2026-01-25 00:18:55 +09:00
CPython Developers
4f1d191ca9 Update nturl2path from v3.14.2 2026-01-25 00:18:55 +09:00
Jeong, YunWon
2ac1b04914 Upgrade nt_path from v3.14.2 2026-01-25 00:18:55 +09:00
Jeong YunWon
448658e49d Update urllib from v3.14.2 2026-01-25 00:18:55 +09:00
Shahar Naveh
77add04d3d Update to ruff 0.14.14 (#6861) 2026-01-24 22:49:19 +09:00
Jeong, YunWon
f35791ec64 Fix update_lib tests (#6866) 2026-01-24 22:47:03 +09:00
Jeong, YunWon
e0e29260f5 Remove Copy from PyPayload 2026-01-24 22:03:30 +09:00
Jeong, YunWon
0cc8f63849 [update_lib] deps grouping (#6854) 2026-01-24 21:29:11 +09:00
Copilot
185f360fea Update html module from Python 3.14.2 (#6855) 2026-01-24 19:12:23 +09:00
Copilot
f8d4d991f1 Upgrade zipimport to Python 3.14.2 (#6857) 2026-01-24 16:11:36 +09:00
Jeong, YunWon
483e4a2bab Align psuedo ops to CPython 3.14.2 (#6846)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Optimized attribute and super-attribute bytecode emission and
consolidated pseudo-instruction handling, improving consistency and
stack-effect accuracy.
* **Chores**
  * Adjusted opcode constants and simplified opcode/name mappings.
* **New Features**
* Added a utility to enumerate special method names and a helper for
resolving dependency parent modules.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-24 16:10:36 +09:00
Jeong YunWon
c0bdb9a3e5 align psuedo ops to 3.14.2 2026-01-24 16:09:40 +09:00
Jeong YunWon
771650a012 align HAVE_ARGUMENT 2026-01-24 16:09:40 +09:00
Jeong, YunWon
691d2816f9 update_lib todo also shows test todo (#6859)
also tracking untracked files considered in  #6775
2026-01-24 16:04:36 +09:00
Copilot
eedc70dfae Upgrade cmd module from CPython v3.14.2 (#6853)
Upgrades the `cmd` module and its tests from CPython v3.14.2 tag.
2026-01-24 11:37:32 +09:00
Copilot
da41a0cb20 Upgrade quopri from Python 3.14.0 (#6852)
Upgrades the `quopri` module from Python 3.14.0 as part of the ongoing
standard library update effort.

## Changes

- Updated `Lib/quopri.py` from CPython v3.14.0
  - Removed shebang line
  - Changed file mode from executable to regular file
- No functional changes to the module
2026-01-24 10:57:22 +09:00
Noa
f842fbe25d Use std::fmt::from_fn (#6850)
New in 1.93 - replaces our ad-hoc equivalent types.
2026-01-24 09:21:45 +09:00
Copilot
b0c5bbc589 Update warnings module from CPython 3.14.1 (#6840) 2026-01-24 01:24:41 +09:00
Jeong, YunWon
d86bec3252 Update copyreg from v3.14.2 (#6848) 2026-01-23 16:23:56 +00:00
Jeong, YunWon
180c68e76b improve deps CI formatting and name resolution (#6847)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Improvements**
* Enhanced test module dependency tracking in automated checks to
properly identify and report test-related dependencies.
* Improved dependency resolution logic with better module name mapping
and deduplication for clearer dependency reporting.
* Refined formatting of dependency information and todo items for
improved readability.
* Dependency review comments now automatically refresh with the latest
information when updated.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-24 00:32:32 +09:00
Jeong, YunWon
af7bbfa104 derive Copy where possible (#6844)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Made many lightweight/internal types trivially copyable to simplify
value handling across the codebase.
* Added a workspace lint to surface missing copy implementations as
warnings.
  * Extended buffer support with explicit retain/release hooks.

* **Bug Fixes**
* Improved diagnostics and debug output for thread/lock acquisition
scenarios.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-23 15:24:17 +00:00
CPython Developers
4c670ba5a2 Update copyreg from v3.14.2 2026-01-24 00:24:12 +09:00
CPython Developers
dbbd921a53 Update linecache from v3.14.2 2026-01-23 23:58:24 +09:00
CPython Developers
ac2729511f Update operator from v3.14.2 2026-01-23 23:57:31 +09:00
Jeong, YunWon
37fe0cfae2 Seperate between scope exit & unconditional jump opcodes (#6841)
See:
1fa166888b/Include/internal/pycore_opcode_utils.h (L39-L44)

and
1fa166888b/Include/internal/pycore_opcode_utils.h (L52-L55)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Updated compiler control flow analysis for improved accuracy in
dead-code elimination and stack-depth tracking.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-23 14:56:39 +00:00
ShaharNaveh
2c2b13784e Clippy 2026-01-23 16:41:24 +02:00
ShaharNaveh
a9364cbc52 Fix warnings 2026-01-23 16:41:19 +02:00
ShaharNaveh
2f76ef654b Add workspace lint 2026-01-23 16:41:19 +02:00
Lee Dogeon
5c7f6a2afa Update types from v3.14.2 (#6833)
This pull request updates `types` module to v3.14.2. While doing it, it
fixes also async-related feature. This pull request's base is generated
by #6827.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Generators that act as iterable coroutines are now recognized as
awaitable, improving async behavior.
* Yield-from and await interactions now handle coroutine-iterable
sources more consistently.

* **Bug Fixes**
* Reduces spurious TypeError cases when awaiting or yielding from
wrapped coroutine-like generators.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-23 23:23:02 +09:00
ShaharNaveh
2ee48bc5aa Seperate between scope exit & unconditional jump opcodes 2026-01-23 14:25:03 +02:00
Jeong, YunWon
efce325cbf Fix asyncio related compiler/library issues (#6837)
* Fix socket bytes support

* fix unwind_fblock

* fix posix.sendfile

* fix ssl_write

* Fix SSL ZeroReturn

* fix context

* fix generator

* Enable unittest test_async_case again
2026-01-23 19:59:29 +09:00
Lee Dogeon
9b56aa5b60 [update_lib] show dependent tests in deps subcommand (#6828)
---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2026-01-23 08:13:16 +09:00
Jeong, YunWon
125ade5f55 fix clippy 1.93.0 (#6836)
* fix clippy 1.93.0

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-23 02:18:39 +09:00
Jeong, YunWon
11a59bb405 Merge pull request #6835 from youknowone/updatelib-ci
CI runs update_lib
2026-01-23 00:57:07 +09:00
Jeong, YunWon
8cbaf3c2ef typevar as module (#6834) 2026-01-22 22:06:14 +09:00
Jeong YunWon
43bd4940ea CI tests update_lib 2026-01-22 20:59:49 +09:00
Jeong YunWon
0ba57b0632 Fix ast.Constant.__init__ 2026-01-22 20:59:49 +09:00
Jeong, YunWon
40a43f3210 instruction improvements (#6829)
New Features

Direct small-integer loading (0–255) and locals-loading for faster execution
Async-generator wrapping and improved generator resume behavior
Performance

Faster integer loads and streamlined jump/loop handling for better runtime performance
Bug Fixes

More robust StopIteration handling and stricter init return checks
Corrected iterator cleanup for async and sync loops
Improvements

Aligns loop and jump semantics with CPython 3.14 patterns
2026-01-22 17:25:56 +09:00
CPython Developers
aed3a60e17 Update pydoc_data from v3.14.2 2026-01-22 16:02:41 +09:00
Jeong, YunWon
47df6dd270 Merge pull request #6740 from youknowone/multi-phase-module-init
Multi phase module init
2026-01-22 12:12:43 +09:00
Jeong YunWon
20376451eb Implement Py_mod_create slot support in multi-phase init 2026-01-22 11:21:42 +09:00
dependabot[bot]
512c84dbc0 Bump lodash from 4.17.21 to 4.17.23 in /wasm/demo
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-22 09:31:19 +09:00
CPython Developers
9c8aef3c05 Update token from v3.14.2 2026-01-22 08:02:43 +09:00
Jeong YunWon
bc02b2318c module_exec 2026-01-21 22:39:40 +09:00
Jeong YunWon
32d2406fa8 module_def 2026-01-21 22:39:40 +09:00
Jeong YunWon
fee1a4b097 multiphase 2026-01-21 22:39:39 +09:00
Jeong YunWon
3ccff6d7c9 os.stat remove winodws-only attrs 2026-01-21 22:39:39 +09:00
Jeong, YunWon
726a053ca2 Merge pull request #6818 from youknowone/regrtest
libregrtest from 3.14.2
2026-01-21 21:35:43 +09:00
Jeong, YunWon
9e9bfa56c2 Update libregrtest from v3.14.2 2026-01-21 20:44:26 +09:00
Jeong YunWon
2fb2a7eda8 winapi.WaitForSingleObject 2026-01-21 20:44:25 +09:00
fanninpm
122fac9c53 Optimize CI cache usage (#6707)
* Make whats_left.py compile with same settings as before

* Add --no-default-features to scripts/whats_left.py

* Build RustPython with no default features

* Retrigger CI
2026-01-21 20:17:42 +09:00
Lee Dogeon
ce1bbc7938 Bump mimetypes to 3.14.2 (#6808)
* Bump mimetypes to 3.14.2

* Unmark resolved tests

* Mark failed test in Windows
2026-01-21 20:14:30 +09:00
Jeong, YunWon
2df879c5ee Fix Drop to prevent TLS data loss (#6825)
Remove pending_tls_output.clear() from Drop implementation.

SSLSocket._real_close() in Python doesn't call shutdown() before
closing - it just sets _sslobj = None. This means pending TLS data
in the output buffer may not have been flushed to the socket yet.
Clearing this buffer in Drop causes data loss, resulting in empty
HTTP responses (test_socketserver failure on Windows).

The explicit clearing is also unnecessary since all struct fields
are automatically freed when the struct is dropped.
2026-01-21 17:29:22 +09:00
Jeong, YunWon
1b5deea53a Merge pull request #6826 from moreal/bump-3.14.2-reprlib
Update reprlib from v3.14.2
2026-01-21 17:17:47 +09:00
Lee Dogeon
4eecd15a2a Mark failing tests 2026-01-21 15:10:40 +09:00
CPython Developers
d7dba07304 Update reprlib from v3.14.2 2026-01-21 15:10:40 +09:00
Jeong, YunWon
d690b2d65e [update_lib] todo subcommand (#6823)
* [update_lib] todo

* better CI comment
2026-01-21 09:00:45 +09:00
Jeong, YunWon
22974cff20 Update struct from v3.14.2 (#6824)
Co-authored-by: CPython Developers <>
2026-01-21 03:05:05 +09:00
Jeong, YunWon
a7f2351244 Remove BEFORE_WITH,BINARY_SUBSCR (#6822) 2026-01-21 02:58:24 +09:00
Jeong, YunWon
58c804309b [update_lib] show deps (#6821)
* show_deps

* soft deps tree

* show deps CI
2026-01-21 01:25:30 +09:00
Jeong, YunWon
274e8b4b6b Remove JUMP_IF_NOT_EXC_MATCH, SET_EXC_INFO (#6820) 2026-01-21 01:23:43 +09:00
Jeong, YunWon
90ff571493 Update _collections_abc from v3.14.2 (#6819)
Co-authored-by: CPython Developers <>
2026-01-21 01:01:09 +09:00
Jeong, YunWon
e0cceaf31c Remove ReturnConst/Break/Continue ops (#6816)
* Remove ReturnConst

* Remove break/continue ops
2026-01-21 00:03:43 +09:00
Lee Dogeon
3bcd071e4d Bump collections to 3.14.2 (#6770)
* Bump collections to 3.14.2

* Unmark resolved tests
2026-01-20 23:58:43 +09:00
Jeong, YunWon
5ef8d48e5a update_lib hard dependency resolver (#6817)
* update_lib __init__ handling

* dependency

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-20 23:52:28 +09:00
Jeong, YunWon
e01f26c1f6 Update inspect from v3.14.2 (#6815)
* Update inspect from v3.14.2

* temp asyncio.event._set_event_loop_policy

* mark inspect tests

---------

Co-authored-by: CPython Developers <>
2026-01-20 21:34:34 +09:00
Jeong, YunWon
94ccfb8b18 Prohibit AI PR submit (#6813) 2026-01-20 21:13:28 +09:00
Jeong, YunWon
0f28d4e199 Merge pull request #6811 from youknowone/functools
Implement more functools features and Update functools from v3.14.2
2026-01-20 19:18:14 +09:00
Lee Dogeon
0cad3cb8b8 Use empty list as default according to Python.asdl (#6812)
* Use empty list as default in ClassDef.{bases,keywords}

* Unmark resolved tests

* Use empty list as default in FunctionDef.type_params

* Use empty list as default in ClassDef.type_params
2026-01-20 18:02:42 +09:00
Jeong, YunWon
479bb7af1a skip weakref crash test 2026-01-20 17:33:07 +09:00
Jeong, YunWon
ff4f7a0e94 temp inspect patch 2026-01-20 17:33:07 +09:00
Jeong, YunWon
ddc5ba0b98 cspell phcount 2026-01-20 17:32:58 +09:00
Jeong, YunWon
c82da8b887 mark functools tests 2026-01-20 17:32:52 +09:00
CPython Developers
40c29f689f Update functools from v3.14.2 2026-01-20 17:32:52 +09:00
Jeong, YunWon
4d27603cb5 fix functools.reduce 2026-01-20 17:32:52 +09:00
Jeong, YunWon
b472e88caf classmethod/staticmethod __annotate__ 2026-01-20 17:32:52 +09:00
Jeong, YunWon
f0c3e7d51f Remove SUBSCRIPT, JUMP_IF_{TRUE,FALSE}_OR_POP (#6810)
* Align ForIter behavior (incomplete)

* Romove JUMP_IF_{TRUE,FALSE}_OR_POP, subscript

* Remove LoadAssetionError

* Update snapshot for ForIter behavior change
2026-01-20 16:21:23 +09:00
Lee Dogeon
52a854a57a Unmark resolved test 2026-01-20 15:29:05 +09:00
Lee Dogeon
c8b1cb8cab Bump re to 3.14.2 2026-01-20 15:29:05 +09:00
Jeong, YunWon
25a3d6dde1 functools.Placeholder 2026-01-20 13:45:52 +09:00
Jeong, YunWon
6e842cf110 PyPartial::__get__ 2026-01-20 11:42:59 +09:00
github-actions[bot]
f1fd1e9121 Auto-format: ruff format 2026-01-20 11:38:26 +09:00
Jeong, YunWon
41cdc5cd5b BooleanOptionalAction 2026-01-20 11:38:26 +09:00
Lee Dogeon
876d3f5e80 Correct testcase 2026-01-20 11:38:26 +09:00
Lee Dogeon
fe6a60ade7 Fix error lines extraction 2026-01-20 11:38:26 +09:00
Jeong, YunWon
d119c47d2e Remake update_lib 2026-01-20 11:38:26 +09:00
github-actions[bot]
6f41a9491b Auto-format: cargo fmt --all 2026-01-20 10:59:47 +09:00
Jeong, YunWon
1740b083b8 namespace constructor and __replace__ 2026-01-20 10:59:47 +09:00
Jeong, YunWon
1dceb7533d mark failing tests on test_types 2026-01-20 10:59:47 +09:00
Jeong, YunWon
301ff32a59 mark failing test_dataclasses 2026-01-20 10:59:47 +09:00
CPython Developers
2823053614 Update test_types from v3.14.2 2026-01-20 10:59:47 +09:00
CPython Developers
a7a823daaa Update typing from v3.14.2 2026-01-20 10:59:47 +09:00
Jeong, YunWon
700b34adeb Hashable for PyMappingProxy 2026-01-20 10:59:47 +09:00
Jeong, YunWon
bb72418d9e _typing.Union 2026-01-20 10:59:47 +09:00
Jeong, YunWon
2bbb3d07c6 _abc 2026-01-20 10:59:47 +09:00
Jeong, YunWon
ef2c0a6d9d More FromArgs message (#6806) 2026-01-20 10:37:27 +09:00
Jeong, YunWon
97167ab1cc Instruction 3.14 (#6805)
* renumber enum Instruction

* renumber magic

* Update test__opcode from 3.14.2

---------

Co-authored-by: CPython Devleopers <>
2026-01-20 03:52:52 +09:00
Jeong, YunWon
b76906a8bf Add sys._stdlib_dir (#6798) 2026-01-20 03:38:40 +09:00
dependabot[bot]
7d2f284ee6 Bump cranelift from 0.127.1 to 0.127.2 (#6802)
Bumps [cranelift](https://github.com/bytecodealliance/wasmtime) from 0.127.1 to 0.127.2.
- [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
  dependency-version: 0.127.2
  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-01-20 02:16:24 +09:00
Jeong, YunWon
2da102fab8 FromArgs with error_msg (#6804) 2026-01-20 02:10:19 +09:00
dependabot[bot]
e5720232e1 Bump chrono from 0.4.42 to 0.4.43 (#6803)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.42 to 0.4.43.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.42...v0.4.43)

---
updated-dependencies:
- dependency-name: chrono
  dependency-version: 0.4.43
  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-01-20 01:43:11 +09:00
dependabot[bot]
aff85c87ce Bump junction from 1.3.0 to 1.4.1 (#6801)
Bumps [junction](https://github.com/tesuji/junction) from 1.3.0 to 1.4.1.
- [Release notes](https://github.com/tesuji/junction/releases)
- [Changelog](https://github.com/tesuji/junction/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tesuji/junction/compare/v1.3.0...v1.4.1)

---
updated-dependencies:
- dependency-name: junction
  dependency-version: 1.4.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-01-20 01:42:55 +09:00
dependabot[bot]
b7f55decbd Bump cranelift-jit from 0.127.1 to 0.127.2 (#6800)
Bumps [cranelift-jit](https://github.com/bytecodealliance/wasmtime) from 0.127.1 to 0.127.2.
- [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.127.2
  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-01-20 01:06:56 +09:00
Jeong, YunWon
82a8f67c71 Replace custom opcodes with CPython standard sequences (#6794)
* Replace custom opcodes with standard sequences

Remove RustPython-specific opcodes (BuildListFromTuples,
BuildMapForCall, BuildSetFromTuples, BuildTupleFromTuples)
and replace their usage with CPython 3.14 standard opcode
sequences:

- BuildListFromTuples → BUILD_LIST + LIST_EXTEND loop
- BuildSetFromTuples → BUILD_SET + SET_UPDATE loop
- BuildTupleFromTuples → BUILD_LIST + LIST_EXTEND + CALL_INTRINSIC_1(ListToTuple)
- BuildMapForCall → DICT_MERGE loop

Implement missing opcodes:
- ListExtend: Extend list with iterable elements
- SetUpdate: Add iterable elements to set
- DictMerge: Merge dict with duplicate key checking

* Auto-generate: generate_opcode_metadata.py

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-20 00:34:46 +09:00
Jeong, YunWon
d7a885cea8 sys._clear_type_descriptors (#6795) 2026-01-19 16:43:20 +09:00
Jeong, YunWon
6a064aad3b Drop for PySSLSocket (#6791) 2026-01-19 10:01:11 +09:00
Lee Dogeon
4ab6a45405 Use stdlib native modules in benchmarks (#6792)
Change benchmark interpreter from without_stdlib to with_init to load
Rust-optimized native modules like _json, ensuring benchmarks measure
actual optimized performance rather than pure Python fallbacks.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 08:43:25 +09:00
Jiseok CHOI
00440b179a Update sqlite3 and the tests to CPython 3.14.2 (#6787)
* Update sqlite3, dbm.sqlite3 and tests to CPython 3.14.2

* Add skip decorators for failing sqlite3 tests

Skip tests that fail due to unimplemented features or behavior differences:
- _iterdump not implemented (test_dump.py)
- Unraisable exception handling not implemented (test_hooks.py, test_userfunctions.py)
- Keyword-only arguments not supported for various methods
- Autocommit behavior differences (test_transactions.py)
- TransactionTests skipped due to timeout parameter type issue
- Various error message differences (test_dbapi.py)
- SQLITE_DBCONFIG constants not implemented
- Row and Connection signature inspection issues

All tests now pass with 95 skipped out of 493 total tests.

* Change @unittest.skip to @unittest.expectedFailure per code review

- Convert @unittest.skip decorators to @unittest.expectedFailure for tests that fail without panic/hang
- Keep @unittest.skip only for TransactionTests class (setUp fails with timeout=0 int type)

* fixup
2026-01-19 02:45:47 +09:00
Jeong, YunWon
130bb82a43 LoadClosure as pseudo op (#6789) 2026-01-19 02:45:00 +09:00
Lee Dogeon
9b5eefbb7b Replace _compression with compression and update related too (#6788) 2026-01-19 00:38:51 +09:00
Jeong, YunWon
0717b53123 Align CallFunctionEx to 3.14 (#6786) 2026-01-19 00:37:18 +09:00
Lee Dogeon
ed785e3d86 Bump base64 to 3.14.2 (#6776) 2026-01-19 00:04:06 +09:00
Jeong, YunWon
3a9f020234 update test_sys from 3.14.2 (#6781)
* sys.is_remote_debug_enabled, supports_isolated_interpreters

* Update test_sys form Python 3.14.2

---------

Co-authored-by: CPython Devleopers <>
2026-01-18 23:58:39 +09:00
Jiseok CHOI
050db4725f sqlite3: fix Connection.cursor() factory argument handling (#6783)
Fix test_is_instance in CursorFactoryTests by properly handling the
factory argument in Connection.cursor() method. Now the factory can
be passed as both positional and keyword argument, and returns the
correct subclass type instead of always returning PyRef<Cursor>.

- Use FromArgs derive macro with CursorArgs struct for argument parsing
- Return PyObjectRef instead of PyRef<Cursor> to allow subclasses
- Use fast_issubclass to validate returned cursor is a Cursor subclass
- Properly differentiate between 'no argument' and 'None passed'
2026-01-18 23:37:47 +09:00
Jeong, YunWon
9301ae2746 traverse and clear (#6780) 2026-01-18 23:23:11 +09:00
Jeong, YunWon
2b8fac3af3 auto mark parent tests (#6778)
* don't skip auto-format

* auto mark parent
2026-01-18 22:06:07 +09:00
Jeong, YunWon
252fa816d6 sys._jit (#6779) 2026-01-18 21:54:44 +09:00
Lee Dogeon
4b5e73577a Add metadata when uploading benchmark data (#6759)
Generate a metadata file alongside criterion benchmark data containing
the commit hash, ref name, and timestamp for tracking purposes.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 21:51:39 +09:00
Jeong, YunWon
d3d63ea2d3 enable test_bytes (#6777) 2026-01-18 21:33:37 +09:00
Jeong, YunWon
6f266651c0 better ssl write handling (#6763)
* ssl_write

* Fix thread count timing
2026-01-18 21:10:35 +09:00
Jeong, YunWon
e0479fe197 Merge pull request #6768 from youknowone/support
Update support to 3.14.2
2026-01-18 20:36:04 +09:00
Jeong, YunWon
e0b19c833c skip flaky test 2026-01-18 20:33:12 +09:00
Jeong, YunWon
8f7b1343bc mark and unmark successful/failing tests 2026-01-18 20:00:15 +09:00
Jeong, YunWon
3836958eaa Make ready_to_import always remove tempdir from sys.path (re-apply #6687) 2026-01-18 20:00:14 +09:00
CPython Devleopers
005014a3f9 Update test.support from 3.14.2 2026-01-18 19:56:55 +09:00
Jeong, YunWon
e0185aefb1 mark failing tests 2026-01-18 19:56:54 +09:00
CPython Devleopers
dfed341ec8 Upgrade unittest from 3.14.2 2026-01-18 19:56:54 +09:00
Lee Dogeon
5242ff5243 Bump json to 3.14.2 (#6774) 2026-01-18 19:16:48 +09:00
Jeong, YunWon
e8dd5826e6 init warnings before site (#6771) 2026-01-18 19:16:02 +09:00
Jeong, YunWon
f8a78e6ab6 stack soft limit (#6754) 2026-01-18 18:09:55 +09:00
Jeong, YunWon
941a7bb784 setup ndk on CI (#6773) 2026-01-18 18:02:57 +09:00
Jeong, YunWon
1ccd99ea20 use ast namespace (#6769) 2026-01-18 17:30:36 +09:00
Jeong, YunWon
eb50246b41 fix annotation (#6767) 2026-01-18 15:24:17 +09:00
Jeong, YunWon
7c7b824aa9 fix asyncgen finalizer (#6761) 2026-01-18 14:18:30 +09:00
fanninpm
e4ce70bbda Update Lib/warnings to CPython 3.14 (#6762)
* Update Lib/warnings to CPython 3.14

* Add context_aware_warnings setting

XXX- The default setting of this and thread_inherit_context should be
true.

* Unmark passing test

* Mark failing tests

* Skip failing test in test_sys for now

Updating test_sys to CPython 3.14 is beyond the scope of this PR.

* test_improper_option is fixed

---------

Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2026-01-18 13:59:49 +09:00
Jeong, YunWon
96e08a425e Initialize warnings module during interpreter startup to process (#6766) 2026-01-18 13:15:40 +09:00
Jeong, YunWon
6616961d08 skip flaky tests (#6764) 2026-01-18 11:16:25 +09:00
Jeong, YunWon
abea6bd114 traverse for generator (#6760) 2026-01-18 09:36:09 +09:00
Jeong, YunWon
ca76cb7bb0 thread_inherit_context, upgrade threading & contextvar (#6727)
* thread_inherit_context

* Update contextvar, threading from CPython 3.14.2

* partially patch test_sys to 3.14.2

---------

Co-authored-by: CPython Devleopers <>
2026-01-18 08:53:41 +09:00
Jeong, YunWon
11c9b0e783 fix finalizing and atexit timing (#6626)
* fix finalizing and atexit timing

* fix shutdown
2026-01-18 01:54:16 +09:00
Shahar Naveh
8ce5f49908 Update graphlib, heapq, ipaddress to 3.14.2 (#6758) 2026-01-18 01:23:34 +09:00
Jeong, YunWon
6df3753229 Py 3.14 changes fix (#6755)
* marshal v5

* conditional blclk

* fix jit don't use lossy string

* add varname

* symboltable takes responsibility of __debug__
2026-01-18 01:02:40 +09:00
Shahar Naveh
892116c009 Add some compiler tests from 3.13.11 (#6752)
* Add some compiler tests from 3.13.11

* Unmark passing test
2026-01-18 00:26:06 +09:00
Shahar Naveh
6dd5e36d02 Remove misleading placeholder comments (#6757) 2026-01-17 22:53:00 +09:00
Lee Dogeon
3d86b26caf Complement upgrade-pylib Claude Code command (#6742)
* Insert unittest import line if it doesn't exist

* Complement upgrade-pylib Claude Code command

* Ensure test module having import statement at least one
2026-01-17 21:54:16 +09:00
Jeong, YunWon
4eb49491a3 Change dockerfile to be available on aarch64 (#6751) 2026-01-17 21:53:13 +09:00
Jeong, YunWon
c490a357fd relocate scripts (#6753) 2026-01-17 21:52:53 +09:00
Jeong, YunWon
273c5a349b Merge pull request #6718 from youknowone/py-3.14
RustPython version to 3.14
2026-01-17 20:58:23 +09:00
github-actions[bot]
60fb4384e1 Auto-format: ruff check --select I --fix 2026-01-17 10:34:53 +00:00
Jeong, YunWon
faeed2cdcc clean up 2026-01-17 19:22:18 +09:00
Jeong, YunWon
133bdf655e auto_mark_test uses regex to check Run tests? sequentially 2026-01-17 19:22:18 +09:00
CPython Devleopers
314a61562c Update calendar from CPython 3.14.2 2026-01-17 19:22:00 +09:00
CPython Devleopers
d75f272c8b Update argparse from CPython 3.14.2 2026-01-17 19:22:00 +09:00
CPython Devleopers
ef22bf4774 Update _colorize from CPython 3.14.2 2026-01-17 19:22:00 +09:00
CPython Devleopers
65e08c02b6 Update ensurepip from 3.14.2 2026-01-17 19:22:00 +09:00
Jeong, YunWon
33689c1d0f Fix jit failure 2026-01-17 19:22:00 +09:00
Jeong, YunWon
522793850a mark and unmark unittest functions 2026-01-17 19:21:38 +09:00
CPython Devleopers
076d692b42 Upgrade string from CPython 3.14.2 2026-01-17 19:21:11 +09:00
Jeong, YunWon
346481d95e partially patch Lib/typing to 3.14 2026-01-17 19:21:11 +09:00
Jeong, YunWon
96038e48c5 partially patch inspect for PEP 649 in 3.13 2026-01-17 19:21:11 +09:00
Jeong, YunWon
37cc6b44f9 fix whats_left to support __annotate__ 2026-01-17 19:21:11 +09:00
Jeong, YunWon
dc93614f5a Add annotationlib,ann_module from 3.14.2
also partially update test_module
2026-01-17 19:21:11 +09:00
Jeong, YunWon
566b6f438b PEP 649 annotation phase 4 2026-01-17 19:21:11 +09:00
Jeong, YunWon
a78b569d92 PEP 649 annotation phase 3 2026-01-17 19:21:11 +09:00
Jeong, YunWon
353a9f6104 PEP 649 annotation phase 2 2026-01-17 19:21:11 +09:00
Jeong, YunWon
51b628642c PEP 649 annotation phase 1 2026-01-17 19:21:11 +09:00
Jeong, YunWon
fdfede7545 fix win clippy 2026-01-17 19:21:11 +09:00
Jeong, YunWon
0793bd3d08 co_consts 2026-01-17 19:21:11 +09:00
Jeong, YunWon
f5b44f505a Fix Exception.__init__ 2026-01-17 19:21:11 +09:00
Jeong, YunWon
24bff8d5bf fix unsigned validation 2026-01-17 19:21:11 +09:00
Jeong, YunWon
eafa0c0149 Fix int rounding 2026-01-17 19:21:11 +09:00
Jeong, YunWon
db01a1d653 Remove pickle from itertools 2026-01-17 19:21:11 +09:00
Jeong, YunWon
2fe140fdb9 Fix bytes/bytearray fromhex 2026-01-17 19:21:11 +09:00
Jeong, YunWon
8d901a7300 Implement bool(NotImplemented) 2026-01-17 19:21:10 +09:00
Jeong, YunWon
280caea579 upgrade venvlauncher 2026-01-17 19:21:10 +09:00
Jeong, YunWon
7594ef5121 upgrade site to 3.14.2 2026-01-17 19:21:10 +09:00
Jeong, YunWon
ea1e60e9ca mark version to 3.14 2026-01-17 19:21:10 +09:00
Jeong, YunWon
299f1ea0aa skip flaky test_concurrent_futures.test_process_pool.test_ressources_gced_in_workers (#6750) 2026-01-17 19:18:54 +09:00
Jeong, YunWon
f4363a6b27 PEP 750 tstring (#6744) 2026-01-17 18:41:40 +09:00
Jeong, YunWon
02cc257b42 Support pickle better with __getnewargs_ex__ (#6749)
* Implement pickle more

* add weakref check

* check exports in __setstate__

* remove reducelib
2026-01-17 18:37:16 +09:00
Terry Tianlin Luan
833f7343c8 Update email library v3.13.11 (#6642)
* Updated the email library + added test suite

* Added Windows altzone + Fixed memory error in `bytes_inner.rs`
2026-01-17 18:15:35 +09:00
Jeong, YunWon
c934265304 rename fix_test and support removing unexpected success (#6748)
* fix_test to remove unexpected success

* rename fix_test

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-17 00:11:20 +09:00
Jeong, YunWon
e9a57d172b correct builtins type under function (#6745)
* correct buitins type

* Auto-format: ruff format

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-17 00:00:16 +09:00
Shahar Naveh
380fa39eba Bytecode instrumented placeholder (#6741)
* Add all other bytecodes

* Mark passing/failing tests
2026-01-16 23:08:06 +09:00
Terry Tianlin Luan
4cb3b9d8f0 Updated the urllib + http libraries + test libraries (#6672)
* Updated urllib + urllib tests

* Updated http + test

* Annotated failing tests

* Fixed issues in httpservers, robotparser and urllib2net

* Fixed windows only success

* Annotated flaky test in test_logging
2026-01-16 23:03:49 +09:00
Lee Dogeon
ef871d227e Update json module to 3.13.11 (#6743) 2026-01-16 21:38:15 +09:00
Jeong, YunWon
746e71af87 reject SemLock reduce (#6738) 2026-01-16 10:18:31 +09:00
Jeong, YunWon
a0ace054f3 Fix asyncio compile (#6739) 2026-01-16 09:12:16 +09:00
Terry Tianlin Luan
609dbb1439 Downgraded skips in tests (#6716)
* Downgraded skips in tests

* Fixed failing tests

* Fixed test_ftplib + test_socket + test_ssl + test_threaded_import failures

* Removed comments about which tests are run in which environment

* Addressed PR comments

* Annotated skips on failing tests

* Removed unneeded tests

* Removed unneeded sys import from test_ftplib

* Added annotation to test_ftplib

* Readded skipIf to test_cleanup_with_symlink_modes with a more general ENV_POLLUTING_TESTS_WINDOWS

* Addressed PR comments

* Made changes to minimize diff in PR

* Apply suggestion from @youknowone

---------

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2026-01-15 21:43:22 +09:00
Lee Dogeon
3a702ac772 Improve json.loads performance (#6704)
* Parse JSON in Rust

* Reuse key when decoding JSON

* Unmark resolved test

* Parse null/true/false directly in call_scan_once

Parse JSON constants (null, true, false) directly in Rust within
call_scan_once() instead of falling back to Python scan_once.
This reduces Python-Rust boundary crossings for array/object values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Parse numbers directly in call_scan_once

Parse JSON numbers starting with digits (0-9) directly in Rust within
call_scan_once() by reusing the existing parse_number() method.
This reduces Python-Rust boundary crossings for array/object values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Parse NaN/Infinity/-Infinity in call_scan_once

Parse special JSON constants (NaN, Infinity, -Infinity) and negative
numbers directly in Rust within call_scan_once(). This handles:
- 'N' -> NaN via parse_constant callback
- 'I' -> Infinity via parse_constant callback
- '-' -> -Infinity or negative numbers via parse_constant/parse_number

This reduces Python-Rust boundary crossings for array/object values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Correct wrong index access

* Leave more flame span

* Refactor json scanstring with byte index

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 19:53:46 +09:00
Terry Tianlin Luan
c5deb740ac Update asyncio library (#6601)
* Updated asyncio to v3.13.11

* Removed expectedFailure from `test_async_case.py`
2026-01-15 17:35:01 +09:00
dependabot[bot]
a3d1b5e7ac Bump xml from 1.2.0 to 1.2.1 (#6730)
Bumps [xml](https://github.com/kornelski/xml-rs) from 1.2.0 to 1.2.1.
- [Changelog](https://github.com/kornelski/xml-rs/blob/main/Changelog.md)
- [Commits](https://github.com/kornelski/xml-rs/compare/1.2.0...1.2.1)

---
updated-dependencies:
- dependency-name: xml
  dependency-version: 1.2.1
  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-01-15 17:21:22 +09:00
dependabot[bot]
2a2caa1897 Bump rand_core from 0.9.4 to 0.9.5 (#6731)
Bumps [rand_core](https://github.com/rust-random/rand) from 0.9.4 to 0.9.5.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/commits)

---
updated-dependencies:
- dependency-name: rand_core
  dependency-version: 0.9.5
  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-01-15 17:20:34 +09:00
Jeong, YunWon
c06701abc8 skip test_multiprocessing_{fork,spawn}.test_threads.py (#6735) 2026-01-15 14:29:41 +09:00
Jeong, YunWon
b214362ca1 remove polluting tests (#6733) 2026-01-15 08:36:56 +09:00
Shahar Naveh
e3890f9b4a Bytecode pseudo opcodes (#6715) 2026-01-15 02:52:25 +09:00
Jeong, YunWon
13a875f609 fix backslash handling in fix_test.py (#6729) 2026-01-15 00:01:14 +09:00
Jeong, YunWon
7a516c44c9 Update AI docs (#6728)
* update copilot instructions

* upgrade-pylib command
2026-01-14 23:51:41 +09:00
Jeong, YunWon
3d4aaaa6c1 thread.setname and context (#6726) 2026-01-14 17:20:52 +09:00
Jeong, YunWon
81d89d1d90 Fix flaky multiprocessing tests (#6725) 2026-01-14 16:59:10 +09:00
Lee Dogeon
2e5257d098 Introduce benchmark for json.loads (#6723) 2026-01-14 14:44:34 +09:00
Lee Dogeon
6cb763a332 Fix some parts of cron-ci workflow (#6724)
* Use shared PYTHON_VERSION in cron-ci benchmark job

* Correct extra-tests/jsontests.py script

When the `extra_tests/jsontests.py` script was added, it was presumably during
the time when Python versions 3.5 to 3.8 were in use. At that time,
`test.libregrtest.runtest` was a valid path, but from CPython version 3.11
onwards, the path changed to `test.libregrtest.findtests`.

* Use ssl-rustls feature instead of ssl in cron-ci workflow

Replace the ssl feature with ssl-rustls in both the CARGO_ARGS
environment variable and the cargo-llvm-cov test command to fix
the cron-ci workflow.

Since 1a783fc9ec, it is disallowed
to use ssl manually.

* Replace inspect.getargspec with inspect.getfullargspec in custom_text_test_runner

inspect.getargspec() was removed in Python 3.11, causing jsontests.py to
fail with "TypeError: 'NoneType' object is not iterable" when running on
RustPython (which targets Python 3.13).

The get_function_args() function was silently catching the AttributeError
and returning None, which then caused the error in store_class_fields()
when trying to iterate over None.

> https://docs.python.org/3/whatsnew/3.11.html
> The getargspec() function, deprecated since Python 3.0; use
> inspect.signature() or inspect.getfullargspec() instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Exclude rustpython-venvlauncher in cron-ci workflow

Since rustpython-venvlauncher is Windows-only,
it disables the project in the cron-ci workflow.

* Apply suggestion from @fanninpm

Co-authored-by: fanninpm <luxverdans@sbcglobal.net>

* Trigger cron-ci workflow in pull_request

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
Co-authored-by: fanninpm <luxverdans@sbcglobal.net>
2026-01-14 13:28:25 +09:00
Lee Dogeon
49e6931c7e Introduce flame_guard in rustpython-stdlib too (#6722) 2026-01-14 09:14:05 +09:00
Shahar Naveh
9eeea925c7 Move OpArg to its own file (#6703)
* Move OpArg to its own file

* Fix merge

* Fix more merge
2026-01-14 08:58:49 +09:00
Jeong, YunWon
bb57724d3b flush on WantWrite (#6717) 2026-01-13 22:05:49 +09:00
Jeong, YunWon
f9e2f9d273 Upgrade math,cmath to 3.14.2 and totally delegate to pymath (#6705)
* Upgrade test_math, test_cmath from 3.14.2

* pymath 0.1.5
2026-01-13 22:05:32 +09:00
Jeong, YunWon
e91bc11b0a Fix dict race condition (#6720) 2026-01-13 21:27:24 +09:00
Jeong, YunWon
4d7a289971 fix lib_update backslash support (#6719) 2026-01-13 19:02:34 +09:00
dependabot[bot]
7e47212886 Bump flate2 from 1.1.5 to 1.1.8 (#6711)
Bumps [flate2](https://github.com/rust-lang/flate2-rs) from 1.1.5 to 1.1.8.
- [Release notes](https://github.com/rust-lang/flate2-rs/releases)
- [Commits](https://github.com/rust-lang/flate2-rs/compare/1.1.5...1.1.8)

---
updated-dependencies:
- dependency-name: flate2
  dependency-version: 1.1.8
  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-01-13 17:19:38 +09:00
dependabot[bot]
10f14fdedd Bump rand_core from 0.9.3 to 0.9.4 (#6714)
Bumps [rand_core](https://github.com/rust-random/rand) from 0.9.3 to 0.9.4.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/commits)

---
updated-dependencies:
- dependency-name: rand_core
  dependency-version: 0.9.4
  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-01-13 10:43:06 +09:00
dependabot[bot]
1bb3439711 Bump winresource from 0.1.28 to 0.1.29 (#6713)
Bumps [winresource](https://github.com/BenjaminRi/winresource) from 0.1.28 to 0.1.29.
- [Commits](https://github.com/BenjaminRi/winresource/commits)

---
updated-dependencies:
- dependency-name: winresource
  dependency-version: 0.1.29
  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-01-13 10:42:55 +09:00
Jeong, YunWon
69601a18a4 Upgrade threading to 3.13.11; sys.setprofile & impl more threading (#6691)
* Upgrade threading,test_threading from 3.13.11

* increase ci limit to 45mins

* impl more threading

* no static for asyncgen
2026-01-12 15:33:28 +09:00
Shahar Naveh
00bfea83bf Update ruff to 0.14.11 in the CI (#6708)
* Update ruff to 0.14.11 in the CI

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-12 10:52:15 +09:00
Lee Dogeon
f3b7f7fd47 Clarify commit message from pr-auto-commit (#6706)
* Clarify commit message from pr-auto-commit

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-12 08:08:50 +09:00
Jeong, YunWon
29bb8b47cd Super instructions (#6694)
* super instructions

* Fix classcell

* ZeroArg
2026-01-11 17:59:32 +09:00
Jeong, YunWon
12f08d6ed5 Merge pull request #6698 from youknowone/quick-import
preparing step to integrate fix_test into lib_updater
2026-01-11 17:20:14 +09:00
Jeong YunWon
f2d5594288 fix_test --quick-import 2026-01-11 16:48:21 +09:00
Jeong, YunWon
1d95a04ac8 Update test/libregrtest to CPython 3.13.10 (#6410)
* Update test/libregrtest to CPython 3.13.10

* Re-add try block around platform and encodings info

* Disable WindowsLoadTracker on Windows

* Disable struct._clearcache()

* Disable saved_test_environment context manager

* Disable support.record_original_stdout()

* Update test/test_regrtest.py to CPython 3.13.11

* Add autotest shim from CPython 3.13.11

* Mark failing tests and skip flaky ones in test/test_regrtest.py

* Mark failing test in test_fstring

* Add _test_eintr.py from CPython 3.13.11

* Skip long-running test in _test_eintr

* Mark failing/erroring tests in _test_eintr

* Skip panicking tests in test_io

* Unmark passing tests in test_io

* Mark failing test in test_urllib2net

* Unmark passing test in test_fstring

* Time out if any one test takes longer than 10 minutes

* Skip more flaky tests in test_threading

* Skip tests that pollute the environment for some reason

* Skip flaky tests in test.test_multiprocessing_fork.test_misc

* Skip flaky tests in test.test_multiprocessing_fork.test_manager

Had to go with the nuclear option.

* Skip flaky tests in test_regrtest

* Mark flaky tests in test_ftplib

* Fix multiprocessing tests on Darwin

* Remove test_sys from list of environment polluters

* Remove test_ftplib from list of environment polluters

* Add test.test_multiprocessing_fork.test_misc to list of environment
polluters

* Check 10 times if a test is still polluting the environment

* Save more of the test environment before a test is run

* Mark test_import as polluting the environment

* Unmark passing test in test_regrtest

* Mark flaky test in test_regrtest

* Remove tests from list of environment polluters

* Skip flaky test in test_logging

* Remove tests from list of polluting tests

* Skip certain problematic tests in test_threading

* Skip test causing worker bug in test_builtin

* Make env polluter skip in test_threading os-agnostic

* Remove test_file_eintr from list of polluters

* Remove test_subprocess from the list of polluters

* Address coderabbit comments

* fix threading._dangling

* Skip environment polluter in test.test_concurrent_futures.test_thread_pool

* Make skip in test_thread_pool os-agnostic

* Skip problematic tests in test_wait

* Skip flaky tests in test_process_pool

* Remove test_import from list of polluters

* Amend skips in test.test_multiprocessing_forkserver.test_processes

* Remove test_raise from list of polluters

* Remove test_ssl from list of polluters

* Skip flaky test in test_ssl

* Fix YAML formatting issue

---------

Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2026-01-11 16:19:34 +09:00
Padraic Fanning
1dcded40b8 Fix YAML formatting issue 2026-01-10 22:52:29 -05:00
Padraic Fanning
7445b27083 Skip flaky test in test_ssl 2026-01-10 22:10:35 -05:00
Padraic Fanning
f9a1f2bda8 Remove test_ssl from list of polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
8d505dc0e5 Remove test_raise from list of polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
80af69e625 Amend skips in test.test_multiprocessing_forkserver.test_processes 2026-01-10 22:10:35 -05:00
Padraic Fanning
717265a59a Remove test_import from list of polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
daaae22077 Skip flaky tests in test_process_pool 2026-01-10 22:10:35 -05:00
Padraic Fanning
1642959b92 Skip problematic tests in test_wait 2026-01-10 22:10:35 -05:00
Padraic Fanning
1c07d3545e Make skip in test_thread_pool os-agnostic 2026-01-10 22:10:35 -05:00
Padraic Fanning
ae6deb7437 Skip environment polluter in test.test_concurrent_futures.test_thread_pool 2026-01-10 22:10:35 -05:00
Jeong, YunWon
def6974bf6 fix threading._dangling 2026-01-10 22:10:35 -05:00
Padraic Fanning
e6d2ba06b1 Address coderabbit comments 2026-01-10 22:10:35 -05:00
Padraic Fanning
a09ed1bf97 Remove test_subprocess from the list of polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
55c309669b Remove test_file_eintr from list of polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
d1e75c0ee0 Make env polluter skip in test_threading os-agnostic 2026-01-10 22:10:35 -05:00
Padraic Fanning
98d8ebcc1d Skip test causing worker bug in test_builtin 2026-01-10 22:10:35 -05:00
Padraic Fanning
b76c0a20a8 Skip certain problematic tests in test_threading 2026-01-10 22:10:35 -05:00
Padraic Fanning
d170b0281f Remove tests from list of polluting tests 2026-01-10 22:10:35 -05:00
Padraic Fanning
12811eb40c Skip flaky test in test_logging 2026-01-10 22:10:35 -05:00
Padraic Fanning
8765d86355 Remove tests from list of environment polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
40da80714c Mark flaky test in test_regrtest 2026-01-10 22:10:35 -05:00
Padraic Fanning
2769c08a0d Unmark passing test in test_regrtest 2026-01-10 22:10:35 -05:00
Padraic Fanning
0a826731cb Mark test_import as polluting the environment 2026-01-10 22:10:35 -05:00
Padraic Fanning
59b80e30ed Save more of the test environment before a test is run 2026-01-10 22:10:35 -05:00
Padraic Fanning
0d128ccc28 Check 10 times if a test is still polluting the environment 2026-01-10 22:10:35 -05:00
Padraic Fanning
3427215611 Add test.test_multiprocessing_fork.test_misc to list of environment
polluters
2026-01-10 22:10:35 -05:00
Padraic Fanning
99168e076c Remove test_ftplib from list of environment polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
9500f6609b Remove test_sys from list of environment polluters 2026-01-10 22:10:35 -05:00
Padraic Fanning
55c71aa0ee Fix multiprocessing tests on Darwin 2026-01-10 22:10:35 -05:00
Padraic Fanning
dc9475c26b Mark flaky tests in test_ftplib 2026-01-10 22:10:35 -05:00
Padraic Fanning
34fe0f2770 Skip flaky tests in test_regrtest 2026-01-10 22:10:35 -05:00
Padraic Fanning
3aa6bf8696 Skip flaky tests in test.test_multiprocessing_fork.test_manager
Had to go with the nuclear option.
2026-01-10 22:10:35 -05:00
Padraic Fanning
e831124a21 Skip flaky tests in test.test_multiprocessing_fork.test_misc 2026-01-10 22:10:35 -05:00
Padraic Fanning
1827af100b Skip tests that pollute the environment for some reason 2026-01-10 22:10:35 -05:00
Padraic Fanning
17cc559dd3 Skip more flaky tests in test_threading 2026-01-10 22:10:35 -05:00
Padraic Fanning
472c12cad7 Time out if any one test takes longer than 10 minutes 2026-01-10 22:10:35 -05:00
Padraic Fanning
8d2b215ccb Unmark passing test in test_fstring 2026-01-10 22:10:35 -05:00
Padraic Fanning
8aafbf2bf7 Mark failing test in test_urllib2net 2026-01-10 22:10:35 -05:00
Padraic Fanning
cadac344a7 Unmark passing tests in test_io 2026-01-10 22:10:35 -05:00
Padraic Fanning
c76e72571e Skip panicking tests in test_io 2026-01-10 22:10:35 -05:00
Padraic Fanning
1f56a0fe0c Mark failing/erroring tests in _test_eintr 2026-01-10 22:10:35 -05:00
Padraic Fanning
bedcb225e7 Skip long-running test in _test_eintr 2026-01-10 22:10:35 -05:00
Padraic Fanning
4a559f3ea4 Add _test_eintr.py from CPython 3.13.11 2026-01-10 22:10:35 -05:00
Padraic Fanning
2789ff7a9f Mark failing test in test_fstring 2026-01-10 22:10:35 -05:00
Padraic Fanning
4c833fc714 Mark failing tests and skip flaky ones in test/test_regrtest.py 2026-01-10 22:10:35 -05:00
Padraic Fanning
404836f187 Add autotest shim from CPython 3.13.11 2026-01-10 22:10:35 -05:00
Padraic Fanning
32fd6bec49 Update test/test_regrtest.py to CPython 3.13.11 2026-01-10 22:10:35 -05:00
Padraic Fanning
e4fb263788 Disable support.record_original_stdout() 2026-01-10 22:10:35 -05:00
Padraic Fanning
a8e7633f58 Disable saved_test_environment context manager 2026-01-10 22:10:35 -05:00
Padraic Fanning
f436c51ef2 Disable struct._clearcache() 2026-01-10 22:10:35 -05:00
Padraic Fanning
08762a35c2 Disable WindowsLoadTracker on Windows 2026-01-10 22:10:35 -05:00
Padraic Fanning
5d253d1ec0 Re-add try block around platform and encodings info 2026-01-10 22:10:35 -05:00
Padraic Fanning
f297888317 Update test/libregrtest to CPython 3.13.10 2026-01-10 22:10:35 -05:00
Shahar Naveh
440b8de763 Move Instruction enum to its own file (#6693)
* Move `Instruction` to its own file

* Fix codegen and frame.rs

* Adjust script
2026-01-11 10:04:24 +09:00
Jeong YunWon
2a1faf4265 integrate to lib_updater 2026-01-11 00:47:06 +09:00
Jeong, YunWon
3909b18eac lib_updater --quick-upgrade (#6695) 2026-01-11 00:11:41 +09:00
Shahar Naveh
6ff7b3ed27 LoadClassDeref -> LoadFromDictOrDeref (#6692) 2026-01-10 23:16:11 +09:00
Shahar Naveh
a400386774 Remove _dis module (#6690)
* Remove `_dis` module

* Adjust CodeFlag
2026-01-10 22:11:53 +09:00
Jeong, YunWon
9e225f52e2 impl gc finialized (#6689)
* impl gc finialized

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-10 19:50:25 +09:00
Jeong, YunWon
aa9fc7fede Pseudo ops (#6678)
* better expect

* pseudo instruction

* Fix CallKw narg

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-10 19:49:58 +09:00
Jeong, YunWon
2d3ef86231 fix SSL callback (#6688)
* fix SSL callback

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-10 19:49:40 +09:00
Shahar Naveh
6fe05987f0 Remove Reverse bytecode (#6675)
* Remove `Reverse` bytecode

* Update crates/compiler-core/src/bytecode.rs

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>

* Gen

* Remove Reverse

* Auto-format: cargo fmt --all

* Revert comment

* Remove debug code

* Fix CI

---------

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-10 18:55:15 +09:00
Shahar Naveh
9291178b01 Ci gen opcode metadata (#6677)
* Autogen opcode metadata

* Manually modify opcode metadata
2026-01-10 17:33:11 +09:00
Terry Tianlin Luan
8c157990cf Updated test_contextlib_async.py (#6686) 2026-01-10 16:38:21 +09:00
Jeong, YunWon
e835ce9275 Make ready_to_import always remove tempdir from sys.path (#6687) 2026-01-10 16:32:26 +09:00
Lee Dogeon
0cd6eb7b13 Write profile even if failed (#6685) 2026-01-10 15:26:49 +09:00
dependabot[bot]
53feed5920 Bump libc from 0.2.179 to 0.2.180 (#6683)
* Bump libc from 0.2.179 to 0.2.180

Bumps [libc](https://github.com/rust-lang/libc) from 0.2.179 to 0.2.180.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.180/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.179...0.2.180)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.180
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix musl compatibility in libc 0.2.180 bump (#6684)

* Bump libc from 0.2.179 to 0.2.180

Bumps [libc](https://github.com/rust-lang/libc) from 0.2.179 to 0.2.180.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.180/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.179...0.2.180)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.180
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Initial plan

* Revert os.rs changes by removing newly added files

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Fix musl compatibility by removing target_env musl from cfg

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-10 15:10:18 +09:00
dependabot[bot]
95f5a5b240 Bump cranelift from 0.127.0 to 0.127.1 (#6680)
Bumps [cranelift](https://github.com/bytecodealliance/wasmtime) from 0.127.0 to 0.127.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
  dependency-version: 0.127.1
  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-01-10 09:33:40 +09:00
dependabot[bot]
91f0d70c0c Bump syn from 2.0.113 to 2.0.114 (#6679)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.113 to 2.0.114.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.113...2.0.114)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.114
  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-01-10 08:30:42 +09:00
dependabot[bot]
f2224de8d7 Bump cranelift-jit from 0.127.0 to 0.127.1 (#6682)
Bumps [cranelift-jit](https://github.com/bytecodealliance/wasmtime) from 0.127.0 to 0.127.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.127.1
  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-01-10 08:26:41 +09:00
dependabot[bot]
d4be424bf6 Bump indexmap from 2.12.1 to 2.13.0 (#6681)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.12.1 to 2.13.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.12.1...2.13.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.13.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-01-10 08:26:12 +09:00
Jeong, YunWon
de0bc732c9 Replace CallMethod/LoadMethod opcodes with plain Call (#6674)
* implementing call the correct way

stack is [Option<PyObjectRef>]

* fix again

* list custom instructions

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-10 08:25:56 +09:00
Shahar Naveh
c2bfdf30bd Assign opcode ids (#6637) 2026-01-09 22:52:47 +09:00
HueCodes
83a0deac25 Fix set in-place operators with self argument (#6661)
Co-authored-by: Hugh <HueCodes@users.noreply.github.com>
2026-01-09 21:31:21 +09:00
Terry Tianlin Luan
b38cdaa30e Update the concurrent library + Added tests (#6673)
* Updated concurrent library

* Added test_concurrent_futures from v3.13.11

* Annotated failing tests in test_concurrent_futures
2026-01-09 15:28:12 +09:00
Copilot
a3425b435e Track symbol table cursors to avoid exhaustion (#6670)
* Handle missing symbol table without panic

* Update symbol table error message

* Track symbol table cursors to avoid exhaustion

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-09 06:32:12 +09:00
Noa
bf80d2715d Update to windows-2025 runner on ci (#5571)
* Update to windows-2025 on ci

* Unmark unexpected successes

* Try adding .dll
2026-01-07 12:53:44 +09:00
Noa
09bde28281 Move typeid into vtable (#6231)
* Move typeid into vtable

* Bump rust-version
2026-01-07 12:33:29 +09:00
dependabot[bot]
42cc59a90c Bump openssl-probe from 0.1.6 to 0.2.0 (#6663)
* Bump openssl-probe from 0.1.6 to 0.2.0

Bumps [openssl-probe](https://github.com/alexcrichton/openssl-probe) from 0.1.6 to 0.2.0.
- [Release notes](https://github.com/alexcrichton/openssl-probe/releases)
- [Commits](https://github.com/alexcrichton/openssl-probe/compare/0.1.6...0.2.0)

---
updated-dependencies:
- dependency-name: openssl-probe
  dependency-version: 0.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix openssl-probe 0.2.0 API compatibility (#6669)

* Bump openssl-probe from 0.1.6 to 0.2.0

Bumps [openssl-probe](https://github.com/alexcrichton/openssl-probe) from 0.1.6 to 0.2.0.
- [Release notes](https://github.com/alexcrichton/openssl-probe/releases)
- [Commits](https://github.com/alexcrichton/openssl-probe/compare/0.1.6...0.2.0)

---
updated-dependencies:
- dependency-name: openssl-probe
  dependency-version: 0.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Initial plan

* Fix openssl-probe 0.2.0 API compatibility

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-07 10:13:13 +09:00
Lee Dogeon
6408228b3e Update DEVELOPMENT.md (#6671)
* Update DEVELOPMENT.md to reflect crates/ directory structure

* Update DEVELOPMENT.md to reflect Ruff parser-related changes
2026-01-07 10:09:51 +09:00
dependabot[bot]
4fbf617c06 Bump quote from 1.0.42 to 1.0.43 (#6667)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.42 to 1.0.43.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.42...1.0.43)

---
updated-dependencies:
- dependency-name: quote
  dependency-version: 1.0.43
  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-01-07 08:54:21 +09:00
dependabot[bot]
985961b013 Bump rustls from 0.23.35 to 0.23.36 (#6666)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.35 to 0.23.36.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.35...v/0.23.36)

---
updated-dependencies:
- dependency-name: rustls
  dependency-version: 0.23.36
  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-01-07 08:54:10 +09:00
dependabot[bot]
8df2df3ed7 Bump webpki-roots from 1.0.4 to 1.0.5 (#6665)
Bumps [webpki-roots](https://github.com/rustls/webpki-roots) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/rustls/webpki-roots/releases)
- [Commits](https://github.com/rustls/webpki-roots/compare/v/1.0.4...v/1.0.5)

---
updated-dependencies:
- dependency-name: webpki-roots
  dependency-version: 1.0.5
  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-01-07 08:52:50 +09:00
dependabot[bot]
7becac9a10 Bump proc-macro2 from 1.0.104 to 1.0.105 (#6664)
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.104 to 1.0.105.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.104...1.0.105)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-version: 1.0.105
  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-01-07 08:52:40 +09:00
Terry Tianlin Luan
cd613edc71 Update the zipfile + zipimport libraries + tests - v3.13.11 (#6639)
* Updated zipimport library + test

* Updated zipfile library + test

* Annotated failing/erroring tests in test_zipfile and test_zipimport

* Changed all skips in `test_core.py` to expectedFailures

* skip EncodedMetadataTests

---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2026-01-06 21:35:21 +09:00
Copilot
e367145a4a Add Linux pidfd syscalls to expose asyncio pidfd watcher (#6660)
* Add pidfd support syscalls

* Validate pidfd signal range

* Clarify pidfd syscall return handling

* Skip flaky is_alive_after_fork test

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-01-06 15:10:23 +09:00
Copilot
03380dc4ec Ensure __new__ entries in class __dict__ are staticmethods (#6659)
* Initial plan

* Wrap __new__ methods as staticmethods in type dicts

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Unmark passing enum test

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-06 12:16:51 +09:00
dependabot[bot]
906aebf5a1 Bump malachite-q from 0.8.0 to 0.9.0 (#6653)
* Bump malachite-q from 0.8.0 to 0.9.0

Bumps [malachite-q](https://github.com/mhogrefe/malachite) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/mhogrefe/malachite/releases)
- [Commits](https://github.com/mhogrefe/malachite/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: malachite-q
  dependency-version: 0.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump all malachite-* crates from 0.8.0 to 0.9.0 (#6656)

* Initial plan

* Update all malachite-* crates to 0.9.0

* Fix ctypes overflow check for malachite 0.9.0 compatibility

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-06 12:15:35 +09:00
dependabot[bot]
cf963b74c8 Bump libc from 0.2.178 to 0.2.179 (#6654)
* Bump libc from 0.2.178 to 0.2.179

Bumps [libc](https://github.com/rust-lang/libc) from 0.2.178 to 0.2.179.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.178...0.2.179)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.179
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix musl and wasi builds after libc 0.2.179 upgrade (#6655)

* Initial plan

* Fix musl and wasi build failures after libc 0.2.179 upgrade

- Remove wasi-specific implementation of get_process_time that used CLOCK_PROCESS_CPUTIME_ID
- Exclude wasi from CLOCK_PROCESS_CPUTIME_ID constant export
- Add musl and wasi to use st_atim/st_mtim/st_ctim timespec fields for stat structure

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2026-01-06 10:07:51 +09:00
Shahar Naveh
ead7e0c39c Make use of Unary opcodes (#6647)
* Make use of `Unary` opcodes

* Add `Reserved140` opcode

* re-add support for UnaryPositive in JIT

* JIT support for `ToBool` instruction
2026-01-05 23:40:41 +09:00
dependabot[bot]
85bafc057a Bump syn from 2.0.112 to 2.0.113 (#6652)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.112 to 2.0.113.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.112...2.0.113)

---
updated-dependencies:
- dependency-name: syn
  dependency-version: 2.0.113
  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-01-05 22:53:46 +09:00
dependabot[bot]
ab885d37de Bump libffi from 5.0.0 to 5.1.0 (#6651)
Bumps [libffi](https://github.com/libffi-rs/libffi-rs) from 5.0.0 to 5.1.0.
- [Commits](https://github.com/libffi-rs/libffi-rs/compare/libffi-v5.0.0...libffi-v5.1.0)

---
updated-dependencies:
- dependency-name: libffi
  dependency-version: 5.1.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-01-05 22:53:15 +09:00
dependabot[bot]
dd65baf617 Bump insta from 1.45.1 to 1.46.0 (#6650)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.45.1 to 1.46.0.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.45.1...1.46.0)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.46.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-01-05 22:53:03 +09:00
Shahar Naveh
4af869b121 Fix opcode args (#6649) 2026-01-05 22:24:56 +09:00
Terry Tianlin Luan
75838e567e Update pty + tty + associated libraries - v3.13.10 (#6630)
* Update tty + added the test from v3.13.10

* Updated pty.py + test
2026-01-05 14:38:04 +09:00
Terry Tianlin Luan
8977c39b60 Updated the re library + test (#6648)
* Updated re library + test

* Copied over generate_sre_constants from cpython/Tools

* Customized `generate_sre_constants.py` + ran to update `constants.rs`

* Clarified `dump_enum` docstring in `generate_sre_constants.py`
2026-01-05 13:55:07 +09:00
Jeong, YunWon
bdf3b3654e fix syntaxerror ast end_offset (#6646) 2026-01-05 08:01:41 +09:00
Gyubong Lee
ce5e1bd455 Update threading lib and tests from CPython 3.12.0a0 (#4156)
* Update threading.py from CPython v3.12.0a0

* Update test_threading.py from CPython v3.12.0a0

* mark failing tests

---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2026-01-05 08:01:00 +09:00
Jeong, YunWon
6cc103bad3 Update README with venv and PIP instructions (#6127) 2026-01-05 01:35:56 +09:00
Jeong, YunWon
e1b22f19e6 vm.run_pyc_bytes (#6645)
* rustpython_vm::import::check_pyc_magic_number_bytes

* vm.new_scope_with_main

* PyCode::from_pyc

* vm.run_pyc_bytes

* add boundary check
2026-01-04 22:58:16 +09:00
Terry Tianlin Luan
ec564ac190 Updated tempfile libraries + associated tests - V3.13.11 (#6632)
* Updated subprocess library test

* Updated tempfile library + test

* Skipped the test_cleanup_with_symlink_modes test
2026-01-04 21:15:42 +09:00
Jeong, YunWon
b47337780b remove ssl from debugger configs (#6644) 2026-01-04 21:10:28 +09:00
Jeong, YunWon
bdd036ea6b pythonrun (#6643) 2026-01-04 20:52:35 +09:00
Jeong, YunWon
fd27c2d78f Fix SSL handshake partial send (#6635)
* simplify test_ftplib

* mark flaky test

* better ssl handshake, pending tls output

* flush_all_pending

* deadline

* flush pending
2026-01-04 17:53:59 +09:00
Jeong, YunWon
8bfe927de5 skip TestTLS_FTPClass (#6640) 2026-01-04 11:07:58 +09:00
Terry Tianlin Luan
68d65a7280 Updated token + weakref libraries + weakref test - v3.13.11 (#6633)
* Updated the token library

* Updated the weakref library

* Annotated the failing weakref test
2026-01-04 10:17:12 +09:00
Terry Tianlin Luan
f929a6eef8 Updated the platform + plistlib libraries + associated tests v.3.13.10 (#6611)
* Updated plistlib + test library

* Updated the platform library + test

* Re-added Rustpython platform version
2026-01-04 10:02:24 +09:00
Terry Tianlin Luan
4bec6bb6f9 Updated the pkgutil + unittest libraries + the pkgutil, unittest and import tests - v.3.13.11 (#6610)
* Updated unittest + tests

* Updated pkgutil + the associated test

* Updated test_import

* Fixed import + unittest test errors

* Updated test_dll_dependency_import with expectedFailure
2026-01-04 09:54:01 +09:00
Jeong, YunWon
eee360822c Rework compiler about exception handling (#6638)
* asyncgen

* fix coroutine

* rewrite compiler

* tests

* set pyc magic number
2026-01-04 09:15:38 +09:00
Terry Tianlin Luan
f0526b9e9a Updated the wsgiref + xmlrpc libraries + associated tests - v3.13.11 (#6634)
* Updated the wsgiref library + test

* Uncommented fixed wsgi tests

* Updated the xmlrpc library + test

* Added expectedFailure to test_request_length in test_wsgiref

* Annotated doc_xmlrpc test failures/errors
2026-01-03 16:29:10 +09:00
Terry Tianlin Luan
eea6cc49cb Update the signal + socket libraries + associated tests - v3.13.11 (#6631)
* Updated signal test library

* Update socketserve + test library

* Fixed test issues with mac

* Added sys import into ftplib

* Reverted ftplib test changes
2026-01-03 16:27:25 +09:00
Jeong, YunWon
cff37af5f6 Fix locale.getencoding, overlapped.getresult, chdir for windows (#6629)
* locale getencoding

* overlapped.getresult

* Fix windows chdir

* mark flaky
2026-01-03 11:43:29 +09:00
Terry Tianlin Luan
5ae75a679b Update the fractions and ftplib libraries + associated tests - v.3.13.10 (#6607)
* Updated ftplib + test library

* Updated fractions + test library
2026-01-03 08:28:25 +09:00
Terry Tianlin Luan
52dd8292c4 Updated enum + file_cmp library + tests - v3.13.10 (#6606)
* Updated enum library

* Updated filecmp + file_cmp test

* Removed unneeded @unittest.expectedFailures

* Updated unexpected success in enum test

* Fixed unexpected success in test_filecmp
2026-01-03 08:27:18 +09:00
Jeong, YunWon
0eddee500a fix fcntl, sslsocket traverse, super , excepthook (#6623)
* fix fcntl

* fix traverse

* fix super

* excepthook

* fix warn
2026-01-03 00:15:31 +09:00
Jeong, YunWon
546d35b8c1 impl multiprocessing SemLock (#6542) 2026-01-03 00:14:43 +09:00
Jeong, YunWon
346519bb6b Warn user not to override special magic methods (#6625) 2026-01-02 17:49:55 +09:00
Jeong, YunWon
32184103c3 ruff_source_file (#6624) 2026-01-02 13:14:39 +09:00
Jeong, YunWon
2e2924801c __{get,set,del}item__ without pymethods (#6622)
* __getitem__ without pymethod

* __setitem__ __delitem__ without pymethod

* sort slot names
2026-01-02 08:29:42 +09:00
Jeong, YunWon
1f8ef0aa36 sequence, mapping slots and fix separate __delitem__ slots (#6621)
* fix call

* Remove repr pymethod

* Remove __get__ and __set__ pymethod

* Remove __contains__ pymethod

* Remove __len__ pymethod

* mapping priors sequence

* fix ctypes
2026-01-02 00:35:59 +09:00
Jeong, YunWon
b19312b403 slot for numeric ops (#6619)
* fix sequence_repeat wrappers

* slot for numeric ops

* fix bpo-37619
2026-01-01 22:39:16 +09:00
Jeong, YunWon
d3e2fa47cc Remove wrong Deref (#6620) 2026-01-01 18:50:34 +09:00
Shahar Naveh
3e2ada0586 Update test_sys.py from 3.13.11 (#6428)
* mark RustPython as  free-threaded

* Py_GIL_DISABLED

* Update `test_sys.py` from 3.13.11

* mark tests

---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2026-01-01 18:35:55 +09:00
Shahar Naveh
b446dac359 Update shelve.py from 3.13.11 (#6483)
* Update `test_shelve.py` from 3.13.11

* Update `shelve.py` from 3.13.11
2026-01-01 17:15:00 +09:00
Jeong, YunWon
a445a22868 fix compiler (#6615) 2026-01-01 16:55:39 +09:00
Jeong, YunWon
020ff3058c update_sub_slot, __delitem__, no __bool__, missing rops (#6618)
* fix update_sub_slot

* Fix __setitem__ __delitem__ handling

* contains

* no __bool__ anymore

* Add rops to wrapper

* Remove pymethod from set

* remove len
2026-01-01 15:00:30 +09:00
Jeong, YunWon
e10dc94992 Fix stdio_encoding, stdio_errors (#6617)
* stdio_errors, stdio_encodings

* add ascii

* unmark tests
2026-01-01 14:48:27 +09:00
Jeong, YunWon
e58cf84c71 Update dbm from 3.13.11, implement sqlite3 autocommit (#6616) 2026-01-01 14:44:53 +09:00
Terry Tianlin Luan
a4df238b3d Updated the importlib + nturl2path + pathlib libraries + associated tests - v3.13.10 (#6609)
* Updated the nturl2path library

* Updated pathlib library + test

* Updated urllib test

* Added expectedFailure to failing tests

* Updated importlib library
2026-01-01 08:58:59 +09:00
Jeong, YunWon
1e706d911e Interpreter (#6614)
* Refine documentation in interpreter.rs

Improve InterpreterConfig with convenience methods (with_debug, add_path, add_paths), better documentation with working examples, refactored stdlib setup into focused functions, and comprehensive unit tests while maintaining 100% backward compatibility.

* Improve error message for non-Unicode paths

* Use consistent make_module naming in all doc examples

* cut useful changes

---------

Co-authored-by: Mohamed Gharbi <galaxysea777@gmail.com>
2025-12-31 22:14:28 +09:00
Shahar Naveh
6ccf3b5104 Update ruff to 0.14.10 (#6271)
* Update `ruff` to `0.14.10`

* Mark failing test
2025-12-31 21:13:29 +09:00
Jeong, YunWon
7c8df94f4e Fix __origname__ of frozen modules (#6613) 2025-12-31 21:12:21 +09:00
Jeong, YunWon
a8cfa36c8a Remove duplicated richcompare pymethods and add missing rops' wrapper, __getattr__, proper __bool__ impl (#6579)
* slots: remove duplicate pymethods from object

* Add missing __rmul__

* migrate __bool__

* subclass_update, __getattr__
2025-12-31 20:26:38 +09:00
Jeong, YunWon
d3afd465f3 fix concurrent socket close (#6612) 2025-12-31 17:52:24 +09:00
Jeong, YunWon
b3b97cac7c fix symtable for named_expressions (#6604)
* check duplicated type parameter

* typealias

* fix self.in_comp_inner_loop_target
2025-12-31 15:02:08 +09:00
Jeong, YunWon
01a5f94e7b Fix error / warning messages (#6594)
* fix functools.partial repr

* warn_deprecated_throw_signature

* fix function error messages
2025-12-31 15:01:55 +09:00
Terry Tianlin Luan
bd0aaf6f4f Updated the hmac + mailbox libraries + associated tests (#6608)
* Updated hmac + test library

* Updated mailbox library

* Added mailbox library test (v3.13.10)
2025-12-31 14:58:34 +09:00
dependabot[bot]
37c47fca6b Bump qs and express in /wasm/demo (#6603)
Bumps [qs](https://github.com/ljharb/qs) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `qs` from 6.13.0 to 6.14.1
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.1)

Updates `express` from 4.21.2 to 4.22.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.2...v4.22.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.1
  dependency-type: indirect
- dependency-name: express
  dependency-version: 4.22.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-31 12:02:47 +09:00
Ashwin Naren
4cf5697e06 Symboltable updates (#5861)
* symboltable updates

* Fix symboltable

---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2025-12-31 11:57:42 +09:00
Jeong, YunWon
00ae4a1751 enhance error codec and exit code (#6602) 2025-12-31 11:28:04 +09:00
dependabot[bot]
580f6e3f34 Bump cranelift-* from 0.126.1 to 0.127.0 (#6571)
Bumps [cranelift](https://github.com/bytecodealliance/wasmtime) from 0.126.1 to 0.127.0.
- [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)

--------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2025-12-31 09:47:59 +09:00
Terry Tianlin Luan
cd012f9b2e Added doctest tests (#6598) 2025-12-31 09:43:10 +09:00
Terry Tianlin Luan
e22091ef60 Update straightforward packages Pt 1 (#6595)
* Deleted _pycodecs.py

* Updated code.py library

* Updated the _pydatetime.py lib

* Removed distutils package

* Updated doctest package

* * Updated datetimetester.py
* Added back in _pycodecs.py

* Used tool to verify datetimetester + test_code_module
2025-12-31 02:31:08 +09:00
Jeong, YunWon
6bf1ab65c0 Initiailzer for PyCStructure (#6586) 2025-12-31 01:58:34 +09:00
Vinh Nguyen
ca1c4c1f71 feat: Sync code.py + test_code_module.py from CPython v3.13.1 (#6181)
* feat: Sync code module from cpython 3.13

* also udpate test_code_module

---------

Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2025-12-31 01:54:50 +09:00
Jeong, YunWon
b98f06214d Update issubclass and make mro representation fit to CPython (#5866) 2025-12-31 01:42:11 +09:00
Jeong, YunWon
9fcc471c94 Prevent tb_next loop (#6596) 2025-12-30 17:36:15 +09:00
Ashwin Naren
7b9f0d9657 More tkinter (#5784)
* fix build

* fix usage of Tcl_GetVar2Ex

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* no panic on mainloop

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* add static var

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* fix getvar

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* add globalgetvar support

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* formatting

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>

* address review

* fix build

* Auto-format: cargo fmt --all

---------

Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
Co-authored-by: Jeong YunWon <jeong@youknowone.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-30 17:07:03 +09:00
Jeong, YunWon
e79a1a1a66 Fix traceback (#6569)
* Fix traceback

* Update traceback from CPython 3.13.11

* unmark test_traceback

* fix code

* fix debug range

* fix tests
2025-12-30 16:58:53 +09:00
Terry Tianlin Luan
6bd1d90b6a Updated bdb.py + test_bdb.py (#6593)
* Updated bdb.py + test_bdb.py

* Double checked test_bdb.py with the script
2025-12-30 16:35:58 +09:00
Jeong, YunWon
6ed0b4f0ac Add PythonFinallizationError and upgrade multiprocessing (#6592)
* PythonFinallizationError

* fix posixprocess

* upgrade multiprocessing from 3.13.11
2025-12-30 15:15:16 +09:00
Jeong, YunWon
dac236dac0 more no_std clippy (#6587) 2025-12-30 15:15:02 +09:00
Jack O'Connor
5e1fc93f50 Update zipfile to 3.13.5 (#6069)
* Add Lib/test/archiver_tests.py @ 3.13.5

Needed for updated zipfile tests.

* Update zipfile to 3.13.5

Notes:

- I have to skip some brand new tests due to shift_jis encoding not
being supported

- `test_write_filtered_python_package` marked as `expectedFailure` with
"AttributeError: module 'os' has no attribute 'supports_effective_ids'"
  - I didn't want to do a partial or full update to os module in this PR

---------

Co-authored-by: Jack O'Connor <jack@jackoconnor.dev>
2025-12-30 13:42:47 +09:00
Terry Tianlin Luan
1464d5ca43 Adding + Fixing Clippy rules to better align with #[no_std] (#6570)
* * Added alloc_instead_of_core, std_instead_of_alloc, and std_instead_of_core clippy rules
* Manually changed part of the code to use core/alloc

* use clippy --fix to fix issues in stdlib

* * Used clippy --fix to fix issues in vm
* Imported Range in vm/src/anystr.rs

* * Used clippy --fix to fix issues in common
2025-12-30 13:10:14 +09:00
Jeong, YunWon
489289f54a Fix subprocess and Update subprocess from Python 3.13.11 (#6583)
* fix test_subprocess

* fix posixsubprocess

* fix io warning

* Update subprocess from Python 3.13.11

* fix

* Auto-format: cargo fmt --all

* fix macos

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-30 12:28:47 +09:00
Terry Tianlin Luan
f274164dcd Add __future__.py tests (#6585)
* Added tests for __future__.py from v3.13.10

* * Deleted futures not in v3.13.10
* "Fixed" issues in test_future.py
2025-12-30 12:28:03 +09:00
Jeong, YunWon
2f4ca509c8 Initializer for PyCSimple, PyCArray (#6581) 2025-12-30 00:58:26 +09:00
Jeong, YunWon
f91ffe34d4 Fix pyexpat parse (#6582) 2025-12-30 00:36:52 +09:00
Jeong, YunWon
4ce6827916 various fix to support subprocess/multiprocessing (#6567)
* hashlib.UnsupportedDigestmodError

* mmap readonly

* thread _refcount

* fix os.statvfs

* apply review
2025-12-30 00:24:07 +09:00
Jeong, YunWon
39aa6f9524 use accept_raw (#6578) 2025-12-29 23:17:16 +09:00
dependabot[bot]
80599603e5 Bump insta from 1.45.0 to 1.45.1 (#6575)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.45.0 to 1.45.1.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.45.0...1.45.1)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.45.1
  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>
2025-12-29 22:53:04 +09:00
dependabot[bot]
879813f763 Bump rustix from 1.1.2 to 1.1.3 (#6572)
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v1.1.2...v1.1.3)

---
updated-dependencies:
- dependency-name: rustix
  dependency-version: 1.1.3
  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>
2025-12-29 22:52:52 +09:00
Jeong, YunWon
feb5066be2 stdlib_module_names (#6568) 2025-12-29 21:43:40 +09:00
Jeong, YunWon
45a1940fc6 Merge pull request #6564 from youknowone/socket 2025-12-29 14:26:56 +09:00
Jeong YunWon
ed62c8ef58 mark failures 2025-12-29 11:27:08 +09:00
Jeong, YunWon
183cebe8a1 Upgrade socket from 3.13.11 2025-12-29 11:27:08 +09:00
Jeong YunWon
5079ee8fe3 fix socket 2025-12-29 11:27:07 +09:00
Jeong YunWon
2271f74642 del for socket 2025-12-29 11:25:34 +09:00
Jeong YunWon
219b4e316b DEFAULT_TIMEOUT 2025-12-29 11:25:34 +09:00
Jeong YunWon
81c13347a4 CAN_ attrs 2025-12-29 11:25:31 +09:00
Jeong, YunWon
a7d417cf63 Fix socket.getattrinfo 2025-12-29 11:25:14 +09:00
Jeong, YunWon
d1ff2cde77 RichCompare contains pymethod. No AtomicCell for slots (#6562)
* slots: add comparison pymethods to Comparable trait

* No AtomicCell
2025-12-29 11:21:11 +09:00
Jeong, YunWon
476b75de49 Upgrade test_io from 3.13.11 and fix more io tests (#6565)
* fix various test_io

* Upgrade test_io from 3.13.11

* Fix more test_io
2025-12-28 20:36:15 +09:00
Jeong, YunWon
3600b6652d update _pyio, test_fileio from 3.13.11 and impl more io features (#6560)
* Update _pyio, test_fileio from 3.13.11

* impl more io

* unmark sucessful tests

* fix windows fileio
2025-12-28 18:06:47 +09:00
Jeong, YunWon
44327a8ee4 Upgrade test/support from CPython 3.13.11 (#6556)
* Upgrade test/support from CPython 3.13.11

* fix test_support
2025-12-28 16:30:44 +09:00
Copilot
7b36c9e8e0 Handle oversized __hash__ results without panicking (#6561)
* Initial plan

* Fix hash wrapper overflow handling

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Auto-format: cargo fmt --all

* Adjust __hash__ wrapper conversion

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Auto-format: cargo fmt --all

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-28 16:27:32 +09:00
Jeong, YunWon
75daf6f5ec fix is_file (#6563) 2025-12-28 16:03:11 +09:00
Jeong, YunWon
a37f4ec38e upgrade shutil (#6558)
* Update shutil from v3.13.11

* fix os.remove

* fix os.fchmod

* fix is_file
2025-12-28 15:49:10 +09:00
Copilot
04a4d1e02f Prevent array.tofile re-entrant deadlock and add regression snippet (#6559)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2025-12-28 14:48:27 +09:00
Jeong, YunWon
f8199d7099 impl basicsize (#6557)
* shape_differs

* may_add_dict

* basicsize
2025-12-28 14:25:10 +09:00
Jeong, YunWon
ee747134b5 signal timer (#6535)
* fix waitpid

* signal timer
2025-12-28 12:04:33 +09:00
Jeong, YunWon
527111bc98 PyWrapperDescrObject and rewrite toggle_slot (#6536)
* SlotFunc

* slotdef

* unify slots

* remove unsed slots
2025-12-28 11:44:36 +09:00
Jeong, YunWon
7edc3bba5d tp_itemsize (#6544) 2025-12-28 10:27:58 +09:00
Jeong, YunWon
012799f560 Fix enum and os.read related to signal (#6552)
* fix enum

* fix os.read to check signals
2025-12-28 10:13:03 +09:00
Jeong, YunWon
fd6c548766 no redundant venvlauncher targets (#6554) 2025-12-28 09:08:16 +09:00
Jeong, YunWon
ce00640b22 fix input to check pty child (#6553)
* fix input to check pty child

* Auto-format: cargo fmt --all

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-28 09:08:03 +09:00
Jeong, YunWon
9b2ad34a08 check surrogates (#6547) 2025-12-28 00:06:29 +09:00
Jeong, YunWon
5b20c458af Implement more except* (#6545)
* clean build documentation

* SetExcInfo

* fix EG subclass

* test_except_star
2025-12-27 23:03:23 +09:00
Shahar Naveh
8d9002800e Update test_dis.py from 3.13.11 (#6528) 2025-12-27 22:53:37 +09:00
Jeong, YunWon
75ecd72428 test_builtin.test_import (#6546)
* fix warnings

* fix test_import
2025-12-27 21:38:12 +09:00
Copilot
27ab62de48 Prevent __class__ reassignment across incompatible layouts (#6521)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2025-12-27 01:16:09 +09:00
Copilot
a7d7f81ca7 Handle unions with GenericAlias subclasses without TypeError (#6540)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeong YunWon <jeong@youknowone.org>
2025-12-27 01:13:20 +09:00
Shahar Naveh
b704f42158 Reduce usage JumpIfTrueOrPop & JumpIfFalseOrPop opcdes. Refactor compile_compare (#6524)
* Remove `JumpIfTrueOrPop` & `JumpIfFalseOrPop` opcdes

* Use correct instruction name

* Extract `compile_cmpop` to its own method

* Better alignment with CPython code

* Restore `PopJumpIf` instructions

* Revert PopJumpIfFalse for comparisons
2025-12-27 00:16:16 +09:00
Jeong, YunWon
7f4d308efc Implement maybe_pyc_file and .pyc file execution (#6539)
* Implement maybe_pyc_file and .pyc file execution

* unmark failing tests
2025-12-26 22:13:00 +09:00
Jeong, YunWon
4a6e8fb29e Add except* support (#6530) 2025-12-26 21:54:23 +09:00
Jeong, YunWon
57b4b4ae45 fix sys path (#6537) 2025-12-26 21:43:00 +09:00
Jeong, YunWon
1856415196 Fix f-string conversion flag ValueError when compiling AST (#6533) (#6534)
The ast_from_object for ConversionFlag was using bytecode::ConvertValueOparg::from_op_arg()
which only accepts internal oparg values (0, 1, 2, 3, 255), but Python's AST uses ASCII codes
('s'=115, 'r'=114, 'a'=97, -1=None).

This caused compile() on parsed AST with conversion flags to fail with "invalid conversion flag".
2025-12-26 21:38:05 +09:00
Jeong, YunWon
72a90da13b increase timeout-minutes to 45 (#6541) 2025-12-26 20:39:48 +09:00
Jeong, YunWon
c9bf8df19c copyslot for init (#6515) 2025-12-26 09:46:33 +09:00
Jeong, YunWon
ae39b132e4 impl more ntpath (#6531) 2025-12-26 09:43:01 +09:00
Jeong, YunWon
21f24cdcc7 impl more winapi (#6529) 2025-12-26 01:37:21 +09:00
Jeong, YunWon
40acd55290 venvlauncher (#6527) 2025-12-26 01:29:58 +09:00
Jeong, YunWon
2063c1e0bc Implement winapi.GetLongPathName (#6525) 2025-12-25 22:37:59 +09:00
Shahar Naveh
bcdf37bef1 Align opcode names in dis (#6526)
* opcodes dis repr like cpython. POP-> POP_TOP

* Adjust frame.rs

* Fix codegen

* snapshots

* Add doc for `PopTop`

* fix jit
2025-12-25 22:17:31 +09:00
Copilot
61ddd98b89 Handle missing type_params on AST Function/Class/TypeAlias nodes (#6512)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-25 19:08:23 +09:00
Copilot
a5a1173c3f Disallow rebinding __module__ on immutable builtins and add regression snippet (#6513)
* Initial plan

* Fix __module__ setter on immutable builtin types

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Remove unused value parameter from immutability helper

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Auto-format: cargo fmt --all

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-25 18:27:19 +09:00
Jeong, YunWon
151f0746a3 Implement copyslot (#6505) 2025-12-25 16:56:41 +09:00
Jeong, YunWon
aaecdd1747 repr for getset (#6514) 2025-12-25 16:55:19 +09:00
Jeong, YunWon
7eb0fe4984 Fix potential deadlock (#6509) 2025-12-25 15:49:44 +09:00
Copilot
8443b2c97e Add POSIX shared memory module for multiprocessing on Unix (#6498)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-25 14:54:44 +09:00
Jeong, YunWon
92acf339a1 fix pyexpat hang (#6507) 2025-12-25 13:31:29 +09:00
Jeong, YunWon
49dbbbd5b9 Fix SSL test_preauth_data_to_tls_server (#6508) 2025-12-25 13:28:50 +09:00
Jeong, YunWon
aae6bf566f Add tp_str (#6495)
* clean up hash

* __str__ slot wrapper
2025-12-25 11:00:53 +09:00
Copilot
6dbc8f0cfa Set SourceFileLoader for script execution in run_simple_file (#6496)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2025-12-25 10:33:22 +09:00
Jeong, YunWon
5122aed738 Fix install ujson (#6502) 2025-12-25 10:32:51 +09:00
Copilot
ebdc033c59 Bundle Lib directory in GitHub releases (#6497)
* Initial plan

* Add Lib directory archives to release workflow

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Add release notes explaining users need both binary and Lib archive

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Remove unnecessary --notes-start-tag parameter

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

* Only create zip archive, remove tar.gz

Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2025-12-25 09:48:20 +09:00
Jeong, YunWon
7ebb0f0c5c impl path_converter and os functions (#6484)
* os.setpgrp

* tcgetpgrp

* impl more os functions

* impl PathConverter
2025-12-25 09:21:09 +09:00
Copilot
72cf6c36d5 Add missing xmlparser attributes: namespace_prefixes, ordered_attributes, specified_attributes, intern (#6494)
* Add namespace_prefixes and other missing xmlparser attributes

- Added namespace_prefixes, ordered_attributes, specified_attributes (boolean attributes)
- Added intern dictionary attribute
- Added stub handlers for all missing handler types to ensure compatibility
- Created bool_property macro to ensure boolean attributes are converted correctly

* Remove expectedFailure decorators from passing tests

- Tests for buffer_text, namespace_prefixes, ordered_attributes, and specified_attributes now pass

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-25 09:09:58 +09:00
Copilot
be9e44aafb Allow SyntaxError.msg to be writable and reflected in string formatting (#6493)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
2025-12-25 09:08:18 +09:00
Jeong, YunWon
cbde5ce321 iter with slot-wrapper (#6488) 2025-12-24 23:35:35 +09:00
Jeong YunWon
c2a7393191 uniform __str__ 2025-12-24 22:23:43 +09:00
Jeong YunWon
a4e60f569e repr 2025-12-24 22:23:43 +09:00
Shahar Naveh
7c3bc5ed8d Update test_http_cookies.py from 3.13.11 (#6481)
* Update `test_http_cookies.py` from 3.13.11

* Mark failing test

* Update `http/cookies.py` from 3.13.11

* Unmark passing test

* Lower amount
2025-12-24 19:45:48 +09:00
Shahar Naveh
9d1477699c Update test_threading_local.py from 3.13.11 (#6482)
* Update `test_threading_local.py` from 3.13.11

* Mark failing test
2025-12-24 18:57:00 +09:00
Jeong, YunWon
c4e77287d1 __hash__ to slot_wrapper (#6480) 2025-12-24 18:56:47 +09:00
Jeong, YunWon
3d7e521acd introduce slot_wrapper (#4884) 2025-12-24 17:29:47 +09:00
Jeong, YunWon
4f0b940b16 impl preexec_fn (#6479) 2025-12-24 17:02:21 +09:00
Jeong, YunWon
309b2ad32d Fix ast end_location (#6478) 2025-12-24 16:54:54 +09:00
Jeong, YunWon
014622ac34 Fix os.access not to raise exception when path doesn't exist (#6477)
* Fix os.access not to raise exception when path doesn't exist

* add test
2025-12-24 14:23:33 +09:00
Jeong, YunWon
c763d67ef4 Fix dict.keys behavior (#6476) 2025-12-24 13:50:26 +09:00
Jeong, YunWon
215c5c6d7b signal.pthread_sigmask (#6475) 2025-12-24 13:15:33 +09:00
Jiseok CHOI
00205aad14 Bump libsqlite3-sys from 0.28 to 0.36 (#6472)
* Bump libsqlite3-sys from 0.28 to 0.36

Update libsqlite3-sys to version 0.36 and adapt to API changes by
replacing sqlite3_close_v2 with sqlite3_close. The v2 variant is no
longer directly exported in the newer version.

Fixes #6471

* Fix clippy
2025-12-24 13:10:57 +09:00
Jeong, YunWon
c35cb89f91 Merge pull request #6473 from youknowone/libffi
Upgrade libffi 5
2025-12-24 10:35:52 +09:00
Jeong YunWon
7c7e55ffc4 Upgrade libffi 2025-12-24 08:44:13 +09:00
Jeong, YunWon
2785bd8224 Merge pull request #6457 from youknowone/ctypes
Enable ctypes
2025-12-24 08:40:19 +09:00
Jeong YunWon
4a352344b6 mark failing test_ctypes 2025-12-24 00:13:30 +09:00
Jeong YunWon
7e6e0f66c8 test_ctypes from CPython 3.13.11 2025-12-24 00:13:30 +09:00
Jeong YunWon
03d6f4634f Upgrade ctypes to Python v3.13.11 2025-12-24 00:13:30 +09:00
Jeong YunWon
987216bcb8 enable ctypes 2025-12-24 00:13:30 +09:00
Jeong YunWon
79abbf0b29 fix ctypes 2025-12-24 00:13:30 +09:00
Jeong YunWon
4bf0bac52d debuggable whats_left 2025-12-23 23:29:15 +09:00
Jeong YunWon
286a5b5b8f msc_info in get_version 2025-12-23 23:28:58 +09:00
Jeong YunWon
fc0a34a5a5 Remove unused ctypes/field.rs 2025-12-23 21:58:51 +09:00
Jeong, YunWon
b44229f7ca debug whats_left 2025-12-23 21:58:51 +09:00
Jeong YunWon
9760249a75 fix multiarch 2025-12-23 16:41:28 +09:00
Jeong, YunWon
ec7588841f Merge pull request #6470 from youknowone/common-fix
Mark unrunnable test_threading. Fix SSL args names. Add args to sysconfigdata
2025-12-23 16:09:57 +09:00
Jeong, YunWon
df523cb58c skip spawnve on windows 2025-12-23 15:01:40 +09:00
Jeong YunWon
a84452ab45 fix sysconfigdata 2025-12-23 13:12:56 +09:00
Jeong YunWon
73e1c3816e fix ssl 2025-12-23 11:56:26 +09:00
Jeong YunWon
d919fe516e mark test_threading 2025-12-23 11:55:40 +09:00
dependabot[bot]
2342006b37 Bump aws-lc-rs from 1.15.1 to 1.15.2 (#6469)
Bumps [aws-lc-rs](https://github.com/aws/aws-lc-rs) from 1.15.1 to 1.15.2.
- [Release notes](https://github.com/aws/aws-lc-rs/releases)
- [Commits](https://github.com/aws/aws-lc-rs/compare/v1.15.1...v1.15.2)

---
updated-dependencies:
- dependency-name: aws-lc-rs
  dependency-version: 1.15.2
  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>
2025-12-23 08:57:03 +09:00
dependabot[bot]
5925f1483c Bump insta from 1.44.3 to 1.45.0 (#6468)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.44.3 to 1.45.0.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.44.3...1.45.0)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.45.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>
2025-12-22 22:25:09 +09:00
Jeong, YunWon
fdd2ac3b30 disallow __new__, __init__ (#6446)
* disallow __new__, __init__

* migrate to Initializer

* apply review
2025-12-22 21:10:43 +09:00
Shahar Naveh
569bee103f Use ruff_python_ast::visitor::Visitor for detecting await (#6466) 2025-12-22 12:43:58 +09:00
Jeong, YunWon
8b3bf558fc Introduce PyConfig to implement Paths correctly (#6461)
* getpath

* introduce PyConfig

* landmark in getpath
2025-12-21 08:25:17 +09:00
Jeong, YunWon
09fa97d1b9 CI to test Apple Intel (#6465) 2025-12-21 00:54:05 +09:00
Jeong, YunWon
898fe85f40 More openssl impl (#6464)
* Add openssl build to CI

* more openssl impls
2025-12-21 00:38:50 +09:00
Jeong, YunWon
6660170bf8 Share more ssl consts and fix openssl (#6462)
* Reuse SSL error names

* Share ssl error codes

* fix ssl

* fix openssl
2025-12-20 22:06:22 +09:00
Jeong, YunWon
7bfa5d9ced Buffer LongDouble + ndim (#6460)
* fix fix_test

* ndim buffer
2025-12-20 18:31:00 +09:00
Jeong, YunWon
46c61a65a6 Upgrade venv to v3.13.11 (#6459)
* Upgrade venv

* get_venv_base_executable

* mark failing tests
2025-12-20 12:17:11 +09:00
Jeong, YunWon
ab1105a61d Fix fix_test.py (#6415) 2025-12-20 09:55:28 +09:00
Shahar Naveh
6c186e3893 Update smptlib and test_smtpnet.py from 3.13.11 (#6435)
* Update `test_smtpnet.py` from 3.13.11

* Update `test_smtplib.py` from 3.13.11

* Update `smtplib.py` from 3.13.11

* Catch AttributeError
2025-12-19 22:51:09 +09:00
Jeong, YunWon
70b93898d4 ctypes overhaul (#6450) 2025-12-19 14:10:55 +09:00
fanninpm
100b043fb6 Merge pull request #6453 from ShaharNaveh/skip-flakey-subprocess-test
Skip flakey test
2025-12-18 10:49:02 -05:00
Jeong, YunWon
9e439667da Fix openssl build and shared ssl/error.rs (#6456)
* Fix openssl

* shared error
2025-12-18 10:44:56 +09:00
Jeong, YunWon
a4c93dfbbf Remove useless &PyRef patterns (#6455)
* fix &PyTypeRef

* Remove useless &PyObjectRef

* Remove useless &PyRef
2025-12-17 20:58:00 +09:00
Jeong, YunWon
0412dfdb3b Fix winerror handling (#6454) 2025-12-17 20:57:34 +09:00
Shahar Naveh
9d2dd17455 Add regression test (#6452) 2025-12-17 19:02:44 +09:00
Shahar Naveh
be559ae453 Update support/import_helper.py from 3.13.11 (#6451)
* Update `support/import_helper.py` from 3.13.11

* Unmark passing test
2025-12-17 08:15:25 +09:00
ShaharNaveh
f3916950bf Skip flakey test 2025-12-16 21:35:55 +01:00
Shahar Naveh
adc2b0dbbe Update test_zipfile64.py from 3.13.11 (#6433)
* Update `test_zipfile64.py` from 3.13.11

* Mark flaky test
2025-12-17 00:28:06 +09:00
Jeong, YunWon
9aa1f18998 Unraiseable traceback (#6449) 2025-12-17 00:16:51 +09:00
Jeong, YunWon
246fab63f7 flag: DISALLOW_INSTANTIATION (#6445)
* Set tp_new slot when build heap/static type

* Improve type tp_call impl to check tp_new existence and error if not exist

* Set DISALLOW_INSTANTIATION flag on several types according to cpython impl

* Allow #[pyslot] for function pointer

* Fix DISALLOW_INSTANTIATION

---------

Signed-off-by: snowapril <sinjihng@gmail.com>
Co-authored-by: snowapril <sinjihng@gmail.com>
2025-12-17 00:12:03 +09:00
Jeong, YunWon
aef4de4ab8 fix buffer (#6447) 2025-12-16 23:27:21 +09:00
Jeong, YunWon
65bdfc3d4e Integrate OSError creations into OSErrorBuilder (#6443) 2025-12-16 23:11:35 +09:00
Jeong, YunWon
272b36daa5 small fixes (#6444)
* fix memoryview

* retype slot zelf
2025-12-16 11:16:38 +09:00
Jeong, YunWon
30cc772454 sys.set_asyncgen_hook (#6439)
* PyAnextAwaitable::state

* sys.set_asyncgen_hook
2025-12-16 10:11:17 +09:00
dependabot[bot]
e6d9a9aef0 Bump actions/download-artifact from 6.0.0 to 7.0.0 (#6441) 2025-12-15 23:54:53 +09:00
dependabot[bot]
78d2e5bdf1 Bump actions/upload-artifact from 5.0.0 to 6.0.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 23:53:21 +09:00
Jeong, YunWon
550497eb79 Use parking_lot in faulthandler (#6438) 2025-12-15 17:56:48 +09:00
Lee Dogeon
8e21db11d6 Update architecture/architecture.md (#6437)
* Replace architecture overview image with mermaid graph

* Update architecture.md with correct paths and links

- Fix file paths to reflect crates/ directory structure
- Update GitHub permalink references to current commit
- Remove trailing slashes from package names
- Use archive.org for broken desosa.nl link
- Change master to main branch reference

* Update architecture documentation for ruff_python_parser

RustPython now uses ruff_python_parser from astral-sh/ruff instead of
the separate RustPython/Parser project. Update architecture documentation
to reflect this change.
2025-12-15 09:57:06 +09:00
Jeong, YunWon
d36a2cffde New subclass payload layout (#6319)
* PySubclass

* Add base payload to exception

* PyStructSequecne base

* redefine PyOSError and subtypes

* heap exception new

* rework UNSUPPORTED_OPERATION
2025-12-14 19:17:14 +09:00
Jeong, YunWon
21300f689d Allow with() in pyexception (#6436) 2025-12-14 12:16:49 +09:00
Jeong, YunWon
b3c2aa6b51 PyAnextAwaitable (#6427) 2025-12-14 12:15:29 +09:00
Shahar Naveh
db71554dbc Update test_urllib2net.py from 3.13.11 (#6434) 2025-12-13 22:06:55 +09:00
Shahar Naveh
dbacb07246 Update test_urllibnet.py from 3.13.11 (#6432) 2025-12-13 22:06:41 +09:00
Shahar Naveh
835918afa1 Set timeout only on test step (#6431) 2025-12-13 11:09:26 +09:00
Shahar Naveh
1e3d4fe218 Update test_xmlrpc.py from 3.13.11 (#6430) 2025-12-13 11:09:11 +09:00
Shahar Naveh
901324f21e Update test_raise.py from 3.13.11 (#6423) 2025-12-13 11:08:47 +09:00
Jeong, YunWon
30dd5bedc6 use posix error messages under 127 (#6420)
* use posix error messages under 127

* use posix error message for unix

* unmark successful tests
2025-12-13 09:24:17 +09:00
Shahar Naveh
e227956a58 Update test_generators.py from 3.13.11 (#6426) 2025-12-13 00:02:02 +09:00
Shahar Naveh
f49d20df22 Update test_setcomps.py from 3.13.11 (#6425) 2025-12-12 23:47:16 +09:00
Shahar Naveh
9ccf4c1872 Ruff auto-format (#6422) 2025-12-12 22:46:39 +09:00
Jeong, YunWon
093ba251ab Merge pull request #5377 from youknowone/inspect
Update dataclasses, inspect from CPython 3.13
2025-12-12 22:44:39 +09:00
Shahar Naveh
6f8050526f Ruff as part of PR auto-format (#6421) 2025-12-12 21:59:56 +09:00
Jeong, YunWon
c11a72a4f1 impl more ast (#6419) 2025-12-12 21:50:54 +09:00
Jeong, YunWon
9bbfdf5067 mark expectedFailure 2025-12-12 21:44:00 +09:00
Jeong, YunWon
ef6c6ad117 patch test_inspect 2025-12-12 21:44:00 +09:00
Jeong, YunWon
152d10bfea hybrid dis.py from CPython 3.13.10 2025-12-12 21:44:00 +09:00
Jeong, YunWon
d11604995a Upgrade dataclasses from CPython 3.13.10 2025-12-12 21:43:57 +09:00
Jeong, YunWon
d22956ebc1 inspect from v3.13.10 2025-12-12 21:43:24 +09:00
Jeong YunWon
be5f660dfe impl more ast 2025-12-12 21:43:24 +09:00
Shahar Naveh
49522e7a5e Update ruff CI version (#6418) 2025-12-12 21:12:19 +09:00
Shahar Naveh
a47b32816b Merge pull request #6417 from ShaharNaveh/regr-test-doc-leak
Add regression test for #4505
2025-12-12 21:12:02 +09:00
Jeong, YunWon
35f88608d2 Fix test_subprocess and locale (#6416)
* Fix test_subprocess

* fix windows locale
2025-12-12 21:03:26 +09:00
Jeong, YunWon
752c0f68fd fix windows locale 2025-12-12 20:26:47 +09:00
Jeong, YunWon
b9fa405fd4 Fix test_subprocess 2025-12-12 20:26:47 +09:00
Shahar Naveh
75dcf8042e Tidy codegen::ir::BlockIdx api (#6413)
* Tidy `codegen::ir::BlockIdx` api

* Update crates/codegen/src/ir.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-12 20:05:31 +09:00
Jeong, YunWon
5a5b721576 Fix misused PyTypeError (#6412) 2025-12-12 17:23:27 +09:00
Jeong, YunWon
df3a0b2f25 impl {raise_,str}signal (#6411)
* year>=1900 on windows

* signal

* set_wakeup_fd
2025-12-12 17:02:32 +09:00
Jeong, YunWon
4bec0ad1c6 Fix test_runpy (#6409) 2025-12-12 15:26:00 +09:00
Jeong, YunWon
772b92edde Fix windows socket (#6408)
* fix _path_splitroot

* Fix windows socket
2025-12-12 10:37:40 +09:00
Jeong, YunWon
2ff1fba6ed Faulthandler (#6406) 2025-12-12 09:23:59 +09:00
Jeong, YunWon
3438fb2850 nt junction (#6407)
* pylib strip path

* nt._path_* functions,

* nt junction
2025-12-12 09:03:19 +09:00
Jeong, YunWon
d34b2cf8f0 TypeData (#6403)
* HeapTypeExt::type_data

* Apply TypeDataSlot to ctypes
2025-12-12 07:57:31 +09:00
Jeong, YunWon
bfd873a8b8 a few unreachable->unimplemented, clippy (#6405)
* unreachable -> unimplemented

* Fix clippy on 1.92
2025-12-12 01:13:28 +09:00
Jeong, YunWon
f379ea8327 winapi._findfirstfile,nt.chmod (#6401) 2025-12-12 00:18:02 +09:00
Jeong, YunWon
04d55fa5c6 Faulthandler (#6400) 2025-12-12 00:08:55 +09:00
Jeong, YunWon
72e892a57d Remove cargo vcpkg from windows build (#6404) 2025-12-11 23:20:24 +09:00
Jeong, YunWon
a477835970 py_new separation for set/tuple/bytes/str 2025-12-11 23:17:10 +09:00
Jeong, YunWon
5f496c955c PyType::py_new to return Self (#6398)
* PyType::py_new to return Self

* int/float/complex
2025-12-11 22:19:11 +09:00
Jeong, YunWon
00543942aa nt.skiproot, winapi.LCMapStringEx (#6399) 2025-12-11 21:42:28 +09:00
Jeong, YunWon
4828fb3ba6 test_os, test_io on windows (#6379)
* Allow hidden env vars on nt

* Enable test_os on windows

* enable test_io on windows
2025-12-11 13:11:29 +09:00
dependabot[bot]
20d9099bf3 Bump insta from 1.44.2 to 1.44.3 (#6394)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.44.2 to 1.44.3.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.44.2...1.44.3)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.44.3
  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>
2025-12-11 08:34:32 +09:00
dependabot[bot]
3a7a5205fb Bump log from 0.4.28 to 0.4.29 (#6396)
Bumps [log](https://github.com/rust-lang/log) from 0.4.28 to 0.4.29.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.28...0.4.29)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.29
  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>
2025-12-11 08:34:20 +09:00
dependabot[bot]
81c9b05f97 Bump libc from 0.2.177 to 0.2.178 (#6395)
Bumps [libc](https://github.com/rust-lang/libc) from 0.2.177 to 0.2.178.
- [Release notes](https://github.com/rust-lang/libc/releases)
- [Changelog](https://github.com/rust-lang/libc/blob/0.2.178/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/libc/compare/0.2.177...0.2.178)

---
updated-dependencies:
- dependency-name: libc
  dependency-version: 0.2.178
  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>
2025-12-11 08:34:06 +09:00
Jeong, YunWon
2055145a7e validate_downcast (#6393) 2025-12-10 23:31:38 +09:00
Jeong, YunWon
1b17587585 __slots__ xor __dict__ , name mangling (#6392)
* __slots__ xor __dict__

* mangle_name for `__` prefixed members
2025-12-10 23:30:10 +09:00
Jeong, YunWon
98fff96f1c __doc__ handing in the right way (#6390) 2025-12-10 20:04:09 +09:00
Jeong, YunWon
90717e5ef7 doc db to include types.* (#6391)
* Add types.* for doc/generate.py

* udpate-doc-db workflow

* Update doc DB for CPython 3.13.9

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-12-10 19:23:27 +09:00
Jeong, YunWon
c71c78c1a0 Upgrade pyo3 (#6389) 2025-12-10 10:29:20 +09:00
Jeong, YunWon
a64cfac2ca Fix multiprocessing closesocket, set_errno (#6388)
* Fix multiprocessing closesocket

* set_errno
2025-12-10 10:02:53 +09:00
Jeong, YunWon
30fb9d73cc Fix test_io on windows (#6387)
* mark skip on test_io

* Drop for FileIO

* IO Desctructors

* Iterator
2025-12-10 09:21:32 +09:00
Jeong, YunWon
14232ad0d2 new_last_{os,errno}_error (#6381)
* new_last_{os,errno}_error

* Remove os::errno_err

* enable ssl multithread test
2025-12-10 09:12:38 +09:00
dependabot[bot]
f723974924 Bump system-configuration from 0.6.1 to 0.7.0 (#6385)
Bumps [system-configuration](https://github.com/mullvad/system-configuration-rs) from 0.6.1 to 0.7.0.
- [Changelog](https://github.com/mullvad/system-configuration-rs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mullvad/system-configuration-rs/compare/v0.6.1...v0.7.0)

---
updated-dependencies:
- dependency-name: system-configuration
  dependency-version: 0.7.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>
2025-12-10 09:01:29 +09:00
dependabot[bot]
9f8090c830 Bump libloading from 0.8.9 to 0.9.0 (#6384)
Bumps [libloading](https://github.com/nagisa/rust_libloading) from 0.8.9 to 0.9.0.
- [Commits](https://github.com/nagisa/rust_libloading/compare/0.8.9...0.9.0)

---
updated-dependencies:
- dependency-name: libloading
  dependency-version: 0.9.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>
2025-12-10 09:01:17 +09:00
dependabot[bot]
42b969b088 Bump oid-registry from 0.7.1 to 0.8.1 (#6383)
Bumps [oid-registry](https://github.com/rusticata/oid-registry) from 0.7.1 to 0.8.1.
- [Commits](https://github.com/rusticata/oid-registry/compare/oid-registry-0.7.1...oid-registry-0.8.1)

---
updated-dependencies:
- dependency-name: oid-registry
  dependency-version: 0.8.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>
2025-12-10 09:01:05 +09:00
dependabot[bot]
4ebdadea45 Bump pyo3 from 0.26.0 to 0.27.2 (#6386)
Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.26.0 to 0.27.2.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.26.0...v0.27.2)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-version: 0.27.2
  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>
2025-12-10 01:02:31 +09:00
Jeong, YunWon
ac48643447 Add TextIOWrapper.truncate, fix a few bugs (#6382) 2025-12-10 00:58:21 +09:00
Jeong, YunWon
26b3445d14 windows pipe/getppid (#6378)
* pipe/pid

* libc first
2025-12-09 23:49:32 +09:00
Jeong, YunWon
8bc46cf83d Fix windows inode to use u128 (#6377) 2025-12-09 23:40:54 +09:00
Sean Lawlor
6003c87582 Freeze ruff libraries to the REV that the TAG points to. (#6375)
Tags can move and be re-aliased to different revisions, and this actually freezes the rev (similar to how a published crate would) so that it only applies to this specific commit hash.
2025-12-09 23:19:42 +09:00
Jeong, YunWon
c578861598 nt.terminalsize (#6374)
* terminalsize

* use _get_osfhandle
2025-12-09 23:17:53 +09:00
Jeong, YunWon
79cd048e1f native ExceptionGroup (#6358) 2025-12-09 23:02:44 +09:00
Jeong, YunWon
5365805312 minimize ssl lock (#6376) 2025-12-09 23:01:12 +09:00
Jeong, YunWon
db95946b8d Fix SSL deferred error (#6371)
* Fix SSL to return deferred error on the right time

* lease conn_guard

* SslError::Io

* is_connection_closed
2025-12-09 21:59:31 +09:00
Jeong, YunWon
a99164fd7b nt is_dir,is_file,listmount,listvolume (#6373)
* is_dir/is_file for windows

* listmount/listvolume

* check_env_var_len
2025-12-09 20:08:16 +09:00
Jeong, YunWon
cc534d2954 Fix failing reason (#6370) 2025-12-09 10:46:40 +09:00
dependabot[bot]
904cc0a575 Bump pem-rfc7468 from 0.7.0 to 1.0.0 (#6369)
Bumps [pem-rfc7468](https://github.com/RustCrypto/formats) from 0.7.0 to 1.0.0.
- [Commits](https://github.com/RustCrypto/formats/compare/pem-rfc7468/v0.7.0...pem-rfc7468/v1.0.0)

---
updated-dependencies:
- dependency-name: pem-rfc7468
  dependency-version: 1.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>
2025-12-09 10:46:14 +09:00
dependabot[bot]
0760551cf7 Bump lz4_flex from 0.11.5 to 0.12.0 (#6367)
Bumps [lz4_flex](https://github.com/pseitz/lz4_flex) from 0.11.5 to 0.12.0.
- [Release notes](https://github.com/pseitz/lz4_flex/releases)
- [Changelog](https://github.com/PSeitz/lz4_flex/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pseitz/lz4_flex/compare/0.11.5...0.12.0)

---
updated-dependencies:
- dependency-name: lz4_flex
  dependency-version: 0.12.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>
2025-12-09 09:29:01 +09:00
dependabot[bot]
7f15c8c1bd Bump js-sys from 0.3.82 to 0.3.83 (#6366)
Bumps [js-sys](https://github.com/wasm-bindgen/wasm-bindgen) from 0.3.82 to 0.3.83.
- [Release notes](https://github.com/wasm-bindgen/wasm-bindgen/releases)
- [Changelog](https://github.com/wasm-bindgen/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/wasm-bindgen/wasm-bindgen/commits)

---
updated-dependencies:
- dependency-name: js-sys
  dependency-version: 0.3.83
  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>
2025-12-09 09:28:49 +09:00
dependabot[bot]
614cb84e7a Bump wasm-bindgen from 0.2.105 to 0.2.106 (#6365)
Bumps [wasm-bindgen](https://github.com/wasm-bindgen/wasm-bindgen) from 0.2.105 to 0.2.106.
- [Release notes](https://github.com/wasm-bindgen/wasm-bindgen/releases)
- [Changelog](https://github.com/wasm-bindgen/wasm-bindgen/blob/main/CHANGELOG.md)
- [Commits](https://github.com/wasm-bindgen/wasm-bindgen/compare/0.2.105...0.2.106)

---
updated-dependencies:
- dependency-name: wasm-bindgen
  dependency-version: 0.2.106
  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>
2025-12-09 09:28:33 +09:00
dependabot[bot]
df7694ca51 Bump x509-parser from 0.16.0 to 0.18.0 (#6364)
Bumps [x509-parser](https://github.com/rusticata/x509-parser) from 0.16.0 to 0.18.0.
- [Changelog](https://github.com/rusticata/x509-parser/blob/x509-parser-0.18.0/CHANGELOG.md)
- [Commits](https://github.com/rusticata/x509-parser/compare/x509-parser-0.16.0...x509-parser-0.18.0)

---
updated-dependencies:
- dependency-name: x509-parser
  dependency-version: 0.18.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>
2025-12-09 06:24:38 +09:00
dependabot[bot]
087e812bc0 Bump pyo3 from 0.26.0 to 0.27.2 (#6363)
Bumps [pyo3](https://github.com/pyo3/pyo3) from 0.26.0 to 0.27.2.
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.26.0...v0.27.2)

---
updated-dependencies:
- dependency-name: pyo3
  dependency-version: 0.27.2
  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>
2025-12-09 06:08:52 +09:00
dependabot[bot]
808c8afbe1 Bump criterion from 0.7.0 to 0.8.1 (#6361)
Bumps [criterion](https://github.com/criterion-rs/criterion.rs) from 0.7.0 to 0.8.1.
- [Release notes](https://github.com/criterion-rs/criterion.rs/releases)
- [Changelog](https://github.com/criterion-rs/criterion.rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/criterion-rs/criterion.rs/compare/criterion-plot-v0.7.0...criterion-v0.8.1)

---
updated-dependencies:
- dependency-name: criterion
  dependency-version: 0.8.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>
2025-12-09 06:08:15 +09:00
dependabot[bot]
d432cfd350 Bump uuid from 1.18.1 to 1.19.0 (#6362)
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.18.1 to 1.19.0.
- [Release notes](https://github.com/uuid-rs/uuid/releases)
- [Commits](https://github.com/uuid-rs/uuid/compare/v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 1.19.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>
2025-12-09 06:08:04 +09:00
Jeong, YunWon
7a5d81a469 Fix os.utime for windows/macos (#6354) 2025-12-09 06:07:48 +09:00
dependabot[bot]
e4b9b26037 Bump winresource from 0.1.27 to 0.1.28 (#6360)
Bumps [winresource](https://github.com/BenjaminRi/winresource) from 0.1.27 to 0.1.28.
- [Commits](https://github.com/BenjaminRi/winresource/commits)

---
updated-dependencies:
- dependency-name: winresource
  dependency-version: 0.1.28
  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>
2025-12-09 06:07:31 +09:00
Jeong, YunWon
bb4e30a6df fspath try from (#6359) 2025-12-09 05:44:21 +09:00
Jeong, YunWon
b200f0e8d2 Replace windows to windows-sys (#6356) 2025-12-09 05:08:45 +09:00
Jeong, YunWon
7157697f96 last_posix_errno (#6341) 2025-12-09 04:41:44 +09:00
Jeong, YunWon
bafaa1a826 scandir/lstat (#6357) 2025-12-09 04:32:06 +09:00
Jeong, YunWon
abfd148d63 SSLError (#6351) 2025-12-09 02:55:34 +09:00
Jeong, YunWon
a484ba4790 st_file_attributes (#6353) 2025-12-09 02:49:36 +09:00
Jeong, YunWon
abc5c223a6 os.waitstatus_to_exitcode for windows (#6355) 2025-12-09 02:46:05 +09:00
Jeong, YunWon
42d0a583e8 fix remove 2025-12-09 02:43:58 +09:00
Jeong, YunWon
649606fc58 Unconstructible 2025-12-09 02:43:58 +09:00
Jeong, YunWon
4438dba49c use ToWide 2025-12-09 02:43:58 +09:00
dependabot[bot]
638a408eff Bump malachite-* from 0.6.1 to 0.8.0 (#6344)
* Bump malachite-base from 0.6.1 to 0.8.0

Bumps [malachite-base](https://github.com/mhogrefe/malachite) from 0.6.1 to 0.8.0.
- [Release notes](https://github.com/mhogrefe/malachite/releases)
- [Commits](https://github.com/mhogrefe/malachite/compare/v0.6.1...v0.8.0)

---
updated-dependencies:
- dependency-name: malachite-base
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump malachite-*

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2025-12-09 01:54:25 +09:00
dependabot[bot]
a3d638ab5f Bump windows-sys from 0.59.0 to 0.61.2 (#6346)
* Bump windows-sys from 0.59.0 to 0.61.2

Bumps [windows-sys](https://github.com/microsoft/windows-rs) from 0.59.0 to 0.61.2.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/commits)

---
updated-dependencies:
- dependency-name: windows-sys
  dependency-version: 0.61.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix breaking changes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeong, YunWon <jeong@youknowone.org>
2025-12-09 00:53:12 +09:00
Jeong, YunWon
bf565e917a Windows execv, spawnv, wait (#6350)
* more const

* wait

* exec

* mkdir
2025-12-09 00:37:52 +09:00
dependabot[bot]
2a425351e2 Bump phf from 0.11.3 to 0.13.1 (#6345)
Bumps [phf](https://github.com/rust-phf/rust-phf) from 0.11.3 to 0.13.1.
- [Release notes](https://github.com/rust-phf/rust-phf/releases)
- [Changelog](https://github.com/rust-phf/rust-phf/blob/main/RELEASE_PROCESS.md)
- [Commits](https://github.com/rust-phf/rust-phf/compare/phf-v0.11.3...v0.13.1)

---
updated-dependencies:
- dependency-name: phf
  dependency-version: 0.13.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>
2025-12-08 23:55:58 +09:00
Jeong, YunWon
4592787946 get_inheritable, dup for windows (#6343) 2025-12-08 23:55:03 +09:00
dependabot[bot]
8ad7f912ea Bump actions/setup-python from 6.0.0 to 6.1.0 (#6349)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v6...v6.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.1.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>
2025-12-08 23:10:53 +09:00
dependabot[bot]
7e34ab743a Bump actions/checkout from 5.0.1 to 6.0.1 (#6348)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.1 to 6.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v5.0.1...v6.0.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  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>
2025-12-08 23:09:50 +09:00
dependabot[bot]
4e6ef4150d Bump webpki-roots from 0.26.11 to 1.0.4 (#6347)
Bumps [webpki-roots](https://github.com/rustls/webpki-roots) from 0.26.11 to 1.0.4.
- [Release notes](https://github.com/rustls/webpki-roots/releases)
- [Commits](https://github.com/rustls/webpki-roots/compare/v/0.26.11...v/1.0.4)

---
updated-dependencies:
- dependency-name: webpki-roots
  dependency-version: 1.0.4
  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>
2025-12-08 23:09:23 +09:00
Jiseok CHOI
7250b1f854 refactor(sqlite): add check_cursor_valid method for cursor state validation (#6342)
Extract cursor validity check into a separate method that doesn't hold
the lock. This is useful for executescript which only needs to verify
the cursor state but doesn't need to modify CursorInner.

- Add check_cursor_valid() method that checks if cursor is initialized
  and not closed without retaining the mutex guard
- Use check_cursor_valid() in executescript instead of inner() since
  executescript doesn't modify CursorInner
2025-12-08 21:54:40 +09:00
Jeong, YunWon
42d497a142 Enable PIP test on windows (#6211)
* Patch ensurepip whl to work on windows

* Enable PIP test on windows
2025-12-08 16:36:33 +09:00
Jeong, YunWon
876368e476 windows umask, win32_xstat_slow_impl, fake EXT_SUFFIX (#6340)
* umask

* EXT_SUFFIX

* File Attributes and win32_xstat_slow_impl

* unmark tests
2025-12-08 10:39:15 +09:00
Jeong, YunWon
cab41c807b windows codecs (#6337)
* mbcs_codec

* oem codec
2025-12-08 01:51:51 +09:00
Jeong, YunWon
f4b8b019ca winapi suppliment (#6338)
* winapi.ExitProcess

* Fix winapi Handle

* msvcrt.GetErrorMode

* windows stats

* unmark tests
2025-12-08 01:35:31 +09:00
Jiseok CHOI
a14dd5921d fix(sqlite): Raise ProgrammingError when operating on a closed cursor (#6339) 2025-12-08 00:14:31 +09:00
Jeong, YunWon
cb7450df31 ssl module for windows (#6332)
* SSL for windows

* mark expected failure on test_ssl_in_multiple_threads
2025-12-08 00:08:53 +09:00
Jeong, YunWon
590da47499 pin flate2 under 1.1.5 (#6335) 2025-12-07 12:10:09 +09:00
Jeong, YunWon
aea956d601 WindowsError (#6333) 2025-12-06 17:21:52 +09:00
Jeong, YunWon
56a7fb129a upgrade cranelift (#6331) 2025-12-06 10:01:37 +09:00
Jeong, YunWon
b3141d1231 mmap for windows (#6329) 2025-12-06 09:26:33 +09:00
Jeong, YunWon
89bcae7bea fix time (#6330) 2025-12-06 08:30:57 +09:00
Jeong, YunWon
c826f9d809 PyStructSequence Compatibility (#6327)
* Fix local time

* PyStructSequence
2025-12-05 22:44:00 +09:00
Jeong, YunWon
6f786b58ad Merge pull request #5594 from arihant2math/winreg
Rewrite of winreg module and bump to 3.13.2
2025-12-05 22:31:09 +09:00
Jeong, YunWon
d8dde123b1 Complete winreg 2025-12-05 19:57:26 +09:00
Ashwin Naren
517987c7b3 rewrite of winreg module and add test_winreg 2025-12-05 15:10:45 +09:00
Yash Suthar
c2ca9a7d31 prefer nb_bool slot in try_to_bool instead of __bool__ (#6328)
Fixed: #6113

---------

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
2025-12-05 08:32:45 +09:00
Shahar Naveh
2b90e826ec Use rust idioms for accessing a Vec in codegen/src/compile.rs (#6326)
* Use rust idioms for accessing a `Vec`

* clippy

* Remove `unsafe`
2025-12-04 09:17:04 +09:00
Shahar Naveh
f49c18578a Move PrintExpr to IntristicFunction1 (#6324) 2025-12-03 21:17:36 +09:00
fanninpm
59f422de66 Add _io.TextIOWrapper.detach method (#6267)
* Stub out _io.TextIOWrapper.detach method

* Implement _io.TextIOWrapper.detach method

* Make _io.TextIOWrapper.detach not re-enter lock
2025-12-03 16:11:39 +09:00
Shahar Naveh
305fb489e7 Remove ImportNameless bytecode (#6325) 2025-12-03 07:07:33 +09:00
Shahar Naveh
9f203ee7d2 Update test_print from 3.13.9 (#6323) 2025-12-03 07:05:58 +09:00
Shahar Naveh
26d5307520 Align f-string related bytecodes with 3.13 (#6321)
* Align `f-string` related bytecodes with 3.13

* Resolve name collision

* Adjust for ruff return value
2025-12-03 07:05:16 +09:00
Shahar Naveh
d287d1e063 Sort Instruction enum & related match arms (#6322)
* Sort `Instruction` enum

* Sort `fmt_dis` match arms

* Sort `run_instruction`

* Sort JIT `add_instruction`
2025-12-02 23:25:33 +09:00
Jeong, YunWon
563dc0fc9e Separate Debug from PyPayload (#6320) 2025-12-02 20:20:31 +09:00
Jeong, YunWon
bf8152b4b8 Remove unused _membership_iter_search (#6318) 2025-12-02 10:11:04 +09:00
Shahar Naveh
9130dd8068 Unify BINARY_OP bytecodes (#6317)
* Unify BINARY_OP bytecodes

* Add missing op to `as_inplace`

* Fix doc example

* Fix jit

* Fix doc

* Use correct opname

* Fix dis fmt

* Inplace ops support in JIT
2025-12-02 09:00:26 +09:00
Jeong, YunWon
8e0a86d163 init debug helper (#6315) 2025-11-30 16:58:07 +09:00
Shahar Naveh
b84f7c19ad Fix super and Update test_{descr,super}.py from 3.13.9 (#6314)
* Update `test_descr.py` from 3.13.9

* Update `test_super.py` from 3.13.9
2025-11-30 10:16:50 +09:00
Shahar Naveh
4051becc9e Ensure BuildSlice oparg to be either 2 or 3 (#6313)
* Force `BuildSlice` oparg to be either 2 or 3

* `compile_slice` to return `BuildSliceArgCount`
2025-11-30 09:05:29 +09:00
Jeong, YunWon
6782fa2219 ctypes buffer (#6311)
* Fix array

* from buffer

* AsBuffer

* Fix instruction

* constructable pointer

* thunk

* in_dll

* fix sizeof/alignment
2025-11-29 21:17:04 +09:00
Jeong, YunWon
e5aec9d7fd DISALLOW_INSTANTIATION (#6310) 2025-11-29 12:46:14 +09:00
Jeong, YunWon
4fcce4d0b9 ctypes struct/union/array (#6309)
* PyCStructure

* impl union

* array

* temp remove

* apply review
2025-11-29 12:13:49 +09:00
Jeong, YunWon
95b8c60756 Fix __build_class__ compatibility (#6308)
* Update __build_class__

* fix test
2025-11-29 11:54:04 +09:00
Shahar Naveh
7d8f0b989c Split TestOperator instruction (#6306)
* Split `TestOperator` inatruction

* Update snapshot

* Set as have label
2025-11-29 09:02:52 +09:00
Jeong, YunWon
a81912857d Ctypes __mul__ (#6305) 2025-11-29 02:15:25 +09:00
Jeong, YunWon
23ec5a55ad Fix import ctypes and Lib/ctypes/__init__.py from cpython3.13.9 (#6304)
* Fix ctypes import blockers

* Update Lib/ctypes/__init__.py from cpython3.13.9
2025-11-29 01:32:52 +09:00
Shahar Naveh
fef9de22c4 Remove Rotate* & Duplicate* instructions (#6303)
* Remove `Instruction::Duplicate2?`

* Remove `Instruction::Rotate*` instructions

* Fix jit

* Update snapshot
2025-11-29 00:46:27 +09:00
Jeong, YunWon
9528ee8246 Merge pull request #5653 from arihant2math/ctypes-pt5
_ctypes pt. 5
2025-11-29 00:24:21 +09:00
Jeong YunWon
8af105fc4f Make mergeable 2025-11-29 00:22:44 +09:00
Jeong YunWon
14cf4e32d0 Fix PyCSimple 2025-11-29 00:22:44 +09:00
Ashwin Naren
f37ea52565 Ctypes
more pointer implementation

fix import

add more classes

ctypes overhall

updates

fix build

fix warnings, pass test

fix panic on improper library load

test on macos

formatting

tmp

minor updates
2025-11-28 23:27:24 +09:00
Wildan M
0e6e256f8e Fix redox compilation in stdlib (#6298) 2025-11-28 08:41:22 +09:00
dependabot[bot]
1b3261a090 Bump node-forge from 1.3.1 to 1.3.2 in /wasm/demo (#6297)
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.1 to 1.3.2.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-28 08:39:46 +09:00
Jeong, YunWon
896144edc3 Merge pull request #6300 from ShaharNaveh/update-libc-177 2025-11-28 08:39:24 +09:00
ShaharNaveh
081a8f0451 Regenerate libc constatnts 2025-11-27 17:16:42 +02:00
ShaharNaveh
e733b7ecf9 Update libc to 0.2.177 2025-11-27 17:15:02 +02:00
Jeong, YunWon
1e7a49036a try auto-format again (#6295) 2025-11-25 20:04:30 +09:00
dependabot[bot]
ea3eb2a9ef Bump actions/checkout from 4 to 6 (#6294)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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>
2025-11-25 05:30:13 +09:00
dependabot[bot]
817f91b5bf Bump xml from 1.0.1 to 1.2.0 (#6292)
Bumps [xml](https://github.com/kornelski/xml-rs) from 1.0.1 to 1.2.0.
- [Changelog](https://github.com/kornelski/xml-rs/blob/main/Changelog.md)
- [Commits](https://github.com/kornelski/xml-rs/compare/1.0.1...1.2.0)

---
updated-dependencies:
- dependency-name: xml
  dependency-version: 1.2.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>
2025-11-25 05:29:53 +09:00
Jiseok CHOI
f61b62e5a2 Ensure sqlite blob methods respect closed connections (#6290) 2025-11-24 00:04:43 +09:00
Jiseok CHOI
a9469a20d5 Fix sqlite connection reinitialization (#6288)
* Fix sqlite connection reinitialization

* Align sqlite connection reinit with CPython

* Enable sqlite test_connection_bad_reinit

* Fix sqlite reinit flag without threading

* Use stronger memory ordering for initialized flag synchronization
2025-11-22 22:13:06 +09:00
Jiseok CHOI
567fb4dec0 fix(sqlite): raise ProgrammingError when operating on a blob with a closed connection, (#6286)
Fixed #6285
2025-11-22 20:42:24 +09:00
Shahar Naveh
f7ddcd2795 Break after annotation future found (#6284) 2025-11-19 01:00:41 +09:00
Jeong, YunWon
bab03bd75b Merge pull request #6283 from youknowone/test-wasm32-without-js
Test wasm32 without js
2025-11-17 22:10:53 +09:00
Jeong YunWon
9134cca17b Make CI to run rustpython-without-js test 2025-11-17 21:30:28 +09:00
Jeong YunWon
eac8968f84 Add wasm runtime and fix the example code to actually run
Co-Authored-By: Valentyn Faychuk <valy@faychuk.com>
Co-Authored-By: Lee Dogeon <dev.moreal@gmail.com>
2025-11-17 21:30:28 +09:00
Jeong YunWon
5fb5db9617 relocate wasm test crate under example_projects 2025-11-17 18:58:07 +09:00
Shahar Naveh
916d3ba94b Move wasm/lib -> crates/wasm (#6280) 2025-11-17 18:47:05 +09:00
Shahar Naveh
88eca9693a Move stdlib -> crates/stdlib (#6268) 2025-11-16 23:25:45 +09:00
Jeong, YunWon
1a783fc9ec Replace SSL backend to rustls (#6244) 2025-11-16 22:17:35 +09:00
Lee Dogeon
7c4c1eabf0 Fix wasm32-unknown-unknown target build (#6278) 2025-11-16 22:16:34 +09:00
Shahar Naveh
1568d2a7fb Remove leftovers after vm crate move (#6279) 2025-11-16 19:15:32 +09:00
Shahar Naveh
93e4e42b53 Move compiler/source -> crates/compiler-source (#6261) 2025-11-16 19:14:35 +09:00
Jeong YunWon
6991a80e13 Add __main__.__cached__ 2025-11-15 23:09:31 +09:00
Jeong YunWon
4f8ef16937 Re-organize vm.run_script inner functions 2025-11-15 23:09:31 +09:00
Shahar Naveh
8715ae76f1 Move derive -> crates/derive (#6264) 2025-11-15 22:54:25 +09:00
Shahar Naveh
8968aeafb9 Move vm -> crates/vm (#6269) 2025-11-15 21:51:33 +09:00
Shahar Naveh
041dd30602 Move compiler/src -> crates/compiler (#6270) 2025-11-15 19:50:19 +09:00
Shahar Naveh
9e60940f1b Move vm/sre_engine -> crates/sre_engine (#6265) 2025-11-15 19:49:48 +09:00
Shahar Naveh
6479a2063c move derive-impl -> crates/derive-impl (#6263) 2025-11-15 19:49:00 +09:00
Shahar Naveh
cc2e84b9fc Move jit -> crates/jit (#6262) 2025-11-15 18:26:24 +09:00
Shahar Naveh
7f45ba4c9c Move compiler/codegen -> crates/codegen (#6260) 2025-11-15 18:25:46 +09:00
Jeong, YunWon
477c9b32f0 Fix OSError.__str__ to not display 'None' for filename (#6266) 2025-11-15 12:29:20 +09:00
Shahar Naveh
2071fa2e69 Move compiler/literal -> crates/literal (#6259)
* Move `compiler/literal` -> `crates/compiler-literal`

* Use correct crate name
2025-11-15 07:27:04 +09:00
Shahar Naveh
bb54c5b0e6 Move compiler-core -> crates/compiler-core (#6258) 2025-11-15 07:21:29 +09:00
Shahar Naveh
3728879baf Move wtf8 -> crates/wtf8 (#6257) 2025-11-15 07:21:04 +09:00
Shahar Naveh
4b7e49a17e Move common -> crates/common (#6256) 2025-11-15 07:19:53 +09:00
Shahar Naveh
5eac229eae Move pylib -> crates/pylib (#6225)
* Move `pylib

* clean `build.rs` a bit
2025-11-15 07:05:08 +09:00
Jeong, YunWon
609d99f1e3 implement sys.implementation cache_tag (#6255) 2025-11-14 23:55:17 +09:00
Shahar Naveh
db1adaa2c2 Move __doc__ crate to crates/doc (#6234)
* Add `__doc__` crate

* Base auto-generate ci

* Add dummy files

* Update docs

* Set codegen-units to 1 for doc db

* Mark `*.inc.rs` as auto generated

* Disable doctest

* Reset docs
2025-11-14 23:47:51 +09:00
Jiseok CHOI
9ce85862ce Add BaseException.add_note() and __notes__ attribute support (#6252) 2025-11-14 10:31:47 +09:00
dependabot[bot]
4562930233 Bump streetsidesoftware/cspell-action from 7 to 8 (#6246)
Bumps [streetsidesoftware/cspell-action](https://github.com/streetsidesoftware/cspell-action) from 7 to 8.
- [Release notes](https://github.com/streetsidesoftware/cspell-action/releases)
- [Changelog](https://github.com/streetsidesoftware/cspell-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/streetsidesoftware/cspell-action/compare/v7...v8)

---
updated-dependencies:
- dependency-name: streetsidesoftware/cspell-action
  dependency-version: '8'
  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>
2025-11-12 08:24:16 +09:00
Yash Suthar
0f8c0bc8a8 remove old auto-formte implimentaion (#6251)
Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
2025-11-11 07:35:08 +09:00
Yash Suthar
2d4617236e Update CI auto-formate (#6237)
---------

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
Co-authored-by: fanninpm <luxverdans@sbcglobal.net>
2025-11-10 23:46:14 +09:00
Jeong, YunWon
c9ba9560b5 new_buffer_error (#6243) 2025-11-10 20:58:08 +09:00
Shahar Naveh
9792001703 Add newtype of CodeUnits (#6241) 2025-11-10 09:48:25 +09:00
Jeong, YunWon
d8a4a09ec0 Fix #[pyclass(base=...)] notation (#6242) 2025-11-10 09:47:57 +09:00
fanninpm
ed9a61d956 Add builtin_items updater to whats_left job (#6238)
Corresponds to RustPython/rustpython.github.io#81.
2025-11-09 18:00:59 +09:00
Yash Suthar
5cad66cebf Revert "Update CI auto-format (#6233)" (#6236)
This reverts commit 377151a57f.
2025-11-05 03:14:27 +09:00
Yash Suthar
377151a57f Update CI auto-format (#6233)
---------

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
2025-11-04 17:37:22 +09:00
Shahar Naveh
a7e8ac684b Remove user defined docstrings (#6232) 2025-11-02 00:01:02 +09:00
Jiseok CHOI
e096ce7bc7 Implement property.__name__ attribute (#6230)
* Implement property.__name__ attribute

Add getter and setter for the __name__ attribute on property objects.
The getter returns the explicitly set name if available, otherwise
falls back to the getter function's __name__. Raises AttributeError
if no name is available, matching CPython 3.13 behavior.

The implementation handles edge cases:
- Returns None when explicitly set to None
- Propagates non-AttributeError exceptions from getter's __getattr__
- Raises property-specific AttributeError when getter lacks __name__

This fix enables test_property_name in test_property.py to pass.

* Refactor to use get_property_name in __name__ implementation

Consolidate duplicate logic by making name_getter() use the existing
get_property_name() helper method. This eliminates code duplication
and improves maintainability.

Changes:
- Update get_property_name() to return PyResult<Option<PyObjectRef>>
  to properly handle and propagate non-AttributeError exceptions
- Simplify name_getter() to delegate to get_property_name()
- Update format_property_error() to handle the new return type

This addresses review feedback about the relationship between
get_property_name() and __name__ implementation.

* style comment
2025-11-01 18:30:03 +09:00
Jiseok CHOI
9e7d291b63 Add callable validation to codecs.register_error (#6229)
Validate that the handler argument passed to codecs.register_error
is callable, raising TypeError with message 'handler must be callable'
if it is not. This matches CPython behavior.

This fix enables test_badregistercall in test_codeccallbacks.py to pass.
2025-10-31 00:08:04 +09:00
Jeong, YunWon
614028f9a8 more ssl impl (#6228) 2025-10-29 23:01:04 +09:00
Yash Suthar
8f048dd9fd Implement minimal builtins.anext() (#6226)
* Implement minimal builtins.anext()

* Removed expectedFailure for builtins.anext() tests

* Removed expectedFailure from test_contextlib_async tests fixed by anext

---------

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
2025-10-29 19:09:37 +09:00
Jeong, YunWon
fda9ceea54 Update ssl.py from CPython 3.13.9 (#6217)
* update ssl.py from CPython 3.13.9

* test_ssl

* mark failing tests

* remove test/*.pem

* fix certdata path

* unmark fixed tests
2025-10-28 13:19:12 +09:00
Jeong, YunWon
2acc3be6cf More SSL impl (#6224)
* fix ipv6 formattig

* consts

* fspath

* fix set_ecdh_curve

* minimum/maximum version

* Add SSL_CTX_security_level
2025-10-28 12:00:44 +09:00
Yash Suthar
b6e8a875ac Resolve number slots via MRO in PyNumber and operator, ensure inherit… (#6222)
* Resolve number slots via MRO in PyNumber and operator, ensure inherited and dynamically added methods are found.

Use class().mro_find_map() to mimic the same behaviour as CPython.

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
2025-10-28 12:00:22 +09:00
Christopher Gambrell
6b25fe5c95 5539 - CTRL+Z then Enter will now close shell on Windows. (#6223)
* CTRL+Z then Enter will now close shell on Windows.

* Additional comment.

* Use EOF const

* Add cfg(windows) for EOF_CHAR

---------

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2025-10-28 11:02:24 +09:00
dependabot[bot]
0e15e771c3 Bump actions/upload-artifact from 4 to 5 (#6221)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  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>
2025-10-28 09:37:37 +09:00
dependabot[bot]
d63e44aa3a Bump actions/download-artifact from 5 to 6 (#6220)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  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>
2025-10-28 09:37:14 +09:00
Jeong, YunWon
9a2792a44b PySSLCertificate (#6219) 2025-10-27 22:31:59 +09:00
Jeong YunWon
517b55b8b5 pyssl errors 2025-10-27 18:37:01 +09:00
Jeong YunWon
37915336ea Expand #[pyexception] macro working with module attr 2025-10-27 18:37:01 +09:00
Jeong, YunWon
2e7a8b4735 Implement more SSL methods (#6210)
* openssl-sys version

* selected_alpn_protocol

* get_ciphers

* get_channel_binding

* Add Tls1_1 and Tls1_2

* consts

* fix ssl truncate bug

* verify_flags

* shared ciphers

* verify_client_post_handshake

* shutdown

* get_verified_chain

* fix lints

* fix _wrap_socket

* Fix convert_openssl_error

* clean up ssl

* X509_check_ca

* set default verify flag

* Fix version

* fix import

* consts

* fix httplib
2025-10-26 19:19:24 +09:00
Yash Suthar
c6c931aa0b Fix ldexp to prevent math range errors (#6216)
* Fix ldexp to prevent math range errors

Implemented IEEE 754-compliant handling for large numbers to avoid
overflow and represent results using scientific notation.

Fixes #5471

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>

* Fix ldexp to handle denormalized inputs correctly

Detect denormalized input values below f64::MIN_POSITIVE
Scale subnormal inputs by 2^54 to bring them into normalized range

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>

* tests(math): remove expectedFailure from testLdexp_denormal

As now we have implemented IEEE 754 format , so this will pass.

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>

* Update Lib/test/test_math.py

Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>

* fixed formate in math.rs

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>

---------

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
Co-authored-by: Jeong, YunWon <69878+youknowone@users.noreply.github.com>
2025-10-26 17:33:49 +09:00
yt2b
1d23f6e8df Panic occurs when formatting with separator and some format specifier (#6213)
* Fix get_separator_interval

* Add extra tests

* Remove FormatType::Number from match arm
2025-10-24 21:41:28 +09:00
Jeong, YunWon
9825d8a376 ensurepip from Python 3.13.9 (#5740) 2025-10-23 19:53:58 +09:00
dependabot[bot]
79dcba8fe7 Bump actions/setup-python from 5 to 6 (#6159)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  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>
2025-10-23 19:05:37 +09:00
Jeong, YunWon
3ec905e08a ssl.{SSLSession,MemoryBIO} (#6209)
* SSLSession

* get_unverified_chain

* SSL MemoryBIO
2025-10-23 18:37:40 +09:00
Jeong, YunWon
2463bdff0e Fix time.strptime (#6208) 2025-10-23 17:28:53 +09:00
Shahar Naveh
153d0eef51 Revert "Use ruff for Expr unparsing (#6124)"
This reverts commit 0fb7d0fae2.
2025-10-22 22:34:52 +09:00
Jeong, YunWon
f22aed2614 Fix PyCode constructor/replace (#6193)
* Fix PyCode constructor

* Reuse MarshalError
2025-10-22 21:09:42 +09:00
Shahar Naveh
0fb7d0fae2 Use ruff for Expr unparsing (#6124)
* Use ruff for unparse backend

* Update `test_future_stmt/*.py` from 3.13.7

* Mark failing tests

* Mark failing test

* Merge remote-tracking branch 'upstream/main' into ruff-unparse

* Reapply ruff code

* remove git symbols

* Unmark passing test
2025-10-22 20:29:50 +09:00
Jeong, YunWon
efd3a4e44b Update Lib with changed files in 3.13.8 (#6186)
* Update changed files from 3.13.7 -> 3.13.8

* Reapply some patches

* Reaaply patches to `test_bytes.py`

* fix test markers in `test_exceptions.py`

* Patch `test_posix.py`

* Patched `test_{pyexpat,site,sysconfig}.py`

* Patched `test_typing.py`

* Patch failing tests in `test_typing.py`

* Update `seq_tests` from 3.13.8

* Mark failing tests in `test_genericalias.py`

* mark failing tests in `test_pyexpat.py`

* Mark failing tests in `test_posix.py`

* reapply patch

* mark failing tests

* skip flaky test
2025-10-22 20:28:05 +09:00
Shahar Naveh
5d9e62390c Update functools from 3.13.9 (#6205)
* Update `functools.py` from 3.13.9

* mark/unmark tests
2025-10-22 20:26:19 +09:00
ShaharNaveh
a50cc9b915 skip flaky test 2025-10-22 12:04:01 +03:00
ShaharNaveh
715529bef1 mark failing tests 2025-10-22 11:36:40 +03:00
ShaharNaveh
68e7310d22 reapply patch 2025-10-22 11:02:36 +03:00
ShaharNaveh
604b708741 Mark failing tests in test_posix.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
e069244f89 mark failing tests in test_pyexpat.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
296da56190 Mark failing tests in test_genericalias.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
624a561145 Update seq_tests from 3.13.8 2025-10-22 10:57:47 +03:00
ShaharNaveh
3f7deb49c8 Patch failing tests in test_typing.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
9557acb1c3 Patched test_typing.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
aa56ebb057 Patched test_{pyexpat,site,sysconfig}.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
84b254209f Patch test_posix.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
e18354b990 fix test markers in test_exceptions.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
360f8caead Reaaply patches to test_bytes.py 2025-10-22 10:57:47 +03:00
ShaharNaveh
9b400a9b6f Reapply some patches 2025-10-22 10:57:47 +03:00
ShaharNaveh
19b6241ef9 Update changed files from 3.13.7 -> 3.13.8 2025-10-22 10:57:47 +03:00
winlogon
b15e537692 Use PyStrRef for TypeAliasType name (#6203)
* fix(PyStrRef): fix TODO in typing.rs where PyObjectRef was used

* chore(fmt): apply rustfmt to code
2025-10-21 12:14:57 +09:00
Jiseok CHOI
2faa05dcfb Fix sqlite Connection initialization check (#6199)
* Fix sqlite3 Connection initialization check

Add proper __init__ validation for sqlite3.Connection to ensure base class
__init__ is called before using connection methods. This fixes the
test_connection_constructor_call_check test case.

Changes:
- Modified Connection.py_new to detect subclassing
- For base Connection class, initialization happens immediately in py_new
- For subclassed Connection, db is initialized as None
- Added __init__ method that performs actual database initialization
- Updated _db_lock error message to match CPython: 'Base Connection.__init__ not called.'

This ensures CPython compatibility where attempting to use a Connection
subclass instance without calling the base __init__ raises ProgrammingError.

* use Initializer trait
2025-10-21 11:11:31 +09:00
Jiseok CHOI
25a464eeae Fix sqlite3 Cursor initialization check (#6198)
Add proper __init__ validation for sqlite3.Cursor to ensure base class
__init__ is called before using cursor methods. This fixes the
test_cursor_constructor_call_check test case.

Changes:
- Modified Cursor to initialize with inner=None in py_new
- Added explicit __init__ method that sets up CursorInner
- Updated close() method to check for uninitialized state
- Changed error message to match CPython: 'Base Cursor.__init__ not called.'

This ensures CPython compatibility where attempting to use a Cursor
instance without calling the base __init__ raises ProgrammingError.
2025-10-21 09:33:55 +09:00
dependabot[bot]
13329f0a48 Bump actions/setup-node from 5 to 6 (#6197)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '6'
  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>
2025-10-21 09:32:19 +09:00
Shahar Naveh
fcf196935e Update ruff 0.14.1 (#6195)
* Update ruff to 0.14.1

* Fix test regression in `test_compile`

* Unmark passing test

* Update `test_syntax` from 3.13.9

---------

Co-authored-by: Noa <coolreader18@gmail.com>
2025-10-20 22:46:46 +09:00
Shahar Naveh
9a5d5d6194 Fix CI (#6196)
* Update `vcpkg` to 2025.09.17

* Pin selenium version

* Use `localhost` instead of `0.0.0.0`
2025-10-20 21:40:38 +09:00
fanninpm
3473d824a8 Replace skips in test_importlib/source/test_file_loader with expectedFailures (#6194)
* Replace skips with expectedFailure markings for SimpleTest

* Uncomment Source-PEP451 tests and apply similar monkey-patches as before

* Uncomment Source-PEP302 tests and apply similar monkey-patches as before

* Uncomment Sourceless-PEP451 tests and apply similar monkey-patches as
before

* Uncomment Sourceless-PEP302 tests and apply similar monkey-patches as
before
2025-10-20 17:32:05 +09:00
dependabot[bot]
3b48dcc7c1 Bump serde-wasm-bindgen from 0.3.1 to 0.6.5 (#6188)
Bumps [serde-wasm-bindgen](https://github.com/RReverser/serde-wasm-bindgen) from 0.3.1 to 0.6.5.
- [Commits](https://github.com/RReverser/serde-wasm-bindgen/commits/v0.6.5)

---
updated-dependencies:
- dependency-name: serde-wasm-bindgen
  dependency-version: 0.6.5
  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>
2025-10-13 11:03:49 +09:00
Anton
b56e469a5f Handle OsError in REPL (#6187) 2025-10-13 10:18:51 +09:00
Shahar Naveh
7986fee56f Revert "Pin CI image to windows-2025 (#6148)" (#6182)
This reverts commit 43d643ad09.
2025-10-12 12:55:46 +09:00
Shahar Naveh
c979059eeb Configure dependabot to ignore ruff updates (#6185)
* Make dependabot ignore ruff updates

* Regenrate Cargo.lock

* Fix clippy

* Fix typo
2025-10-12 12:55:23 +09:00
Shahar Naveh
3a6fda4daf Update opcode from 3.13.7 (#6156)
* Update `opcode` from 3.13.7

* Base `_opcode`

* Add `test__opcode.py` from 3.13.7

* Impl `has_*` methods

* Add more methods

* Update `dis.py` from 3.13.7

* Update `support/bytecode_helper.py` from 3.13.7

* correct is_valid

* Patch failing tests

* Unpatch `support/__init__.py`

* clippy

* Make comments to doc

* impl `_varname_from_oparg` for code

* Unmark passing tests

* Revert changes to `dis`

* Mark failing tests
2025-10-05 11:14:33 +09:00
dependabot[bot]
1aea1467da Bump on-headers, serve and compression in /wasm/demo (#6168)
Bumps [on-headers](https://github.com/jshttp/on-headers) to 1.1.0 and updates ancestor dependencies [on-headers](https://github.com/jshttp/on-headers), [serve](https://github.com/vercel/serve) and [compression](https://github.com/expressjs/compression). These dependencies need to be updated together.


Updates `on-headers` from 1.0.2 to 1.1.0
- [Release notes](https://github.com/jshttp/on-headers/releases)
- [Changelog](https://github.com/jshttp/on-headers/blob/master/HISTORY.md)
- [Commits](https://github.com/jshttp/on-headers/compare/v1.0.2...v1.1.0)

Updates `serve` from 14.2.4 to 14.2.5
- [Release notes](https://github.com/vercel/serve/releases)
- [Commits](https://github.com/vercel/serve/compare/14.2.4...v14.2.5)

Updates `compression` from 1.7.4 to 1.8.1
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/1.7.4...v1.8.1)

---
updated-dependencies:
- dependency-name: on-headers
  dependency-version: 1.1.0
  dependency-type: indirect
- dependency-name: serve
  dependency-version: 14.2.5
  dependency-type: direct:development
- dependency-name: compression
  dependency-version: 1.8.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-21 19:34:03 +09:00
Shahar Naveh
3c01be29c4 Update some tests to 3.13.7 (#6171)
* Update `test_call.py` from 3.13.7

* Update `test_yield_from.py` from 3.13.7

* Update more tests to 3.13.7

* More tests to 3.13.7

* Remove patch from passing test
2025-09-21 19:33:41 +09:00
Shahar Naveh
24f4fbad82 Run scheduled CI jobs only on upstream repo (#6157)
* Run scheduled CI jobs only on upstream repo

* Only disable if scheduling on forks
2025-09-21 02:05:32 +09:00
Shahar Naveh
30cbc41298 Update github actions in CI (#6169)
* Update `setup-python` to v6

* Update `checkout` to v5
2025-09-21 01:53:00 +09:00
dependabot[bot]
150e8ef43d Bump actions/download-artifact from 4 to 5 (#6162)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  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>
2025-09-21 00:01:06 +09:00
dependabot[bot]
4b91e985ac Bump ruff_python_ast from 0.11.0 to 0.13.1 (#6166)
Bumps [ruff_python_ast](https://github.com/astral-sh/ruff) from 0.11.0 to 0.13.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](2cd25ef641...706be0a6e7)

---
updated-dependencies:
- dependency-name: ruff_python_ast
  dependency-version: 706be0a6e7e09936511198f2ff8982915520d138
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-20 23:42:02 +09:00
dependabot[bot]
fdae128cec Bump actions/setup-node from 4 to 5 (#6160)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  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>
2025-09-20 23:41:28 +09:00
Shahar Naveh
11e1330758 Reconfigure dependabot (#6158)
* Add `cargo` to dependabot

* Remove noisy comments

* Don't group updates
2025-09-20 22:46:02 +09:00
Jeong, YunWon
aa0eb4bedf rustpython-common wasm_js feature (#6116) 2025-09-17 09:11:13 +09:00
Shahar Naveh
141ed72693 Dependencies cleanup (#6151)
* Update deps

* Remove some unused deps

* Update lockfile
2025-09-17 09:10:35 +09:00
Shahar Naveh
62067aefd3 Update uuid from 3.13.7 (#6155) 2025-09-16 21:58:36 +09:00
Shahar Naveh
b7d9d7d9ae Update test/test_fstring.py from 3.13.7 (#6154)
* Update `test_fstring.py` from 3.13.7

* Patch failing tests
2025-09-16 21:57:36 +09:00
Shahar Naveh
67958ec791 Update {io,encodings} from 3.13.7 (#6153)
* Update `io` from 3.13.7

* Patch test & upsate `encodings` from 3.13.7

* Unmark passing tests
2025-09-16 21:53:25 +09:00
Jeong, YunWon
b666c52df9 code object linetable (#6150)
* Code.replace

* implement linetable
2025-09-16 21:49:54 +09:00
Shahar Naveh
6ead82154e Update glob from 3.13.7 (#6152) 2025-09-16 09:35:08 +09:00
Shahar Naveh
ca95366219 Update fnmatch from 3.13.7 (#6149) 2025-09-15 21:36:11 +09:00
Shahar Naveh
43d643ad09 Pin CI image to windows-2025 (#6148)
For more info see see:
https://github.blog/changelog/2025-07-31-github-actions-new-apis-and-windows-latest-migration-notice/
2025-09-13 21:24:10 +09:00
Shahar Naveh
cc4ebe6256 Update{runpy,numbers}.py from 3.13.7 (#6141)
* Update number.py from 3.13.7

* Update `runpy.py` from 3.13.7
2025-09-11 22:43:11 +09:00
Shahar Naveh
f429ac4939 Use ast.unparse for generating patches with lib_updater.py (#6142)
* Use `ast.unparse` for decorator generation and every ut_method

* Ensure ut_method type for external patches

* use textwrap

* Apply patches to `test_os.py`

* Apoly on `test_xml_etree.py`

* Run on some test files

* Update `test_str.py`

* Update `test_logging.py` from 3.13.7
2025-09-11 22:42:19 +09:00
Shahar Naveh
0c3d14affc Fix docs link in copilot (#6145) 2025-09-11 14:05:59 +09:00
Shahar Naveh
63de4387e7 Fix broken CI on windows (#6143)
* Fix `test_dtrace.py`

* Fix `test_genericpath.py`

* fix `test_ntpath.py`

* Fix `test_py_compile.py`

* Fix `test_shutil.py`

* fix `test_stat.py`

* Fix `test_tarfile.py`

* Mark failing tests
2025-09-11 14:05:04 +09:00
Shahar Naveh
7044d43dc8 Update {site,sysconfig}.py from 3.13.7 (#6132)
* Update `{site,sysconfig}.py` from 3.13.7

* Update vm/src/stdlib/sysconfig.rs
2025-09-08 13:48:51 +09:00
Shahar Naveh
74c2d490ac Update zoneinfo and _strptime from 3.13.7 (#6139) 2025-09-07 17:09:55 +09:00
Shahar Naveh
59d71be85f Update test_collections.py from 3.13.7 (#6136) 2025-09-07 17:08:35 +09:00
Shahar Naveh
9da2e04880 Update html* from 3.13.7 (#6133) 2025-09-07 17:07:54 +09:00
Shahar Naveh
1d53e0c923 Update codecs from 3.13.7 (#6130) 2025-09-07 16:13:22 +09:00
Shahar Naveh
da71b92dd3 Pickle warning for itertools (#6129) 2025-09-07 16:10:22 +09:00
Jeong, YunWon
b640ef1241 Add comment about 6 params (#6125) 2025-09-07 16:06:49 +09:00
Shahar Naveh
c5c2bd050d Add tool for easier test updates (#6089)
* Add scripts/lib_updater.py

* Update `Lib/test/test_os.py` with tool

* Update `test_list.py` as well
2025-09-07 16:05:54 +09:00
Noa
85ca28094e Apply clippy suggestions to switch to let chains (#6126) 2025-09-04 15:34:10 +09:00
xgampx
48d8031f0c lookup slot in hash() (#6102)
* avoid get_class_attr for __hash__; read hash slot via mro_find_map

Reduce calls and lock acquisitions on hot paths by bypassing get_class_attr(__hash__) and directly resolving the hash implementation with mro_find_map(|cls| cls.slots.hash.load()).

* fix linting in hash function
2025-09-03 23:32:15 -05:00
Noa
0c8ae3a384 Update nix to 0.30 (#6120) 2025-09-04 08:44:27 +09:00
Shahar Naveh
056795eed4 Attenpt to automate posix consts (#6117) 2025-09-03 22:19:30 +09:00
Noa
cca4fe6d80 Switch to newer thread::LocalKey convenience methods (#6123) 2025-09-03 22:14:59 +09:00
Jeong, YunWon
d17dcd817e Merge pull request #6115 from ShaharNaveh/update-some-tests-2
Update some tests from 3.13.7
2025-09-03 00:27:57 +09:00
Shahar Naveh
1688e744ba fn unparse_expr -> UnparseExpr::new (#6121) 2025-09-02 23:55:42 +09:00
Shahar Naveh
8b6e1e398b Update test_itertools.py to 3.13.7 (#6122)
* Update `test_itertools.py` to 3.13.7

* Apply patch where test name was changed

* Fix some failing tests
2025-09-02 20:19:57 +09:00
Noa
fa91df6539 Merge pull request #6118 from ever0de/feat/sqlite-fetchmany-size-arg
sqlite3: Support 'size' keyword argument in `Cursor::fetchmany`
2025-09-01 14:17:35 -05:00
Noa
2b67f40c34 Merge pull request #6119 from ShaharNaveh/update-deps
Update dns-lookup and xml-rs (renamed to xml)
2025-09-01 12:50:41 -05:00
ShaharNaveh
1d1aa663f0 Trigger CI 2025-09-01 16:49:06 +03:00
ShaharNaveh
1fe5fd55d3 Update xml(-rs) to 1.0 2025-09-01 14:01:03 +03:00
ShaharNaveh
711f95ec09 Update dns-lookup to 3.0 2025-09-01 13:59:16 +03:00
ShaharNaveh
020692e56b Update lockfile 2025-09-01 11:28:04 +03:00
ShaharNaveh
de3cb8cdbb Mark more failing tests 2025-09-01 11:19:00 +03:00
Jiseok CHOI
2e16f51c68 use FromArgs 2025-09-01 15:43:07 +09:00
Jiseok CHOI
a2b194a6f8 sqlite3: Support 'size' keyword argument in Cursor::fetchmany 2025-09-01 15:23:04 +09:00
ShaharNaveh
373de5ee57 Update test_with.py from 3.13.7 2025-08-31 12:43:52 +03:00
ShaharNaveh
a1c11cdc40 Update test_fileio.py from 3.13.7 2025-08-31 12:37:48 +03:00
ShaharNaveh
41fb6c5a1a Add Lib/test/test_file_eintr.py from 3.13.7 2025-08-31 12:23:58 +03:00
ShaharNaveh
e00a95d15c Update test_userdict.py from 3.13.7 2025-08-31 12:14:40 +03:00
ShaharNaveh
d732c307dc Update test_univnewlines.py from 3.13.7 2025-08-31 12:13:09 +03:00
ShaharNaveh
6a3c348351 Update test_richcmp.py 2025-08-31 12:01:32 +03:00
ShaharNaveh
ec8f37dcd6 Update test_pprint.py from 3.13.7 2025-08-31 11:59:46 +03:00
ShaharNaveh
88506059f9 Update test_pow.py from 3.13.7 2025-08-31 11:58:21 +03:00
ShaharNaveh
15b1b62adb Update test_isinstance.py from 3.13.7 2025-08-31 11:50:04 +03:00
ShaharNaveh
6a4d4b727c Uodate test_grammar.py from 3.13.7 2025-08-31 11:47:32 +03:00
ShaharNaveh
d9ffc47c43 Update test_dynamic.py from 3.13.7 2025-08-31 11:09:34 +03:00
ShaharNaveh
ed6caed3d9 Update test_decorators.py from 3.13.7 2025-08-31 11:08:20 +03:00
ShaharNaveh
37324b443b Update test_eof.py from 3.13.7 2025-08-31 10:54:43 +03:00
ShaharNaveh
88b12bafc9 Update test_kqueue.py from 3.13.7 2025-08-31 10:35:23 +03:00
ShaharNaveh
b56082a980 Update test_keywordonlyarg.py from 3.13.7 2025-08-31 10:34:57 +03:00
Noa
75093873b8 Merge pull request #5789 from coolreader18/crt_fd-rework
Rework crt_fd to be more aligned with io-safety
2025-08-29 11:02:38 -05:00
Noa
8437b06dad Unmark passing tests 2025-08-29 10:59:53 -05:00
Noa
dc4be47751 Windows fixes 2025-08-29 10:59:53 -05:00
Noa
51cbf57470 Rework crt_fd to be more aligned with io-safety 2025-08-29 10:59:51 -05:00
Jeong, YunWon
1c992f84e5 Merge pull request #6110 from youknowone/pattern-mapping
More Pattern matching implementation mapping + class
2025-08-28 12:59:52 +09:00
Jack O'Connor
763d5d48b5 Add sorted.py to microbenchmarks (#6086)
* Add microbenchmark for `sorted`

I chose 5 * Iterations to try better show that RustPython
sort implementation scales noticeably worse CPython's
with respect to the number of elements.

* Mention how to run a specific benchmark

* Update python version in bench README

3.13 better reflects the current state of the project vs 3.7.
2025-08-28 09:58:20 +09:00
Jeong YunWon
f4543f5f51 Fix defaultdict 2025-08-26 21:49:26 +09:00
Jeong YunWon
be54bc0dfd Fix multiple inheritance 2025-08-26 21:49:10 +09:00
Jeong YunWon
b807bc7fc4 Fix patma guard 2025-08-26 21:48:46 +09:00
Jeong YunWon
21fb4aafcf apply review 2025-08-26 21:48:46 +09:00
Jeong YunWon
4b638011bb Add failing test markers 2025-08-26 21:48:46 +09:00
CPython Developers
a109a596c8 Import test_patma from CPython 3.13.7 2025-08-26 21:48:46 +09:00
Jeong YunWon
8ae2dc75f6 MATCH_SELF 2025-08-26 21:48:46 +09:00
Jeong YunWon
50c557419e more match pattern 2025-08-26 21:48:46 +09:00
Jeong, YunWon
c6d1a5784a Fix mkdir error args (#6114) 2025-08-26 20:46:22 +09:00
Jeong, YunWon
776cabb883 New Instruction ToBool,PopJumpIfFalse (#6112)
* New Instruction ToBool

* Rename JustIf{True,False} => PopJumpIf{...}
2025-08-26 16:12:14 +09:00
Jeong, YunWon
16cdcfb96f Fix PyNumber::boolean (#6111) 2025-08-26 15:16:47 +09:00
Jeong, YunWon
711b1a62d5 PyTypeFlags::{SEQUENCE,MAPPING} (#6109) 2025-08-26 10:42:05 +09:00
Jeong, YunWon
dae95849ea Update some tests from 3.13.7 (#6108)
* Update `test_opcache.py`

* Update test_optparse.py

* Add some missing folders & test files

* Update `test_long.py` and impl "is_integer" for int

* Update `support/hypothesis_helper.py` from 3.13.7

* Update test_binascii

* Update test_math

* Add `test_math_property.py`

* Update `test_property.py` from 3.13.7

* Update `test_cmath.py` from 3.13.7

* Unmark passing tests

* Update `test_ucn.py` from 3.13.7

* Mark failing tests

* Add `site-packages` dir
2025-08-24 17:44:31 +09:00
Shahar Naveh
5c6f92d497 Fix unused imports for android (#6106) 2025-08-24 17:04:09 +09:00
ShaharNaveh
e7c87969f0 Add site-packages dir 2025-08-24 10:36:07 +03:00
ShaharNaveh
6cb00e2ae9 Mark failing tests 2025-08-24 00:44:32 +03:00
ShaharNaveh
d28164c150 Update test_ucn.py from 3.13.7 2025-08-24 00:17:50 +03:00
ShaharNaveh
61bc6e8d1c Unmark passing tests 2025-08-23 23:21:19 +03:00
ShaharNaveh
6a232a8830 Update test_cmath.py from 3.13.7 2025-08-23 22:20:54 +03:00
ShaharNaveh
d82554124c Update test_property.py from 3.13.7 2025-08-23 22:13:48 +03:00
ShaharNaveh
1fbd1cd28f Add test_math_property.py 2025-08-23 22:09:11 +03:00
ShaharNaveh
7e03ec7812 Update test_math 2025-08-23 22:06:50 +03:00
ShaharNaveh
fa3ecba7a5 Update test_binascii 2025-08-23 21:53:29 +03:00
ShaharNaveh
2de20539a9 Update support/hypothesis_helper.py from 3.13.7 2025-08-23 21:46:28 +03:00
ShaharNaveh
933db1075f Update test_long.py and impl "is_integer" for int 2025-08-23 21:43:35 +03:00
ShaharNaveh
c0b3cc9048 Add some missing folders & test files 2025-08-23 19:45:53 +03:00
ShaharNaveh
713cb7043e Update test_optparse.py 2025-08-23 19:44:08 +03:00
ShaharNaveh
b4d086b540 Update test_opcache.py 2025-08-23 19:25:28 +03:00
Shahar Naveh
e3e0e8a364 Update base64.py from 3.13.6 (#6087) 2025-08-21 13:22:21 +09:00
Jiseok CHOI
e909e32f31 sqlite: Fix missing ProgrammingError for parameter mismatch (#6104) 2025-08-21 13:19:38 +09:00
Shahar Naveh
9417e1023d Update xml from 3.13.7 (#6100) 2025-08-21 13:15:39 +09:00
Jack O'Connor
109e64c2ba Allow multiple indented blocks in REPL (#6097)
There aren't any tests but being able to do nested for loops seems like
a pretty big win to me so I'm going to put up for review.

The original returned boolean clearly had **false positives** for
detecting bad errors for things like nested `if` and `for` statements.
What is less clear is if there are any **true positives** which I am no
longer catching with the updated return value.

Co-authored-by: Jack O'Connor <jack@jackoconnor.dev>
2025-08-21 13:15:02 +09:00
Shahar Naveh
ceb7046bc4 Fix int respect sys.set_int_max_str_digits value (#6094) 2025-08-21 13:14:10 +09:00
Jeong, YunWon
bfc513e997 Fix future clippy warnings (#6103) 2025-08-20 17:34:29 +09:00
Shahar Naveh
527ce3a872 Update Lib/test/test_float.py from 3.13.7 (#6099)
* Update `Lib/test/test_float.py` from 3.13.7

* Update mathdata

* Unmark passing tests
2025-08-20 14:16:19 +09:00
Lee Dogeon
44a8c9f0b3 Remove completed TODO of extra_tests/fstring.py (#6095) 2025-08-20 14:08:53 +09:00
Shahar Naveh
e6001a48d7 Update netrc.py from 3.13.6 and make pwd accesible on Android (#6083) 2025-08-20 14:08:05 +09:00
Shahar Naveh
242814fa72 Update locale.py from 3.13.6 and made _locale available on android (#6091) 2025-08-20 13:44:57 +09:00
Jeong, YunWon
ddc08498cc Fix match mapping pattern (#6081) 2025-08-20 13:26:18 +09:00
Jeong, YunWon
a9a9e3bf11 Merge pull request #6085 from youknowone/dict-update 2025-08-09 07:41:13 +09:00
Jeong YunWon
d56fcd0774 DictUpdate instruction 2025-08-08 23:53:40 +09:00
Jack O'Connor
33ea50c2e9 Add return annotation to __annotations__ last (#6071)
Functions like `functools.singledispatch` are sensitive to the order of
items in the `__annotations__` map.

CPython puts returns last.
2025-08-08 23:31:12 +09:00
Jiseok CHOI
e922722191 Implement type check in member descriptor __set__ (#6080) 2025-08-08 19:45:15 +09:00
Jeong, YunWon
158c027c23 Rust 1.89 clippy fix (#6082) 2025-08-08 15:00:18 +09:00
Shahar Naveh
133aada0b7 Update os.py from 3.13.5 (#6076)
* Update `os.py` from 3.13.5

* Set availablity of some `os` functions

* revert some cfg

* Mark more failing tests
2025-08-08 14:37:35 +09:00
Jeong, YunWon
4ae5a1f894 Fix ImportError fields (#6079) 2025-08-07 18:29:05 +09:00
Jack O'Connor
93eacdac20 Update zipapp.py from 3.13.5 (#6075) 2025-08-06 10:31:55 +09:00
Shahar Naveh
cac4948afe Update rustyline & socket2 (#6074)
* Update rustyline to 17.0.0

* Update dns-lookup and socket2

* run `cargo update`
2025-08-06 10:31:21 +09:00
Jack O'Connor
b480d234dd Reorder struct lconv members to match locale.h (#6073)
`struct lconv` in locale.h
https://codebrowser.dev/glibc/glibc/locale/locale.h.html#lconv::int_p_cs_precedes.

Order of relevant section in glibc locale.h
```C
char int_p_cs_precedes;
char int_p_sep_by_space;
char int_n_cs_precedes;
char int_n_sep_by_space;
```
2025-08-06 10:30:23 +09:00
Shahar Naveh
91979a3d0e Update {nt,posix}path.py from 3.13.5 (#6070)
* Update `{nt,posix}path.py` from 3.13.5

* Mark failing tests
2025-08-05 23:18:10 +09:00
Shahar Naveh
f5a77a1f68 Update difflib.py from 3.13.5 (#6067) 2025-08-05 17:53:34 +09:00
Jiseok CHOI
a58d582001 Implement unsupported ops for sqlite3.Blob (#6066) 2025-08-05 17:53:06 +09:00
Shahar Naveh
c4a805107f Update genericpath.py from 3.13.5 (#6065) 2025-08-04 20:10:02 +09:00
Shahar Naveh
72fc3c0ba4 Update pickle{tools,}.py from 3.13.5 (#6064) 2025-08-04 20:09:36 +09:00
Shahar Naveh
566d9aabae Update gettext.py from 3.13.5 (#6063) 2025-08-04 20:08:35 +09:00
Shahar Naveh
18a9bf0caf Update configparser.py from 3.13.5 (#6062) 2025-08-04 20:08:17 +09:00
Shahar Naveh
4841776856 Update contextlib from 3.13.5, (#6056)
* Update `contextlib` from 3.13.5

* Add `test_contextlib_async.py`
2025-08-01 22:39:00 +09:00
Shahar Naveh
710941c27f Update bz2.py from 3.13.5 (#6055) 2025-08-01 22:16:32 +09:00
Shahar Naveh
2d65c7f859 Update some deps (#6053) 2025-08-01 22:14:56 +09:00
Jeong, YunWon
92fdfc4c37 Prevent direct instantiation of sqlite3.{Statement,Blob} (#6052)
* Prevent direct instantiation of sqlite3.{Statement,Blob}

* Use `Unconstructible` trait for internal types
2025-08-01 22:13:19 +09:00
Shahar Naveh
7f1fc3602f Add symtable.py from 3.13.5 (#6048)
* Add `symtable.py` from 3.13.5

* Update symtable methods

* Correct `type` return type
2025-08-01 22:12:11 +09:00
Jiseok CHOI
ec0a2325e4 Use Unconstructible trait for internal types 2025-08-01 18:04:15 +09:00
Jeong, YunWon
c3754cdca2 Merge pull request #6049 from youknowone/fix-support
Fix test.support.requires_debug_ranges
2025-08-01 17:52:58 +09:00
Jiseok CHOI
b2d6594bd9 Prevent direct instantiation of sqlite3.{Statement,Blob} 2025-08-01 13:39:45 +09:00
Jeong YunWon
f8891ffe3a Fix test_compile 2025-08-01 09:17:08 +09:00
Jeong YunWon
36cc6d1945 Backport CPython gh-137195
https://github.com/python/cpython/pull/137195
2025-08-01 09:16:34 +09:00
Shahar Naveh
f32a5b105a Update unittest partially (#6051)
* Update `test_unittest` from 3.13.5

* Remove old `test_unittest.py`
2025-07-31 10:53:39 +09:00
Ashwin Naren
1c55f9eee2 Add test.support.interpreters at 3.13.2 (#5684) 2025-07-30 20:20:07 +09:00
Jiseok CHOI
1e6da5f430 sqlite: Align Connection.__call__ error handling with CPython (#6042) 2025-07-30 14:05:17 +09:00
Jeong, YunWon
cee579e7ea Merge pull request #6047 from youknowone/wtf8 2025-07-30 12:45:03 +09:00
Shahar Naveh
4bf32a04f4 Apply some clipy lints (#6045) 2025-07-30 12:16:02 +09:00
Jeong YunWon
9583af057b Apply PyUtf8Str 2025-07-30 12:14:47 +09:00
Jeong YunWon
d46c882347 remove try_to_str
Rewrite sqlite3 UTF8 validation
2025-07-30 12:14:47 +09:00
Jeong YunWon
053cfeecce downcastable_from 2025-07-30 12:02:37 +09:00
Jeong, YunWon
f402deef6d Deprecate ::new_ref (#6046) 2025-07-30 01:09:21 +09:00
Jeong, YunWon
59a8a569dd Wtf8-compatible fixes (#5985)
* deprecate more payload_* functions

* loose trait bount for PyInterned

* Fix levenstein

* Fix genericalias

* Fix PyBoundMethod::repr

* fix repr

* Fix fromhex
2025-07-30 01:03:12 +09:00
Shahar Naveh
57029f6efa Derive Default for GroupByState (#6043) 2025-07-29 00:55:13 +09:00
Jiseok CHOI
d8f1d188c3 stdlib(sqlite): Raise ProgrammingError in closed Blob context manager (#6041) 2025-07-27 21:47:05 +09:00
Jeong, YunWon
89c58d678a Merge pull request #6037 from ShaharNaveh/update-support-init
Update `Lib/test/support/__init__.py` from 3.13.5
2025-07-27 10:50:52 +09:00
Jiseok CHOI
38ca076cb5 feat(stdlib/sqlite): Implement slice assignment for Blob (#6039) 2025-07-27 10:43:11 +09:00
ShaharNaveh
69f6423424 Try to have time_ns same as std 2025-07-26 20:01:21 +02:00
ShaharNaveh
d5793e04ec Don't import unittest.mock in test_int.py 2025-07-26 17:48:07 +02:00
ShaharNaveh
cbe975818e Remove extra spaces 2025-07-26 17:47:53 +02:00
ShaharNaveh
06196fa4f4 Merge remote-tracking branch 'upstream/main' into update-support-init 2025-07-26 17:31:26 +02:00
ShaharNaveh
0d1a02583a Have time_ns for wasi 2025-07-26 17:30:46 +02:00
Jack O'Connor
4079776c36 Add kde function and tests to RustPython statistics module (#6030)
* Copy CPython 3.13 statistics module into RustPython

* Adjust CPython "magic constants" in KDE tests

## test_kde

I'm not too sure why but this one takes a few seconds to run the second
for loop which calculates the cumulative distribution and does a rough
calculation of the area under the curve.

## test_kde_random

I have a lower bound for RustPython to sort a random list of 1_000_000
numbers on my laptop of > 1 hour. By dropping n to 30_000 sort will not
take an egregious amount of time to run. It is then necessary to lower
the tolerance for the math.isclose check, or the computed values may
**randomly** fail due to the higher variance caused by the smaller
sample size.

* Reintroduce expected failure in test_statistics.TestNormalDict.test_slots

* Sync Rust `normal_dist_inv_cdf` with Python equivalent

See https://github.com/python/cpython/pull/95265.

To quote:
> Restores alignment with random.gauss(mu, sigma) and
random.normalvariate(mu, sigma) both. of which are equivalent to
sampling from NormalDist(mu, sigma).inv_cdf(random()). The two functions
in the random module happy accept sigma=0 and give a well-defined
result.

> This also lets the function gently handle a sigma getting smaller,
eventually becoming zero. As sigma decrease, NormalDist(mu,
sigma).inv_cdf(p) forms a tighter and tighter internal around mu and
becoming exactly mu in the limit. For example, NormalDist(100,
1E-300).inv_cdf(0.3) cleanly evaluates to 100.0but withsigma=1e-500``
the function previously would raised an unexpected error.
2025-07-26 00:42:22 +09:00
ShaharNaveh
b829333f1d Fix patch of gc_threshold 2025-07-25 16:59:37 +02:00
ShaharNaveh
0e3ff8ae5f Fix patch of disable_gc 2025-07-25 16:58:25 +02:00
ShaharNaveh
73f5ceb79b Update test_int.py 2025-07-25 16:20:36 +02:00
ShaharNaveh
a5b240aab8 skip crashing test 2025-07-25 16:13:07 +02:00
ShaharNaveh
0648e975d9 Upsate test_dictviews 2025-07-25 16:07:59 +02:00
ShaharNaveh
5d9f9acb1d Update test_collections.py 2025-07-25 16:06:00 +02:00
ShaharNaveh
26cdbfe048 Mark tests correctly 2025-07-25 15:59:14 +02:00
ShaharNaveh
17e60754f6 Fix patching of support 2025-07-25 15:47:48 +02:00
ShaharNaveh
bb08398957 Update test_userlist.py 2025-07-25 15:47:32 +02:00
ShaharNaveh
0d1a68dfab mark failing tests in test_picklebuffer.py 2025-07-25 15:41:37 +02:00
ShaharNaveh
7f97034055 Mark failing tests 2025-07-25 14:53:03 +02:00
ShaharNaveh
409f5dda9f Add more code to support/__init__.py 2025-07-25 14:53:03 +02:00
ShaharNaveh
68cd33f37e Update pickle tests from 3.13.5 2025-07-25 14:53:03 +02:00
ShaharNaveh
4fb5736694 Add run_doctest 2025-07-25 14:53:03 +02:00
ShaharNaveh
b51f6de0c8 Reapply RustPython patches 2025-07-25 14:53:03 +02:00
ShaharNaveh
3058d99fd5 Copy parts of old code to make libregtest work 2025-07-25 14:53:03 +02:00
ShaharNaveh
74201365c6 Update Lib/test/support/__init__.py from 3.13.5 2025-07-25 14:53:03 +02:00
Shahar Naveh
c232b7f1f8 Update csv.py from 3.13.5 (#6035)
* Use `raise_if_stop!` macro

* Update `csv.py` from 3.13.5

* Mark failing tests
2025-07-25 19:08:01 +09:00
Shahar Naveh
ae03bacb39 Update {_py,}decimal.py from 3.13.5 (#6034) 2025-07-25 19:04:49 +09:00
Jiseok CHOI
fb9147736d stdlib(sqlite3): Raise ProgrammingError for missing named parameter (#6036) 2025-07-25 19:02:56 +09:00
Shahar Naveh
9499d39f55 Update html from 3.13.5 (#6031) 2025-07-25 10:58:21 +09:00
Shahar Naveh
6a9579efc7 Update trace.py from 3.13.5 (#6029) 2025-07-25 10:50:41 +09:00
Shahar Naveh
8621b3d7da Added pyclbr from 3.13.5 (#6028) 2025-07-25 10:50:17 +09:00
Shahar Naveh
24f2524e6e Added "os.wait" tests from 3.13.5 (#6027)
* Added "wait" tests from 3.13.5

* Trigger CI
2025-07-25 10:49:05 +09:00
Shahar Naveh
74bee7cbbe Update codeop.py from 3.13.5 (#6026) 2025-07-25 10:47:20 +09:00
Shahar Naveh
01edb93957 Resolve libc::RLIM_NLIMITS warning on android (#6025)
* Resolve warning on android

* Apply CodeRabbit suggestion
2025-07-25 10:46:32 +09:00
Jiseok CHOI
bcf56279ec Raise UnicodeEncodeError for surrogates in sqlite.executescript (#6024) 2025-07-25 10:45:33 +09:00
Shahar Naveh
6bce5e1616 Cleanup vm/src/stdlib/stat.rs (#6018) 2025-07-25 10:44:49 +09:00
Shahar Naveh
b7336366cb Update test_enumerate.py from 3.13.5 (#6032) 2025-07-25 10:43:16 +09:00
Shahar Naveh
96f47a415e Export ruff_source_file types in rustpython_compiler_core (#6020)
* export ruff types in `compiler::core`

* Use exported ruff types in `vm/**`

* unlink `ruff_source_file` as a direct dependency
2025-07-23 12:28:38 +09:00
Shahar Naveh
582e25b11b Update tabnanny.py from 3.13.5 (#6021) 2025-07-23 12:27:56 +09:00
Shahar Naveh
d897f9e0e0 Unpin Rust nightly in CI (#6022) 2025-07-23 12:27:41 +09:00
Shahar Naveh
9995cc60b5 Update warnings from 3.13.5 (#6019) 2025-07-23 12:27:26 +09:00
Shahar Naveh
ba22ad2c0c Replace compiler::source module with ruff_source_file (#6016)
* Replace `compiler::source`` with ruff

* Require `ruff_source_file`
2025-07-22 20:52:25 +09:00
Jiseok CHOI
57bdf35ee6 Enforce valid UTF-8 encoding for sqlite collation names (#6015)
* Make public `PyStr::ensure_valid_utf8`

* Enforce valid UTF-8 encoding for sqlite collation names
2025-07-21 23:42:47 +09:00
Jiseok CHOI
bbe98ddd86 Construct detailed message on text decode failure (#6014) 2025-07-21 22:33:09 +09:00
Jeong, YunWon
52395497dd Update test_typing from Python 3.13.5 (#6013) 2025-07-21 21:14:25 +09:00
2041 changed files with 440770 additions and 128944 deletions

View File

@@ -3,3 +3,6 @@ rustflags = "-C link-arg=/STACK:8000000"
[target.'cfg(all(target_os = "windows", not(target_env = "msvc")))']
rustflags = "-C link-args=-Wl,--stack,8000000"
[target.wasm32-unknown-unknown]
rustflags = ["--cfg=getrandom_backend=\"wasm_js\""]

View File

@@ -0,0 +1,46 @@
---
allowed-tools: Bash(container *), Bash(cargo *), Read, Grep, Glob
---
# Run Tests in Linux Container (Apple `container` CLI)
Run RustPython tests inside a Linux container using Apple's `container` CLI.
**NEVER use Docker, Podman, or any other container runtime.** Only use the `container` command.
## Arguments
- `$ARGUMENTS`: Test command to run (e.g., `test_io`, `test_codecs -v`, `test_io -v -m "test_errors"`)
## Prerequisites
The `container` CLI is installed via `brew install container`.
The dev image `rustpython-dev` is already built.
## Steps
1. **Check if the container is already running**
```shell
container list 2>/dev/null | grep rustpython-test
```
2. **Start the container if not running**
```shell
container run -d --name rustpython-test -m 8G -c 4 \
--mount type=bind,source=/Users/al03219714/Projects/RustPython3,target=/workspace \
-w /workspace rustpython-dev sleep infinity
```
3. **Run the test inside the container**
```shell
container exec rustpython-test sh -c "cargo run --release -- -m test $ARGUMENTS"
```
4. **Report results**
- Show test summary (pass/fail counts, expected failures, unexpected successes)
- Highlight any new failures compared to macOS results if available
- Do NOT stop or remove the container after testing (keep it for reuse)
## Notes
- The workspace is bind-mounted, so local code changes are immediately available
- Use `container exec rustpython-test sh -c "..."` for any command inside the container
- To rebuild after code changes, run: `container exec rustpython-test sh -c "cargo build --release"`
- To stop the container when done: `container rm -f rustpython-test`

View File

@@ -0,0 +1,49 @@
---
allowed-tools: Bash(python3:*), Bash(cargo run:*), Read, Grep, Glob, Bash(git add:*), Bash(git commit:*), Bash(cargo fmt:*), Bash(git diff:*), Task
---
# Investigate Test Failure
Investigate why a specific test is failing and determine if it can be fixed or needs an issue.
## Arguments
- `$ARGUMENTS`: Failed test identifier (e.g., `test_inspect.TestGetSourceBase.test_getsource_reload`)
## Steps
1. **Analyze failure cause**
- Read the test code
- Analyze failure message/traceback
- Check related RustPython code
2. **Verify behavior in CPython**
- Run the test with `python3 -m unittest` to confirm expected behavior
- Document the expected output
3. **Determine fix feasibility**
- **Simple fix** (import issues, small logic bugs): Fix code → Run `cargo fmt --all` → Pre-commit review → Commit
- **Complex fix** (major unimplemented features): Collect issue info and report to user
**Pre-commit review process**:
- Run `git diff` to see the changes
- Use Task tool with `general-purpose` subagent to review:
- Compare implementation against cpython/ source code
- Verify the fix aligns with CPython behavior
- Check for any missed edge cases
- Proceed to commit only after review passes
4. **For complex issues - Collect issue information**
Following `.github/ISSUE_TEMPLATE/report-incompatibility.md` format:
- **Feature**: Description of missing/broken Python feature
- **Minimal reproduction code**: Smallest code that reproduces the issue
- **CPython behavior**: Result when running with python3
- **RustPython behavior**: Result when running with cargo run
- **Python Documentation link**: Link to relevant CPython docs
Report collected information to the user. Issue creation is done only upon user request.
Example issue creation command:
```
gh issue create --template report-incompatibility.md --title "..." --body "..."
```

View File

@@ -0,0 +1,33 @@
---
allowed-tools: Skill(upgrade-pylib), Bash(gh pr list:*)
---
# Upgrade Next Python Library
Find the next Python library module ready for upgrade and run `/upgrade-pylib` for it.
## Current TODO Status
!`cargo run --release -- scripts/update_lib todo 2>/dev/null`
## Open Upgrade PRs
!`gh pr list --search "Update in:title" --json number,title --template '{{range .}}#{{.number}} {{.title}}{{"\n"}}{{end}}'`
## Instructions
From the TODO list above, find modules matching these patterns (in priority order):
1. `[ ] [no deps]` - Modules with no dependencies (can be upgraded immediately)
2. `[ ] [0/n]` - Modules where all dependencies are already upgraded (e.g., `[0/3]`, `[0/5]`)
These patterns indicate modules that are ready to upgrade without blocking dependencies.
**Important**: Skip any modules that already have an open PR in the "Open Upgrade PRs" list above.
**After identifying a suitable module**, run:
```
/upgrade-pylib <module_name>
```
If no modules match these criteria, inform the user that all eligible modules have dependencies that need to be upgraded first.

View File

@@ -0,0 +1,157 @@
---
allowed-tools: Bash(git add:*), Bash(git commit:*), Bash(python3 scripts/update_lib quick:*), Bash(python3 scripts/update_lib auto-mark:*)
---
# Upgrade Python Library from CPython
Upgrade a Python standard library module from CPython to RustPython.
## Arguments
- `$ARGUMENTS`: Library name to upgrade (e.g., `inspect`, `asyncio`, `json`)
## Important: Report Tool Issues First
If during the upgrade process you encounter any of the following issues with `scripts/update_lib`:
- A feature that should be automated but isn't supported
- A bug or unexpected behavior in the tool
- Missing functionality that would make the upgrade easier
**STOP the upgrade and report the issue first.** Describe:
1. What you were trying to do
- Library name
- The full command executed (e.g. python scripts/update_lib quick cpython/Lib/$ARGUMENTS.py)
2. What went wrong or what's missing
3. Expected vs actual behavior
This helps improve the tooling for future upgrades.
## Steps
1. **Run quick upgrade with update_lib**
- Run: `python3 scripts/update_lib quick $ARGUMENTS` (module name)
- Or: `python3 scripts/update_lib quick cpython/Lib/$ARGUMENTS.py` (library file path)
- Or: `python3 scripts/update_lib quick cpython/Lib/$ARGUMENTS/` (library directory path)
- This will:
- Copy library files (delete existing `Lib/$ARGUMENTS.py` or `Lib/$ARGUMENTS/`, then copy from `cpython/Lib/`)
- Patch test files preserving existing RustPython markers
- Run tests and auto-mark new test failures (not regressions)
- Remove `@unittest.expectedFailure` from tests that now pass
- Create a git commit with the changes
- **Handle warnings**: If you see warnings like `WARNING: TestCFoo does not exist in remote file`, it means the class structure changed and markers couldn't be transferred automatically. These need to be manually restored in step 2 or added in step 3.
2. **Review git diff and restore RUSTPYTHON-specific changes**
- Run `git diff Lib/test/test_$ARGUMENTS` to review all changes
- **Only restore changes that have explicit `RUSTPYTHON` comments**. Look for:
- `# XXX: RUSTPYTHON` or `# XXX RUSTPYTHON` - Comments marking RustPython-specific code modifications
- `# TODO: RUSTPYTHON` - Comments marking tests that need work
- Code changes with inline `# ... RUSTPYTHON` comments
- **Do NOT restore other diff changes** - these are likely upstream CPython changes, not RustPython-specific modifications
- When restoring, preserve the original context and formatting
3. **Investigate test failures with subagent**
- First, get dependent tests using the deps command:
```
cargo run --release -- scripts/update_lib deps $ARGUMENTS
```
- Look for the line `- [ ] $ARGUMENTS: test_xxx test_yyy ...` to get the direct dependent tests
- Run those tests to collect failures:
```
cargo run --release -- -m test test_xxx test_yyy ... 2>&1 | grep -E "^(FAIL|ERROR):"
```
- For example, if deps output shows `- [ ] linecache: test_bdb test_inspect test_linecache test_traceback test_zipimport`, run:
```
cargo run --release -- -m test test_bdb test_inspect test_linecache test_traceback test_zipimport 2>&1 | grep -E "^(FAIL|ERROR):"
```
- For each failure, use the Task tool with `general-purpose` subagent to investigate:
- Subagent should follow the `/investigate-test-failure` skill workflow
- Pass the failed test identifier as the argument (e.g., `test_inspect.TestGetSourceBase.test_getsource_reload`)
- If subagent can fix the issue easily: fix and commit
- If complex issue: subagent collects issue info and reports back (issue creation on user request only)
- Using subagent prevents context pollution in the main conversation
4. **Mark remaining test failures with auto-mark**
- Run: `python3 scripts/update_lib auto-mark Lib/test/test_$ARGUMENTS.py --mark-failure`
- Or for directory: `python3 scripts/update_lib auto-mark Lib/test/test_$ARGUMENTS/ --mark-failure`
- This will:
- Run tests and mark ALL failing tests with `@unittest.expectedFailure`
- Remove `@unittest.expectedFailure` from tests that now pass
- **Note**: The `--mark-failure` flag marks all failures including regressions. Review the changes before committing.
5. **Handle panics manually**
- If any tests cause panics/crashes (not just assertion failures), they need `@unittest.skip` instead:
```python
@unittest.skip("TODO: RUSTPYTHON; panics with 'index out of bounds'")
def test_crashes(self):
...
```
- auto-mark cannot detect panics automatically - check the test output for crash messages
6. **Handle class-specific failures**
- If a test fails only in the C implementation (TestCFoo) but passes in the Python implementation (TestPyFoo), or vice versa, move the marker to the specific subclass:
```python
# Base class - no marker here
class TestFoo:
def test_something(self):
...
class TestPyFoo(TestFoo, PyTest): pass
class TestCFoo(TestFoo, CTest):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_something(self):
return super().test_something()
```
7. **Commit the test fixes**
- Run: `git add -u && git commit -m "Mark failing tests"`
- This creates a separate commit for the test markers added in steps 2-6
## Example Usage
```
# Using module names (recommended)
/upgrade-pylib inspect
/upgrade-pylib json
/upgrade-pylib asyncio
# Using library paths (alternative)
/upgrade-pylib cpython/Lib/inspect.py
/upgrade-pylib cpython/Lib/json/
```
## Example: Restoring RUSTPYTHON changes
When git diff shows removed RUSTPYTHON-specific code like:
```diff
-# XXX RUSTPYTHON: we don't import _json as fresh since...
-cjson = import_helper.import_fresh_module('json') #, fresh=['_json'])
+cjson = import_helper.import_fresh_module('json', fresh=['_json'])
```
You should restore the RustPython version:
```python
# XXX RUSTPYTHON: we don't import _json as fresh since...
cjson = import_helper.import_fresh_module('json') #, fresh=['_json'])
```
## Notes
- The cpython/ directory should contain the CPython source that we're syncing from
- `scripts/update_lib` package handles patching and auto-marking:
- `quick` - Combined patch + auto-mark (recommended)
- `migrate` - Only migrate (patch), no test running
- `auto-mark` - Only run tests and mark failures
- `copy-lib` - Copy library files (not tests)
- The patching:
- Transfers `@unittest.expectedFailure` and `@unittest.skip` decorators with `TODO: RUSTPYTHON` markers
- Adds `import unittest # XXX: RUSTPYTHON` if needed for the decorators
- **Limitation**: If a class was restructured (e.g., method overrides removed), update_lib will warn and skip those markers
- The smart auto-mark:
- Marks NEW test failures automatically (tests that didn't exist before)
- Does NOT mark regressions (existing tests that now fail) - these are warnings
- Removes `@unittest.expectedFailure` from tests that now pass
- The script does NOT preserve all RustPython-specific changes - you must review `git diff` and restore them
- Common RustPython markers to look for:
- `# XXX: RUSTPYTHON` or `# XXX RUSTPYTHON` - Inline comments for code modifications
- `# TODO: RUSTPYTHON` - Test skip/failure markers
- Any code with `RUSTPYTHON` in comments that was removed in the diff
- **Important**: Not all changes in the git diff need to be restored. Only restore changes that have explicit `RUSTPYTHON` comments. Other changes are upstream CPython updates.

50
.claude/scripts/setup-env.sh Executable file
View File

@@ -0,0 +1,50 @@
#!/bin/bash
# Claude Code web session startup script
# Sets up the development environment for RustPython
set -e
cd /home/user/RustPython
echo "=== RustPython dev environment setup ==="
# 1. Ensure python3 points to 3.13+ (needed for scripts/update_lib)
# /usr/local/bin takes precedence over /usr/bin in PATH,
# so we update the symlink there directly.
CURRENT_PY=$(python3 --version 2>&1 | grep -oP '\d+\.\d+')
if [ "$(printf '%s\n' "3.13" "$CURRENT_PY" | sort -V | head -1)" != "3.13" ]; then
echo "Upgrading python3 default to 3.13..."
# Find best available Python >= 3.13
TARGET=""
for ver in python3.14 python3.13; do
if command -v "$ver" &>/dev/null; then
TARGET=$(command -v "$ver")
break
fi
done
if [ -n "$TARGET" ]; then
# Override /usr/local/bin/python3 if it exists and is outdated
if [ -e /usr/local/bin/python3 ]; then
sudo ln -sf "$TARGET" /usr/local/bin/python3
fi
# Also set /usr/bin via update-alternatives
sudo update-alternatives --install /usr/bin/python3 python3 "$TARGET" 3 2>/dev/null || true
sudo update-alternatives --set python3 "$TARGET" 2>/dev/null || true
echo "python3 now: $(python3 --version)"
else
echo "WARNING: No Python 3.13+ found. scripts/update_lib may not work."
fi
else
echo "python3 already >= 3.13: $(python3 --version)"
fi
# 2. Clone CPython source if not present (needed for scripts/update_lib)
if [ ! -d "cpython" ]; then
echo "Cloning CPython v3.14.3 (shallow)..."
git clone --depth 1 --branch v3.14.3 https://github.com/python/cpython.git cpython
echo "CPython source ready."
else
echo "CPython source already present."
fi
echo "=== Setup complete ==="

15
.claude/settings.json Normal file
View File

@@ -0,0 +1,15 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "bash .claude/scripts/setup-env.sh"
}
]
}
]
}
}

View File

@@ -1,61 +1,233 @@
ADDOP
aftersign
argdefs
argtypes
asdl
asname
attro
augassign
badcert
badsyntax
baseinfo
basetype
binop
bltin
boolop
BUFMAX
BUILDSTDLIB
bxor
byteswap
cached_tsver
cadata
cafile
calldepth
callinfo
callproc
capath
carg
cellarg
cellvar
cellvars
ceval
cfield
CLASSDEREF
classdict
cmpop
codedepth
CODEUNIT
CONIN
CONOUT
constevaluator
consti
CONVFUNC
convparam
copyslot
cpucount
datastack
defaultdict
denom
deopt
dictbytype
DICTFLAG
dictoffset
distpoint
dynload
elts
eofs
evalloop
excepthandler
exceptiontable
fastlocal
fastlocals
fblock
fblocks
fdescr
ffi_argtypes
fielddesc
fieldlist
fileutils
finalbody
finalizers
firsttraceable
flowgraph
formatfloat
freelist
freevar
freevars
fromlist
getdict
getfunc
getiter
getsets
getslice
globalgetvar
HASARRAY
HASBITFIELD
HASPOINTER
HASSTRUCT
HASUNION
heaptype
hexdigit
HIGHRES
IFUNC
IMMUTABLETYPE
INCREF
inlinedepth
inplace
ismine
ISPOINTER
iteminfo
Itertool
keeped
kwnames
kwonlyarg
kwonlyargs
lasti
libffi
linearise
lineiterator
linetable
loadfast
localsplus
Lshift
lsprof
MAXBLOCKS
maxdepth
metavars
miscompiles
mult
multibytecodec
nameobj
nameop
ncells
nconsts
newargs
newfree
NEWLOCALS
newsemlockobject
nfrees
nkwargs
nkwelts
nlocalsplus
Nondescriptor
noninteger
nops
noraise
nseen
NSIGNALS
numer
opname
opnames
orelse
outparam
outparm
paramfunc
parg
pathconfig
patma
peepholer
phcount
platstdlib
posonlyarg
posonlyargs
prec
preinitialized
pybuilddir
pycore
pyinner
pydecimal
Pyfunc
pylifecycle
pymain
pyrepl
PYTHONTRACEMALLOC
PYTHONUTF8
pythonw
PYTHREAD_NAME
releasebuffer
repr
resinfo
Rshift
SA_ONSTACK
saveall
scls
setdict
setfunc
setprofileallthreads
SETREF
setresult
setslice
settraceallthreads
SLOTDEFINED
SMALLBUF
SOABI
SSLEOF
stackdepth
stackref
staticbase
stginfo
storefast
stringlib
structseq
subkwargs
subparams
subscr
sval
swappedbytes
sysdict
templatelib
testconsole
threadstate
ticketer
tmptype
tok_oldval
tstate
tvars
typeobject
typeparam
Typeparam
typeparams
typeslots
unaryop
uncollectable
Unhandle
unparse
unparser
untracking
VARKEYWORDS
varkwarg
venvlauncher
venvlaunchert
venvw
venvwlauncher
venvwlaunchert
wbits
weakreflist
weakrefobject
webpki
winconsoleio
withitem
withs
worklist
xstat
XXPRIME
XXPRIME

View File

@@ -1,9 +1,13 @@
abiflags
abstractmethods
addcompare
aenter
aexit
aiter
altzone
anext
anextawaitable
annotationlib
appendleft
argcount
arrayiterator
@@ -22,6 +26,7 @@ breakpointhook
cformat
chunksize
classcell
classmethods
closefd
closesocket
codepoint
@@ -30,6 +35,8 @@ codesize
contextvar
cpython
cratio
ctype
ctypes
dealloc
debugbuild
decompressor
@@ -65,12 +72,15 @@ fnctl
frombytes
fromhex
fromunicode
frozensets
fset
fspath
fstring
fstrings
ftruncate
genexpr
genexpressions
getargs
getattro
getcodesize
getdefaultencoding
@@ -80,14 +90,17 @@ getformat
getframe
getframemodulename
getnewargs
getopt
getpip
getrandom
getrecursionlimit
getrefcount
getsizeof
getswitchinterval
getweakref
getweakrefcount
getweakrefs
getweakrefs
getwindowsversion
gmtoff
groupdict
@@ -100,8 +113,12 @@ idxs
impls
indexgroup
infj
inittab
Inittab
instancecheck
instanceof
interpchannels
interpqueues
irepeat
isabstractmethod
isbytes
@@ -126,6 +143,7 @@ listcomp
longrange
lvalue
mappingproxy
markupbase
maskpri
maxdigits
MAXGROUPS
@@ -141,17 +159,20 @@ mformat
mro
mros
multiarch
mymodule
namereplace
nanj
nbytes
ncallbacks
ndigits
ndim
needsfree
nldecoder
nlocals
NOARGS
nonbytes
Nonprintable
onceregistry
origname
ospath
pendingcr
@@ -166,14 +187,21 @@ profilefunc
pycache
pycodecs
pycs
pydatetime
pyexpat
pyio
pymain
PYTHONAPI
PYTHONBREAKPOINT
PYTHONDEBUG
PYTHONDONTWRITEBYTECODE
PYTHONFAULTHANDLER
PYTHONHASHSEED
PYTHONHOME
PYTHONINSPECT
PYTHONINTMAXSTRDIGITS
PYTHONIOENCODING
PYTHONNODEBUGRANGES
PYTHONNOUSERSITE
PYTHONOPTIMIZE
PYTHONPATH
@@ -195,6 +223,7 @@ readbuffer
reconstructor
refcnt
releaselevel
reraised
reverseitemiterator
reverseiterator
reversekeyiterator
@@ -211,10 +240,13 @@ scproxy
seennl
setattro
setcomp
setprofileallthreads
setrecursionlimit
setswitchinterval
settraceallthreads
showwarnmsg
signum
sitebuiltins
slotnames
STACKLESS
stacklevel
@@ -223,14 +255,17 @@ startpos
subclassable
subclasscheck
subclasshook
subclassing
suboffset
suboffsets
SUBPATTERN
subpatterns
sumprod
surrogateescape
surrogatepass
sysconf
sysconfigdata
sysdict
sysvars
teedata
thisclass
@@ -257,6 +292,7 @@ warnopts
weaklist
weakproxy
weakrefs
weakrefset
winver
withdata
xmlcharrefreplace

View File

@@ -5,7 +5,9 @@ biguint
bindgen
bitand
bitflags
bitflagset
bitor
bitvec
bitxor
bstr
byteorder
@@ -28,6 +30,7 @@ hexf
hexversion
idents
illumos
ilog
indexmap
insta
keccak
@@ -50,17 +53,20 @@ nanos
nonoverlapping
objclass
peekable
pemfile
powc
powf
powi
prepended
punct
replacen
retag
rmatch
rposition
rsplitn
rustc
rustfmt
rustls
rustyline
seedable
seekfrom
@@ -80,7 +86,9 @@ unsync
wasip1
wasip2
wasmbind
wasmer
wasmtime
widestring
winapi
winresource
winsock

View File

@@ -0,0 +1,32 @@
cfgs
miri
py
pyarg
pyargs
pyast
pyattr
pyclass
pyclassmethod
pyexception
pyfunction
pygetset
pyimpl
pylib
pymath
pymethod
pymodule
pyname
pyobj
pyobject
pypayload
pyref
pyslot
pystaticmethod
pystone
pystr
pystruct
pystructseq
pytype
rustix
struc
zelf

View File

@@ -9,6 +9,7 @@
"@cspell/dict-win32/cspell-ext.json",
"@cspell/dict-shell/cspell-ext.json",
],
"allowCompoundWords": true,
// language - current active spelling language
"language": "en",
// dictionaries - list of the names of the dictionaries to use
@@ -16,6 +17,7 @@
"cpython", // Sometimes keeping same terms with cpython is easy
"python-more", // Python API terms not listed in python
"rust-more", // Rust API terms not listed in rust
"rustpython", // RustPython derive macros and internal terms
"en_US",
"softwareTerms",
"c",
@@ -38,6 +40,10 @@
{
"name": "rust-more",
"path": "./.cspell.dict/rust-more.txt"
},
{
"name": "rustpython",
"path": "./.cspell.dict/rustpython.txt"
}
],
"ignorePaths": [
@@ -46,97 +52,44 @@
"Lib/**"
],
// words - list of words to be always considered correct
// (compound words like pyarg, baseclass, microbenchmark are handled by allowCompoundWords)
"words": [
"RUSTPYTHONPATH",
// RustPython terms
"aiterable",
"alnum",
"baseclass",
"boxvec",
"Bytecode",
"cfgs",
"codegen",
"coro",
"dedentations",
"dedents",
"deduped",
"deoptimize",
"downcastable",
"downcasted",
"dumpable",
"emscripten",
"excs",
"finalizer",
"GetSet",
"groupref",
"internable",
"interps",
"jitted",
"jitting",
"kwonly",
"lossily",
"makeunicodedata",
"miri",
"notrace",
"openat",
"pyarg",
"pyarg",
"pyargs",
"pyast",
"PyAttr",
"mcache",
"oparg",
"pyc",
"PyClass",
"PyClassMethod",
"PyException",
"PyFunction",
"pygetset",
"pyimpl",
"pylib",
"pymath",
"pymember",
"PyMethod",
"PyModule",
"pyname",
"pyobj",
"PyObject",
"pypayload",
"PyProperty",
"pyref",
"PyResult",
"pyslot",
"PyStaticMethod",
"pystone",
"pystr",
"pystruct",
"pystructseq",
"pytrace",
"reducelib",
"richcompare",
"RustPython",
"significand",
"struc",
"summands", // plural of summand
"sysmodule",
"tracebacks",
"typealiases",
"unconstructible",
"unhashable",
"uninit",
"summands",
"unraisable",
"unresizable",
"wasi",
"zelf",
"weaked",
// unix
"posixshmem",
"shm",
"CLOEXEC",
"codeset",
"endgrent",
"gethrvtime",
"getrusage",
"nanosleep",
"sigaction",
"WRLCK",
// win32
"birthtime",
"IFEXEC",
// "stat"
"FIRMLINK"
"IFEXEC"
],
// flagWords - list of words to be always considered incorrect
"flagWords": [

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/vscode/devcontainers/rust:1-bullseye
FROM rust:bullseye
# Install clang
RUN apt-get update \

65
.gitattributes vendored
View File

@@ -4,4 +4,67 @@ Cargo.lock linguist-generated
vm/src/stdlib/ast/gen.rs linguist-generated -merge
Lib/*.py text working-tree-encoding=UTF-8 eol=LF
**/*.rs text working-tree-encoding=UTF-8 eol=LF
*.pck binary
crates/rustpython_doc_db/src/*.inc.rs linguist-generated=true
# Binary data types
*.aif binary
*.aifc binary
*.aiff binary
*.au binary
*.bmp binary
*.exe binary
*.icns binary
*.gif binary
*.ico binary
*.jpg binary
*.pck binary
*.pdf binary
*.png binary
*.psd binary
*.tar binary
*.wav binary
*.whl binary
*.zip binary
# Text files that should not be subject to eol conversion
[attr]noeol -text
Lib/test/cjkencodings/* noeol
Lib/test/tokenizedata/coding20731.py noeol
Lib/test/decimaltestdata/*.decTest noeol
Lib/test/test_email/data/*.txt noeol
Lib/test/xmltestdata/* noeol
# Shell scripts should have LF even on Windows because of Cygwin
Lib/venv/scripts/common/activate text eol=lf
Lib/venv/scripts/posix/* text eol=lf
# CRLF files
[attr]dos text eol=crlf
# Language aware diff headers
# https://tekin.co.uk/2020/10/better-git-diff-output-for-ruby-python-elixir-and-more
# https://gist.github.com/tekin/12500956bd56784728e490d8cef9cb81
*.css diff=css
*.html diff=html
*.py diff=python
*.md diff=markdown
# Generated files
# https://github.com/github/linguist/blob/master/docs/overrides.md
#
# To always hide generated files in local diffs, mark them as binary:
# $ git config diff.generated.binary true
#
[attr]generated linguist-generated=true diff=generated
Lib/_opcode_metadata.py generated
Lib/keyword.py generated
Lib/idlelib/help.html generated
Lib/test/certdata/*.pem generated
Lib/test/certdata/*.0 generated
Lib/test/levenshtein_examples.json generated
Lib/test/test_stable_abi_ctypes.py generated
Lib/token.py generated
.github/workflows/*.lock.yml linguist-generated=true merge=ours

View File

@@ -0,0 +1,49 @@
# This action installs a few dependencies necessary to build RustPython on Linux.
# It can be configured depending on which libraries are needed:
#
# ```
# - uses: ./.github/actions/install-linux-deps
# with:
# gcc-multilib: true
# musl-tools: false
# ```
#
# See the `inputs` section for all options and their defaults. Note that you must checkout the
# repository before you can use this action.
#
# This action will only install dependencies when the current operating system is Linux. It will do
# nothing on any other OS (macOS, Windows).
name: Install Linux dependencies
description: Installs the dependencies necessary to build RustPython on Linux.
inputs:
gcc-multilib:
description: Install gcc-multilib (gcc-multilib)
required: false
default: "false"
musl-tools:
description: Install musl-tools (musl-tools)
required: false
default: "false"
gcc-aarch64-linux-gnu:
description: Install gcc-aarch64-linux-gnu (gcc-aarch64-linux-gnu)
required: false
default: "false"
clang:
description: Install clang (clang)
required: false
default: "false"
runs:
using: composite
steps:
- name: Install Linux dependencies
shell: bash
if: ${{ runner.os == 'Linux' }}
run: >
sudo apt-get update
sudo apt-get install --no-install-recommends
${{ fromJSON(inputs.gcc-multilib) && 'gcc-multilib' || '' }}
${{ fromJSON(inputs.musl-tools) && 'musl-tools' || '' }}
${{ fromJSON(inputs.clang) && 'clang' || '' }}
${{ fromJSON(inputs.gcc-aarch64-linux-gnu) && 'gcc-aarch64-linux-gnu linux-libc-dev-arm64-cross libc6-dev-arm64-cross' || '' }}

View File

@@ -0,0 +1,47 @@
# This action installs a few dependencies necessary to build RustPython on macOS. By default it installs
# autoconf, automake and libtool, but can be configured depending on which libraries are needed:
#
# ```
# - uses: ./.github/actions/install-macos-deps
# with:
# openssl: true
# libtool: false
# ```
#
# See the `inputs` section for all options and their defaults. Note that you must checkout the
# repository before you can use this action.
#
# This action will only install dependencies when the current operating system is macOS. It will do
# nothing on any other OS (Linux, Windows).
name: Install macOS dependencies
description: Installs the dependencies necessary to build RustPython on macOS.
inputs:
autoconf:
description: Install autoconf (autoconf)
required: false
default: "true"
automake:
description: Install automake (automake)
required: false
default: "true"
libtool:
description: Install libtool (libtool)
required: false
default: "true"
openssl:
description: Install openssl (openssl@3)
required: false
default: "false"
runs:
using: composite
steps:
- name: Install macOS dependencies
shell: bash
if: ${{ runner.os == 'macOS' }}
run: >
brew install
${{ fromJSON(inputs.autoconf) && 'autoconf' || '' }}
${{ fromJSON(inputs.automake) && 'automake' || '' }}
${{ fromJSON(inputs.libtool) && 'libtool' || '' }}
${{ fromJSON(inputs.openssl) && 'openssl@3' || '' }}

14
.github/aw/actions-lock.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"entries": {
"actions/github-script@v8": {
"repo": "actions/github-script",
"version": "v8",
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
},
"github/gh-aw/actions/setup@v0.43.22": {
"repo": "github/gh-aw/actions/setup",
"version": "v0.43.22",
"sha": "fe858c3e14589bf396594a0b106e634d9065823e"
}
}
}

152
.github/dependabot.yml vendored
View File

@@ -1,13 +1,149 @@
# Keep GitHub Actions up to date with GitHub's Dependabot...
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
# cspell:ignore manyhow tinyvec zeroize
version: 2
updates:
- package-ecosystem: github-actions
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly
groups:
criterion:
patterns:
- "criterion*"
crypto:
patterns:
- "digest"
- "md-5"
- "sha-1"
- "sha2"
- "sha3"
- "blake2"
- "hmac"
- "pbkdf2"
futures:
patterns:
- "futures*"
get-size2:
patterns:
- "get-size*2"
iana-time-zone:
patterns:
- "iana-time-zone*"
jiff:
patterns:
- "jiff*"
lexical:
patterns:
- "lexical*"
libffi:
patterns:
- "libffi*"
malachite:
patterns:
- "malachite*"
manyhow:
patterns:
- "manyhow*"
num:
patterns:
- "num-bigint"
- "num-complex"
- "num-integer"
- "num-iter"
- "num-rational"
- "num-traits"
num_enum:
patterns:
- "num_enum*"
openssl:
patterns:
- "openssl*"
parking_lot:
patterns:
- "parking_lot*"
phf:
patterns:
- "phf*"
plotters:
patterns:
- "plotters*"
portable-atomic:
patterns:
- "portable-atomic*"
pyo3:
patterns:
- "pyo3*"
quote-use:
patterns:
- "quote-use*"
random:
patterns:
- "getrandom"
- "mt19937"
- "rand*"
rayon:
patterns:
- "rayon*"
regex:
patterns:
- "regex*"
result-like:
patterns:
- "result-like*"
security-framework:
patterns:
- "security-framework*"
serde:
patterns:
- "serde"
- "serde_core"
- "serde_derive"
system-configuration:
patterns:
- "system-configuration*"
thiserror:
patterns:
- "thiserror*"
time:
patterns:
- "time*"
tinyvec:
patterns:
- "tinyvec*"
tls_codec:
patterns:
- "tls_codec*"
toml:
patterns:
- "toml*"
wasm-bindgen:
patterns:
- "wasm-bindgen*"
wasmtime:
patterns:
- "cranelift*"
- "wasmtime*"
webpki-root:
patterns:
- "webpki-root*"
windows:
patterns:
- "windows*"
zerocopy:
patterns:
- "zerocopy*"
zeroize:
patterns:
- "zeroize*"
ignore:
# TODO: Remove when we use ruff from crates.io
# for some reason dependabot only updates the Cargo.lock file when dealing
# with git dependencies. i.e. not updating the version in Cargo.toml
- dependency-name: "ruff_*"
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: npm
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly

View File

@@ -16,95 +16,17 @@ concurrency:
cancel-in-progress: true
env:
CARGO_ARGS: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl
# Skip additional tests on Windows. They are checked on Linux and MacOS.
# test_glob: many failing tests
# test_io: many failing tests
# test_os: many failing tests
# test_pathlib: support.rmtree() failing
# test_posixpath: OSError: (22, 'The filename, directory name, or volume label syntax is incorrect. (os error 123)')
# test_venv: couple of failing tests
WINDOWS_SKIPS: >-
test_glob
test_io
test_os
test_rlcompleter
test_pathlib
test_posixpath
test_venv
# PLATFORM_INDEPENDENT_TESTS are tests that do not depend on the underlying OS. They are currently
# only run on Linux to speed up the CI.
PLATFORM_INDEPENDENT_TESTS: >-
test__colorize
test_array
test_asyncgen
test_binop
test_bisect
test_bool
test_bytes
test_call
test_class
test_cmath
test_collections
test_complex
test_contains
test_copy
test_dataclasses
test_decimal
test_decorators
test_defaultdict
test_deque
test_dict
test_dictcomps
test_dictviews
test_dis
test_enumerate
test_exception_variations
test_float
test_format
test_fractions
test_genericalias
test_genericclass
test_grammar
test_range
test_index
test_int
test_int_literal
test_isinstance
test_iter
test_iterlen
test_itertools
test_json
test_keyword
test_keywordonlyarg
test_list
test_long
test_longexp
test_math
test_operator
test_ordered_dict
test_pow
test_raise
test_richcmp
test_scope
test_set
test_slice
test_sort
test_string
test_string_literals
test_strtod
test_structseq
test_subclassinit
test_super
test_syntax
test_tuple
test_types
test_unary
test_unpack
test_weakref
test_yield_from
CARGO_ARGS: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl-rustls,host_env
CARGO_ARGS_NO_SSL: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,host_env
# Crates excluded from workspace builds:
# - rustpython_wasm: requires wasm target
# - rustpython-compiler-source: deprecated
# - rustpython-venvlauncher: Windows-only
WORKSPACE_EXCLUDES: --exclude rustpython_wasm --exclude rustpython-compiler-source --exclude rustpython-venvlauncher
# Python version targeted by the CI.
PYTHON_VERSION: "3.13.1"
PYTHON_VERSION: "3.14.3"
X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR: C:\Program Files\OpenSSL\lib\VC\x64\MD
X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR: C:\Program Files\OpenSSL\include
jobs:
rust_tests:
@@ -112,118 +34,131 @@ jobs:
env:
RUST_BACKTRACE: full
name: Run rust tests
runs-on: ${{ matrix.os }}
timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 30 }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
os: [macos-latest, ubuntu-latest, windows-2025]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Set up the Windows environment
shell: bash
run: |
git config --system core.longpaths true
cargo install --target-dir=target -v cargo-vcpkg
cargo vcpkg -v build
if: runner.os == 'Windows'
- name: Set up the Mac environment
run: brew install autoconf automake libtool
if: runner.os == 'macOS'
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install macOS dependencies
uses: ./.github/actions/install-macos-deps
- name: run clippy
run: cargo clippy ${{ env.CARGO_ARGS }} --workspace --all-targets --exclude rustpython_wasm -- -Dwarnings
run: cargo clippy ${{ env.CARGO_ARGS }} --workspace --all-targets ${{ env.WORKSPACE_EXCLUDES }} -- -Dwarnings
- name: run rust tests
run: cargo test --workspace --exclude rustpython_wasm --verbose --features threading ${{ env.CARGO_ARGS }}
if: runner.os != 'macOS'
- name: run rust tests
run: cargo test --workspace --exclude rustpython_wasm --exclude rustpython-jit --verbose --features threading ${{ env.CARGO_ARGS }}
if: runner.os == 'macOS'
run: cargo test --workspace ${{ env.WORKSPACE_EXCLUDES }} --verbose --features threading ${{ env.CARGO_ARGS }}
- name: check compilation without threading
run: cargo check ${{ env.CARGO_ARGS }}
- name: check compilation without host_env (sandbox mode)
run: |
cargo check -p rustpython-vm --no-default-features --features compiler
cargo check -p rustpython-stdlib --no-default-features --features compiler
cargo build --no-default-features --features stdlib,importlib,stdio,encodings,freeze-stdlib
if: runner.os == 'Linux'
- name: sandbox smoke test
run: |
target/debug/rustpython extra_tests/snippets/sandbox_smoke.py
target/debug/rustpython extra_tests/snippets/stdlib_re.py
if: runner.os == 'Linux'
- name: Test openssl build
run: cargo build --no-default-features --features ssl-openssl
if: runner.os == 'Linux'
# - name: Install tk-dev for tkinter build
# run: sudo apt-get update && sudo apt-get install -y tk-dev
# if: runner.os == 'Linux'
# - name: Test tkinter build
# run: cargo build --features tkinter
# if: runner.os == 'Linux'
- name: Test example projects
run:
run: |
cargo run --manifest-path example_projects/barebone/Cargo.toml
cargo run --manifest-path example_projects/frozen_stdlib/Cargo.toml
if: runner.os == 'Linux'
- name: prepare AppleSilicon build
uses: dtolnay/rust-toolchain@stable
with:
target: aarch64-apple-darwin
if: runner.os == 'macOS'
- name: Check compilation for Apple Silicon
run: cargo check --target aarch64-apple-darwin
if: runner.os == 'macOS'
- name: prepare iOS build
uses: dtolnay/rust-toolchain@stable
with:
target: aarch64-apple-ios
if: runner.os == 'macOS'
- name: Check compilation for iOS
run: cargo check --target aarch64-apple-ios
if: runner.os == 'macOS'
- name: run update_lib tests
run: cargo run -- -m unittest discover -s scripts/update_lib/tests -v
env:
PYTHONPATH: scripts
if: runner.os == 'Linux'
exotic_targets:
cargo_check:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
name: Ensure compilation on various targets
runs-on: ubuntu-latest
timeout-minutes: 30
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
targets:
- aarch64-linux-android
- i686-unknown-linux-gnu
- i686-unknown-linux-musl
- wasm32-wasip2
- x86_64-unknown-freebsd
dependencies:
gcc-multilib: true
musl-tools: true
- os: ubuntu-latest
targets:
- aarch64-unknown-linux-gnu
dependencies:
gcc-aarch64-linux-gnu: true # conflict with `gcc-multilib`
- os: macos-latest
targets:
- aarch64-apple-ios
- x86_64-apple-darwin
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
target: i686-unknown-linux-gnu
persist-credentials: false
- name: Install gcc-multilib and musl-tools
run: sudo apt-get update && sudo apt-get install gcc-multilib musl-tools
- name: Check compilation for x86 32bit
run: cargo check --target i686-unknown-linux-gnu
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
prefix-key: v0-rust-${{ join(matrix.targets, '-') }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Install dependencies
uses: ./.github/actions/install-linux-deps
# zizmor has an issue with dynamic `with`
# with: ${{ matrix.dependencies || fromJSON('{}') }}
with:
gcc-multilib: ${{ matrix.dependencies.gcc-multilib || false }}
musl-tools: ${{ matrix.dependencies.musl-tools || false }}
gcc-aarch64-linux-gnu: ${{ matrix.dependencies.gcc-aarch64-linux-gnu || false }}
- uses: dtolnay/rust-toolchain@stable
with:
target: aarch64-linux-android
targets: ${{ join(matrix.targets, ',') }}
- name: Check compilation for android
run: cargo check --target aarch64-linux-android
- uses: dtolnay/rust-toolchain@stable
- name: Setup Android NDK
if: ${{ contains(matrix.targets, 'aarch64-linux-android') }}
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
target: aarch64-unknown-linux-gnu
- name: Install gcc-aarch64-linux-gnu
run: sudo apt install gcc-aarch64-linux-gnu
- name: Check compilation for aarch64 linux gnu
run: cargo check --target aarch64-unknown-linux-gnu
- uses: dtolnay/rust-toolchain@stable
with:
target: i686-unknown-linux-musl
- name: Check compilation for musl
run: cargo check --target i686-unknown-linux-musl
- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-unknown-freebsd
- name: Check compilation for freebsd
run: cargo check --target x86_64-unknown-freebsd
- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-unknown-freebsd
- name: Check compilation for freeBSD
run: cargo check --target x86_64-unknown-freebsd
ndk-version: r27
add-to-path: true
# - name: Prepare repository for redox compilation
# run: bash scripts/redox/uncomment-cargo.sh
@@ -233,104 +168,201 @@ jobs:
# command: check
# args: --ignore-rust-version
- name: Check compilation
run: |
for target in ${{ join(matrix.targets, ' ') }}
do
echo "::group::${target}"
cargo check --target $target ${{ env.CARGO_ARGS_NO_SSL }}
echo "::endgroup::"
done
env:
CC_aarch64_linux_android: ${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang
AR_aarch64_linux_android: ${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER: ${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang
snippets_cpython:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
env:
RUST_BACKTRACE: full
# Tests that can be flaky when running with multiple processes `-j 2`. We will use `-j 1` for these.
FLAKY_MP_TESTS: >-
test_class
test_eintr
test_multiprocessing_fork
test_multiprocessing_forkserver
test_multiprocessing_spawn
name: Run snippets and cpython tests
runs-on: ${{ matrix.os }}
timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 30 }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
include:
- os: macos-latest
extra_test_args:
- '-u all'
env_polluting_tests: []
skips: []
timeout: 50
- os: ubuntu-latest
extra_test_args:
- '-u all'
env_polluting_tests: []
skips: []
timeout: 60
- os: windows-2025
extra_test_args: [] # TODO: Enable '-u all'
env_polluting_tests: []
skips:
- test_rlcompleter
- test_pathlib # panic by surrogate chars
- test_posixpath # OSError: (22, 'The filename, directory name, or volume label syntax is incorrect. (os error 123)')
- test_venv # couple of failing tests
timeout: 50
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v5
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up the Windows environment
shell: bash
run: |
git config --system core.longpaths true
cargo install cargo-vcpkg
cargo vcpkg build
if: runner.os == 'Windows'
- name: Set up the Mac environment
run: brew install autoconf automake libtool openssl@3
if: runner.os == 'macOS'
- name: build rustpython
run: cargo build --release --verbose --features=threading ${{ env.CARGO_ARGS }}
if: runner.os == 'macOS'
- name: build rustpython
run: cargo build --release --verbose --features=threading ${{ env.CARGO_ARGS }},jit
if: runner.os != 'macOS'
- uses: actions/setup-python@v5
- name: Install macOS dependencies
uses: ./.github/actions/install-macos-deps
with:
python-version: ${{ env.PYTHON_VERSION }}
openssl: true
- name: build rustpython
run: cargo build --release --verbose --features=threading,jit ${{ env.CARGO_ARGS }}
- name: run snippets
run: python -m pip install -r requirements.txt && pytest -v
working-directory: ./extra_tests
- if: runner.os == 'Linux'
name: run cpython platform-independent tests
run:
target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed -v ${{ env.PLATFORM_INDEPENDENT_TESTS }}
- if: runner.os == 'Linux'
name: run cpython platform-dependent tests (Linux)
run: target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
- if: runner.os == 'macOS'
name: run cpython platform-dependent tests (MacOS)
run: target/release/rustpython -m test -j 1 --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
- if: runner.os == 'Windows'
name: run cpython platform-dependent tests (windows partial - fixme)
run:
target/release/rustpython -m test -j 1 --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }} ${{ env.WINDOWS_SKIPS }}
- name: Detect available cores
id: cores
shell: bash
run: |
cores=$(python -c 'print(__import__("os").process_cpu_count())')
echo "cores=${cores}" >> $GITHUB_OUTPUT
- name: Run CPython tests
run: |
target/release/rustpython -m test -j ${{ steps.cores.outputs.cores }} ${{ join(matrix.extra_test_args, ' ') }} --slowest --fail-env-changed --timeout 600 -v -x ${{ env.FLAKY_MP_TESTS }} ${{ join(matrix.skips, ' ') }}
timeout-minutes: ${{ matrix.timeout }}
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
- name: Run flaky MP CPython tests
run: |
target/release/rustpython -m test -j 1 ${{ join(matrix.extra_test_args, ' ') }} --slowest --fail-env-changed --timeout 600 -v ${{ env.FLAKY_MP_TESTS }}
timeout-minutes: ${{ matrix.timeout }}
env:
RUSTPYTHON_SKIP_ENV_POLLUTERS: true
- name: run cpython tests to check if env polluters have stopped polluting
shell: bash
run: |
for thing in ${{ join(matrix.env_polluting_tests, ' ') }}; do
for i in $(seq 1 10); do
set +e
target/release/rustpython -m test -j 1 --slowest --fail-env-changed --timeout 600 -v ${thing}
exit_code=$?
set -e
if [ ${exit_code} -eq 3 ]; then
echo "Test ${thing} polluted the environment on attempt ${i}."
break
fi
done
if [ ${exit_code} -ne 3 ]; then
echo "Test ${thing} is no longer polluting the environment after ${i} attempts!"
echo "Please remove ${thing} from matrix.env_polluting_tests in '.github/workflows/ci.yaml'."
echo "Please also remove the skip decorators that include the word 'POLLUTERS' in ${thing}."
if [ ${exit_code} -ne 0 ]; then
echo "Test ${thing} failed with exit code ${exit_code}."
echo "Please investigate which test item in ${thing} is failing and either mark it as an expected failure or a skip."
fi
exit 1
fi
done
timeout-minutes: 15
- if: runner.os != 'Windows'
name: check that --install-pip succeeds
run: |
mkdir site-packages
target/release/rustpython --install-pip ensurepip --user
target/release/rustpython -m pip install six
- if: runner.os != 'Windows'
name: Check that ensurepip succeeds.
- name: Check that ensurepip succeeds.
run: |
target/release/rustpython -m ensurepip
target/release/rustpython -c "import pip"
- if: runner.os != 'Windows'
name: Check if pip inside venv is functional
run: |
target/release/rustpython -m venv testvenv
testvenv/bin/rustpython -m pip install wheel
- name: Check whats_left is not broken
run: python -I whats_left.py
shell: bash
run: python -I scripts/whats_left.py ${{ env.CARGO_ARGS }} --features jit
lint:
name: Check Rust code with rustfmt and clippy
name: Lint Rust & Python code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
components: rustfmt, clippy
- name: run rustfmt
run: cargo fmt --check
- name: run clippy on wasm
run: cargo clippy --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings
- uses: actions/setup-python@v5
persist-credentials: false
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: install ruff
run: python -m pip install ruff==0.11.8
- name: Check for redundant test patches
run: python scripts/check_redundant_patches.py
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: run clippy on wasm
run: cargo clippy --manifest-path=crates/wasm/Cargo.toml -- -Dwarnings
- name: Ensure docs generate no warnings
run: cargo doc
- name: run ruff check
run: ruff check --diff
- name: run ruff format
run: ruff format --check
run: cargo doc --locked
- name: Ensure Lib/_opcode_metadata is updated
run: |
python scripts/generate_opcode_metadata.py
if [ -n "$(git status --porcelain)" ]; then
exit 1
fi
- name: Install ruff
uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
with:
version: "0.15.5"
args: "--version"
- run: ruff check --diff
- run: ruff format --check
- name: install prettier
run: yarn global add prettier && echo "$(yarn global bin)" >>$GITHUB_PATH
run: |
yarn global add prettier
yarn global bin >> "$GITHUB_PATH"
- name: check wasm code with prettier
# prettier doesn't handle ignore files very well: https://github.com/prettier/prettier/issues/8506
run: cd wasm && git ls-files -z | xargs -0 prettier --check -u
@@ -338,9 +370,9 @@ jobs:
- name: install extra dictionaries
run: npm install @cspell/dict-en_us @cspell/dict-cpp @cspell/dict-python @cspell/dict-rust @cspell/dict-win32 @cspell/dict-shell
- name: spell checker
uses: streetsidesoftware/cspell-action@v7
uses: streetsidesoftware/cspell-action@v8
with:
files: '**/*.rs'
files: "**/*.rs"
incremental_files_only: true
miri:
@@ -349,19 +381,27 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
RUSTUP_TOOLCHAIN: nightly-2025-07-18 # TODO: Set to "nightly" once https://github.com/rust-lang/rust/issues/144168 is fixed
NIGHTLY_CHANNEL: nightly
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUSTUP_TOOLCHAIN }}
components: miri
toolchain: ${{ env.NIGHTLY_CHANNEL }}
components: miri
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: Swatinem/rust-cache@v2
- name: Run tests under miri
# miri-ignore-leaks because the type-object circular reference means that there will always be
# a memory leak, at least until we have proper cyclic gc
run: MIRIFLAGS='-Zmiri-ignore-leaks' cargo +${{ env.RUSTUP_TOOLCHAIN }} miri test -p rustpython-vm -- miri_test
run: cargo +${{ env.NIGHTLY_CHANNEL }} miri test -p rustpython-vm -- miri_test
env:
# miri-ignore-leaks because the type-object circular reference means that there will always be
# a memory leak, at least until we have proper cyclic gc
MIRIFLAGS: "-Zmiri-ignore-leaks"
wasm:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }}
@@ -369,10 +409,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: install geckodriver
@@ -380,18 +426,19 @@ jobs:
wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-linux64.tar.gz
mkdir geckodriver
tar -xzf geckodriver-v0.36.0-linux64.tar.gz -C geckodriver
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: python -m pip install -r requirements.txt
working-directory: ./wasm/tests
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
with:
cache: "npm"
cache-dependency-path: "wasm/demo/package-lock.json"
- name: run test
run: |
export PATH=$PATH:`pwd`/../../geckodriver
driver_path="$(pwd)/../../geckodriver"
export PATH="$PATH:${driver_path}"
npm install
npm run test
env:
@@ -401,11 +448,13 @@ jobs:
with: { wabt-version: "1.0.36" }
- name: check wasm32-unknown without js
run: |
cd wasm/wasm-unknown-test
cargo build --release --verbose
if wasm-objdump -xj Import target/wasm32-unknown-unknown/release/wasm_unknown_test.wasm; then
echo "ERROR: wasm32-unknown module expects imports from the host environment" >2
cd example_projects/wasm32_without_js/rustpython-without-js
cargo build
cd ..
if wasm-objdump -xj Import rustpython-without-js/target/wasm32-unknown-unknown/debug/rustpython_without_js.wasm; then
echo "ERROR: wasm32-unknown module expects imports from the host environment" >&2
fi
cargo run --release --manifest-path wasm-runtime/Cargo.toml rustpython-without-js/target/wasm32-unknown-unknown/debug/rustpython_without_js.wasm
- name: build notebook demo
if: github.ref == 'refs/heads/release'
run: |
@@ -430,19 +479,55 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
target: wasm32-wasip1
- uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Setup Wasmer
uses: wasmerio/setup-wasmer@v3
- name: Install clang
run: sudo apt-get update && sudo apt-get install clang -y
uses: ./.github/actions/install-linux-deps
with:
clang: true
- name: build rustpython
run: cargo build --release --target wasm32-wasip1 --features freeze-stdlib,stdlib --verbose
- name: run snippets
run: wasmer run --dir `pwd` target/wasm32-wasip1/release/rustpython.wasm -- `pwd`/extra_tests/snippets/stdlib_random.py
run: wasmer run --dir $(pwd) target/wasm32-wasip1/release/rustpython.wasm -- "$(pwd)/extra_tests/snippets/stdlib_random.py"
- name: run cpython unittest
run: wasmer run --dir `pwd` target/wasm32-wasip1/release/rustpython.wasm -- `pwd`/Lib/test/test_int.py
run: wasmer run --dir $(pwd) target/wasm32-wasip1/release/rustpython.wasm -- "$(pwd)/Lib/test/test_int.py"
cargo-shear:
name: cargo shear
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: cargo-bins/cargo-binstall@1800853f2578f8c34492ec76154caef8e163fbca # v1.17.7
- run: cargo binstall --no-confirm cargo-shear
- run: cargo shear
security-lint:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2

View File

@@ -7,7 +7,7 @@ on:
jobs:
issue_assign:
if: (!github.event.issue.pull_request) && github.event.comment.body == 'take'
runs-on: ubuntu-latest
runs-on: ubuntu-slim
concurrency:
group: ${{ github.actor }}-issue-assign

View File

@@ -1,16 +1,19 @@
on:
schedule:
- cron: '0 0 * * 6'
- cron: "0 0 * * 6"
workflow_dispatch:
push:
paths:
- .github/workflows/cron-ci.yaml
pull_request:
paths:
- .github/workflows/cron-ci.yaml
name: Periodic checks/tasks
env:
CARGO_ARGS: --no-default-features --features stdlib,importlib,encodings,ssl,jit
PYTHON_VERSION: "3.13.1"
CARGO_ARGS: --no-default-features --features stdlib,importlib,stdio,encodings,ssl-rustls,jit,host_env
PYTHON_VERSION: "3.14.3"
jobs:
# codecov collects code coverage data from the rust tests, python snippets and python test suite.
@@ -18,34 +21,45 @@ jobs:
codecov:
name: Collect code coverage data
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: sudo apt-get update && sudo apt-get -y install lcov
- name: Run cargo-llvm-cov with Rust tests.
run: cargo llvm-cov --no-report --workspace --exclude rustpython_wasm --verbose --no-default-features --features stdlib,importlib,encodings,ssl,jit
run: cargo llvm-cov --no-report --workspace --exclude rustpython_wasm --exclude rustpython-compiler-source --exclude rustpython-venvlauncher --verbose --no-default-features --features stdlib,importlib,stdio,encodings,ssl-rustls,jit,host_env
- name: Run cargo-llvm-cov with Python snippets.
run: python scripts/cargo-llvm-cov.py
continue-on-error: true
- name: Run cargo-llvm-cov with Python test suite.
run: cargo llvm-cov --no-report run -- -m test -u all --slowest --fail-env-changed
run: cargo llvm-cov --no-report run -- -m test -u all --slowest --fail-env-changed
continue-on-error: true
- name: Prepare code coverage data
run: cargo llvm-cov report --lcov --output-path='codecov.lcov'
- name: Upload to Codecov
if: ${{ github.event_name != 'pull_request' }}
uses: codecov/codecov-action@v5
with:
file: ./codecov.lcov
files: ./codecov.lcov
testdata:
name: Collect regression test data
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true
- uses: dtolnay/rust-toolchain@stable
- name: build rustpython
run: cargo build --release --verbose
@@ -54,6 +68,7 @@ jobs:
env:
RUSTPYTHONPATH: ${{ github.workspace }}/Lib
- name: upload tests data to the website
if: ${{ github.event_name != 'pull_request' }}
env:
SSHKEY: ${{ secrets.ACTIONS_TESTS_DATA_DEPLOY_KEY }}
GITHUB_ACTOR: ${{ github.actor }}
@@ -73,21 +88,27 @@ jobs:
whatsleft:
name: Collect what is left data
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: build rustpython
run: cargo build --release --verbose
- name: Collect what is left data
run: |
chmod +x ./whats_left.py
./whats_left.py --features "ssl,sqlite" > whats_left.temp
chmod +x ./scripts/whats_left.py
./scripts/whats_left.py --features "ssl,sqlite" > whats_left.temp
env:
RUSTPYTHONPATH: ${{ github.workspace }}/Lib
- name: Upload data to the website
if: ${{ github.event_name != 'pull_request' }}
env:
SSHKEY: ${{ secrets.ACTIONS_TESTS_DATA_DEPLOY_KEY }}
GITHUB_ACTOR: ${{ github.actor }}
@@ -103,6 +124,23 @@ jobs:
rm ./_data/whats_left/modules.csv
echo -e "module" > ./_data/whats_left/modules.csv
cat ./_data/whats_left.temp | grep "(entire module)" | cut -d ' ' -f 1 | sort >> ./_data/whats_left/modules.csv
awk -f - ./_data/whats_left.temp > ./_data/whats_left/builtin_items.csv <<'EOF'
BEGIN {
OFS=","
print "builtin,name,is_inherited"
}
/^# builtin items/ { in_section=1; next }
/^$/ { if (in_section) exit }
in_section {
split($1, a, ".")
rest = ""
idx = index($0, " ")
if (idx > 0) {
rest = substr($0, idx+1)
}
print a[1], $1, rest
}
EOF
git add -A
if git -c user.name="Github Actions" -c user.email="actions@github.com" commit -m "Update what is left results" --author="$GITHUB_ACTOR"; then
git push
@@ -111,12 +149,17 @@ jobs:
benchmark:
name: Collect benchmark data
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v5
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
python-version: 3.9
persist-credentials: true
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: cargo install cargo-criterion
- name: build benchmarks
run: cargo build --release --benches
@@ -127,16 +170,17 @@ jobs:
- name: restructure generated files
run: |
cd ./target/criterion/reports
find -type d -name cpython | xargs rm -rf
find -type d -name rustpython | xargs rm -rf
find -mindepth 2 -maxdepth 2 -name violin.svg | xargs rm -rf
find -type f -not -name violin.svg | xargs rm -rf
for file in $(find -type f -name violin.svg); do mv $file $(echo $file | sed -E "s_\./([^/]+)/([^/]+)/violin\.svg_./\1/\2.svg_"); done
find -mindepth 2 -maxdepth 2 -type d | xargs rm -rf
find . -type d -name cpython -print0 | xargs -0 rm -rf
find . -type d -name rustpython -print0 | xargs -0 rm -rf
find . -mindepth 2 -maxdepth 2 -name violin.svg -print0 | xargs -0 rm -rf
find . -type f -not -name violin.svg -print0 | xargs -0 rm -rf
find . -type f -name violin.svg -exec sh -c 'for file; do mv "$file" "$(echo "$file" | sed -E "s_\./([^/]+)/([^/]+)/violin\.svg_./\1/\2.svg_")"; done' _ {} +
find . -mindepth 2 -maxdepth 2 -type d -print0 | xargs -0 rm -rf
cd ..
mv reports/* .
rmdir reports
- name: upload benchmark data to the website
if: ${{ github.event_name != 'pull_request' }}
env:
SSHKEY: ${{ secrets.ACTIONS_TESTS_DATA_DEPLOY_KEY }}
run: |
@@ -148,7 +192,11 @@ jobs:
cd website
rm -rf ./assets/criterion
cp -r ../target/criterion ./assets/criterion
git add ./assets/criterion
printf '{\n "generated_at": "%s",\n "rustpython_commit": "%s",\n "rustpython_ref": "%s"\n}\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
"${{ github.sha }}" \
"${{ github.ref_name }}" > ./_data/criterion-metadata.json
git add ./assets/criterion ./_data/criterion-metadata.json
if git -c user.name="Github Actions" -c user.email="actions@github.com" commit -m "Update benchmark results"; then
git push
fi

123
.github/workflows/lib-deps-check.yaml vendored Normal file
View File

@@ -0,0 +1,123 @@
name: Lib Dependencies Check
on:
pull_request_target:
types: [opened, synchronize, reopened]
paths:
- "Lib/**"
concurrency:
group: lib-deps-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.14.3"
jobs:
check_deps:
permissions:
pull-requests: write
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout base branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Use base branch for scripts (security: don't run PR code with elevated permissions)
ref: ${{ github.event.pull_request.base.ref }}
fetch-depth: 0
persist-credentials: false
- name: Fetch PR head
run: |
git fetch origin ${{ github.event.pull_request.head.sha }}
- name: Checkout PR Lib files
run: |
# Checkout only Lib/ directory from PR head for accurate comparison
git checkout ${{ github.event.pull_request.head.sha }} -- Lib/
- name: Checkout CPython
run: |
git clone --depth 1 --branch "v${{ env.PYTHON_VERSION }}" https://github.com/python/cpython.git cpython
- name: Get changed Lib files
id: changed-files
run: |
# Get the list of changed files under Lib/
changed=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- 'Lib/*.py' 'Lib/**/*.py' | head -50)
echo "Changed files:"
echo "$changed"
# Extract unique module names
modules=""
for file in $changed; do
if [[ "$file" == Lib/test/* ]]; then
# Test files: Lib/test/test_pydoc.py -> test_pydoc, Lib/test/test_pydoc/foo.py -> test_pydoc
module=$(echo "$file" | sed -E 's|^Lib/test/||; s|\.py$||; s|/.*||')
# Skip non-test files in test/ (e.g., support.py, __init__.py)
if [[ ! "$module" == test_* ]]; then
continue
fi
else
# Lib files: Lib/foo.py -> foo, Lib/foo/__init__.py -> foo
module=$(echo "$file" | sed -E 's|^Lib/||; s|/__init__\.py$||; s|\.py$||; s|/.*||')
fi
if [[ -n "$module" && ! " $modules " =~ " $module " ]]; then
modules="$modules $module"
fi
done
modules=$(echo "$modules" | xargs) # trim whitespace
echo "Detected modules: $modules"
echo "modules=$modules" >> $GITHUB_OUTPUT
- name: Setup Python
if: steps.changed-files.outputs.modules != ''
uses: actions/setup-python@v6.2.0
with:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Run deps check
if: steps.changed-files.outputs.modules != ''
id: deps-check
run: |
# Run deps for all modules at once
python scripts/update_lib deps ${{ steps.changed-files.outputs.modules }} --depth 2 > /tmp/deps_output.txt 2>&1 || true
# Read output for GitHub Actions
echo "deps_output<<EOF" >> $GITHUB_OUTPUT
cat /tmp/deps_output.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Check if there's any meaningful output
if [ -s /tmp/deps_output.txt ]; then
echo "has_output=true" >> $GITHUB_OUTPUT
else
echo "has_output=false" >> $GITHUB_OUTPUT
fi
- name: Post comment
if: steps.deps-check.outputs.has_output == 'true'
uses: marocchino/sticky-pull-request-comment@v3
with:
header: lib-deps-check
number: ${{ github.event.pull_request.number }}
message: |
## 📦 Library Dependencies
The following Lib/ modules were modified. Here are their dependencies:
${{ steps.deps-check.outputs.deps_output }}
**Legend:**
- `[+]` path exists in CPython
- `[x]` up-to-date, `[ ]` outdated
- name: Remove comment if no Lib changes
if: steps.changed-files.outputs.modules == ''
uses: marocchino/sticky-pull-request-comment@v3
with:
header: lib-deps-check
number: ${{ github.event.pull_request.number }}
delete: true

74
.github/workflows/pr-format.yaml vendored Normal file
View File

@@ -0,0 +1,74 @@
name: Format Check
# This workflow triggers when a PR is opened/updated
# Posts inline suggestion comments instead of auto-committing
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
- release
concurrency:
group: format-check-${{ github.event.pull_request.number }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.14.3"
jobs:
format_check:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: reviewdog/action-actionlint@0d952c597ef8459f634d7145b0b044a9699e5e43 # v1.71.0
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run cargo fmt
run: cargo fmt --all
- name: Install ruff
uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
with:
version: "0.15.4"
args: "--version"
- name: Run ruff format
run: ruff format
- name: Run ruff check import sorting
run: ruff check --select I --fix
- uses: actions/setup-python@v6.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Run generate_opcode_metadata.py
run: python scripts/generate_opcode_metadata.py
- name: Check for formatting changes
run: |
if ! git diff --exit-code; then
echo "::error::Formatting changes detected. Please run 'cargo fmt --all', 'ruff format', and 'ruff check --select I --fix' locally."
exit 1
fi
- name: Post formatting suggestions
if: failure()
uses: reviewdog/action-suggester@v1
with:
tool_name: auto-format
github_token: ${{ secrets.GITHUB_TOKEN }}
level: warning
filter_mode: diff_context

View File

@@ -17,10 +17,14 @@ permissions:
env:
CARGO_ARGS: --no-default-features --features stdlib,importlib,encodings,sqlite,ssl
X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR: C:\Program Files\OpenSSL\lib\VC\x64\MD
X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR: C:\Program Files\OpenSSL\include
jobs:
build:
runs-on: ${{ matrix.platform.runner }}
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
strategy:
matrix:
platform:
@@ -40,28 +44,24 @@ jobs:
target: aarch64-apple-darwin
# - runner: macos-latest
# target: x86_64-apple-darwin
- runner: windows-latest
- runner: windows-2025
target: x86_64-pc-windows-msvc
# - runner: windows-latest
# - runner: windows-2025
# target: i686-pc-windows-msvc
# - runner: windows-latest
# - runner: windows-2025
# target: aarch64-pc-windows-msvc
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: cargo-bins/cargo-binstall@main
- name: Set up Environment
shell: bash
run: rustup target add ${{ matrix.platform.target }}
- name: Set up Windows Environment
shell: bash
run: |
git config --global core.longpaths true
cargo install --target-dir=target -v cargo-vcpkg
cargo vcpkg -v build
if: runner.os == 'Windows'
- name: Set up MacOS Environment
run: brew install autoconf automake libtool
if: runner.os == 'macOS'
@@ -81,15 +81,20 @@ jobs:
if: runner.os == 'Windows'
- name: Upload Binary Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7.0.0
with:
name: rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}
path: target/rustpython-release-${{ runner.os }}-${{ matrix.platform.target }}*
build-wasm:
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-wasip1
@@ -101,14 +106,14 @@ jobs:
run: cp target/wasm32-wasip1/release/rustpython.wasm target/rustpython-release-wasm32-wasip1.wasm
- name: Upload Binary Artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7.0.0
with:
name: rustpython-release-wasm32-wasip1
path: target/rustpython-release-wasm32-wasip1.wasm
- name: install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- uses: actions/setup-node@v4
- uses: actions/setup-node@v6
- uses: mwilliamson/setup-wabt-action@v3
with: { wabt-version: "1.0.30" }
- name: build demo
@@ -136,15 +141,25 @@ jobs:
release:
runs-on: ubuntu-latest
# Disable this scheduled job when running on a fork.
if: ${{ github.repository == 'RustPython/RustPython' || github.event_name != 'schedule' }}
needs: [build, build-wasm]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Download Binary Artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8.0.1
with:
path: bin
pattern: rustpython-*
merge-multiple: true
- name: Create Lib Archive
run: |
zip -r bin/rustpython-lib.zip Lib/
- name: List Binaries
run: |
ls -lah bin/
@@ -154,8 +169,9 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref_name }}
run: ${{ github.run_number }}
PRE_RELEASE_INPUT: ${{ github.event.inputs.pre-release }}
run: |
if [[ "${{ github.event.inputs.pre-release }}" == "false" ]]; then
if [[ "${PRE_RELEASE_INPUT}" == "false" ]]; then
RELEASE_TYPE_NAME=Release
PRERELEASE_ARG=
else
@@ -168,6 +184,7 @@ jobs:
--repo="$GITHUB_REPOSITORY" \
--title="RustPython $RELEASE_TYPE_NAME $today-$tag #$run" \
--target="$tag" \
--notes "⚠️ **Important**: To run RustPython, you must download both the binary for your platform AND the \`rustpython-lib.zip\` archive. Extract the Lib directory from the archive to the same location as the binary, or set the \`RUSTPYTHONPATH\` environment variable to point to the Lib directory." \
--generate-notes \
$PRERELEASE_ARG \
bin/rustpython-release-*

124
.github/workflows/update-doc-db.yml vendored Normal file
View File

@@ -0,0 +1,124 @@
name: Update doc DB
permissions:
contents: write
pull-requests: write
on:
workflow_dispatch:
inputs:
python-version:
description: Target python version to generate doc db for
type: string
default: "3.14.3"
base-ref:
description: Base branch to create the update branch from
type: string
default: "main"
defaults:
run:
shell: bash
jobs:
generate:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
sparse-checkout: |
crates/doc
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ inputs.python-version }}
- name: Generate docs
run: python crates/doc/generate.py
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: doc-db-${{ inputs.python-version }}-${{ matrix.os }}
path: "crates/doc/generated/*.json"
if-no-files-found: error
retention-days: 7
overwrite: true
merge:
runs-on: ubuntu-latest
needs: generate
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true
ref: ${{ inputs.base-ref }}
token: ${{ secrets.AUTO_COMMIT_PAT }}
- name: Create update branch
env:
PYTHON_VERSION: ${{ inputs.python-version }}
run: git switch -c "update-doc-${PYTHON_VERSION}"
- name: Download generated doc DBs
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: "doc-db-${{ inputs.python-version }}-**"
path: crates/doc/generated/
merge-multiple: true
- name: Transform JSON
env:
PYTHON_VERSION: ${{ inputs.python-version }}
run: |
# Merge all artifacts
jq -s "add" --sort-keys crates/doc/generated/*.json > crates/doc/generated/merged.json
# Format merged json for the phf macro
jq -r 'to_entries[] | " \(.key | @json) => \(.value | @json),"' crates/doc/generated/merged.json > crates/doc/generated/raw_entries.txt
OUTPUT_FILE='crates/doc/src/data.inc.rs'
echo -n '' > $OUTPUT_FILE
echo '// This file was auto-generated by `.github/workflows/update-doc-db.yml`.' >> $OUTPUT_FILE
echo "// CPython version: ${PYTHON_VERSION}" >> $OUTPUT_FILE
echo '// spell-checker: disable' >> $OUTPUT_FILE
echo '' >> $OUTPUT_FILE
echo "pub static DB: phf::Map<&'static str, &'static str> = phf::phf_map! {" >> $OUTPUT_FILE
cat crates/doc/generated/raw_entries.txt >> $OUTPUT_FILE
echo '};' >> $OUTPUT_FILE
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: doc-db-${{ inputs.python-version }}
path: "crates/doc/src/data.inc.rs"
if-no-files-found: error
retention-days: 7
overwrite: true
- name: Commit, push and create PR
env:
GH_TOKEN: ${{ secrets.AUTO_COMMIT_PAT }}
PYTHON_VERSION: ${{ inputs.python-version }}
BASE_REF: ${{ inputs.base-ref }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if [ -n "$(git status --porcelain)" ]; then
git add crates/doc/src/data.inc.rs
git commit -m "Update doc DB for CPython ${PYTHON_VERSION}"
git push -u origin HEAD
gh pr create \
--base "${BASE_REF}" \
--title "Update doc DB for CPython ${PYTHON_VERSION}" \
--body "Auto-generated by update-doc-db workflow."
fi

View File

@@ -0,0 +1,90 @@
name: Updated libs status
on:
push:
branches:
- main
paths:
- "Lib/**"
workflow_dispatch:
permissions:
contents: read
issues: write
env:
PYTHON_VERSION: "v3.14.3"
ISSUE_ID: "6839"
jobs:
update-issue:
runs-on: ubuntu-latest
if: ${{ github.repository == 'RustPython/RustPython' }}
steps:
- name: Clone RustPython
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: rustpython
persist-credentials: false
sparse-checkout: |-
Lib
scripts/update_lib
- name: Clone CPython ${{ env.PYTHON_VERSION }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: python/cpython
path: cpython
ref: ${{ env.PYTHON_VERSION }}
persist-credentials: false
sparse-checkout: |
Lib
- name: Get current date
id: current_date
run: |
now=$(date -u +"%Y-%m-%d %H:%M:%S")
echo "date=$now" >> "$GITHUB_OUTPUT"
- name: Write body prefix
run: |
cat > body.txt <<EOF
<!--
THIS BODY IS AUTO-GENERATED. DO NOT EDIT MANUALLY!
-->
## Summary
Check \`scripts/update_lib\` for tools. As a note, the current latest Python version is \`${{ env.PYTHON_VERSION }}\`.
Previous versions' issues as reference
- 3.13: #5529
<!--
Quick guideline for Copilot:
# Clone \`github.com/python/cpython\` \`${{ env.PYTHON_VERSION }}\` tag under RustPython working dir with depth 1 option; never 3.14.0 or 3.14.1 or 3.14.2
# Pick a library or test to update. Probably user give one.
# Run \`python3 scripts/update_lib quick <name>\`
# A commit is automatically created. push the commit.
# Do not include keywords that automatically close an issue (e.g., Fix, Close, Resolve) next to an issue link or issue number.
-->
## Details
${{ steps.current_date.outputs.date }} (UTC)
\`\`\`shell
$ python3 scripts/update_lib todo --done
\`\`\`
EOF
- name: Run todo
run: python3 rustpython/scripts/update_lib todo --cpython cpython --lib rustpython/Lib --done >> body.txt
- name: Update GH issue
run: gh issue edit ${{ env.ISSUE_ID }} --body-file ../body.txt
env:
GH_TOKEN: ${{ github.token }}
working-directory: rustpython

1093
.github/workflows/upgrade-pylib.lock.yml generated vendored Normal file

File diff suppressed because it is too large Load Diff

134
.github/workflows/upgrade-pylib.md vendored Normal file
View File

@@ -0,0 +1,134 @@
---
description: |
Pick an out-of-sync Python library from the todo list and upgrade it
by running `scripts/update_lib quick`, then open a pull request.
on:
workflow_dispatch:
inputs:
name:
description: "Module name to upgrade (leave empty to auto-pick)"
required: false
type: string
timeout-minutes: 45
permissions:
contents: read
issues: read
pull-requests: read
network:
allowed:
- defaults
- rust
- python
engine: copilot
runtimes:
python:
version: "3.14"
tools:
bash:
- ":*"
edit:
github:
toolsets: [repos, issues, pull_requests]
read-only: true
safe-outputs:
create-pull-request:
title-prefix: "Update "
labels: [pylib-sync]
draft: false
expires: 30
cache:
key: cpython-lib-${{ env.PYTHON_VERSION }}
path: cpython
restore-keys:
- cpython-lib-
env:
PYTHON_VERSION: "v3.14.3"
ISSUE_ID: "6839"
---
# Upgrade Python Library
You are an automated maintenance agent for RustPython, a Python 3 interpreter written in Rust. Your task is to upgrade one out-of-sync Python standard library module from CPython.
## Step 1: Set up the environment
The CPython source may already be cached. Check if the `cpython` directory exists and has the correct version:
```bash
if [ -d "cpython/Lib" ]; then
echo "CPython cache hit, skipping clone"
else
git clone --depth 1 --branch "$PYTHON_VERSION" https://github.com/python/cpython.git cpython
fi
```
## Step 2: Determine module name
Run this script to determine the module name:
```bash
MODULE_NAME="${{ github.event.inputs.name }}"
if [ -z "$MODULE_NAME" ]; then
echo "No module specified, running todo to find one..."
python3 scripts/update_lib todo
echo "Pick one module from the list above that is marked [ ], has no unmet deps, and has a small Δ number."
echo "Do NOT pick: opcode, datetime, random, hashlib, tokenize, pdb, _pyrepl, concurrent, asyncio, multiprocessing, ctypes, idlelib, tkinter, shutil, tarfile, email, unittest"
else
echo "Module specified by user: $MODULE_NAME"
fi
```
If the script printed "Module specified by user: ...", use that exact name. If it printed the todo list, pick one suitable module from it.
## Step 3: Run the upgrade
Run the quick upgrade command. This will copy the library from CPython, migrate test files preserving RustPython markers, auto-mark test failures, and create a git commit:
```bash
python3 scripts/update_lib quick <module_name>
```
This takes a while because it builds RustPython (`cargo build --release`) and runs tests to determine which ones pass or fail.
If the command fails, report the error and stop. Do not try to fix Rust code or modify test files manually.
## Step 4: Verify the result
After the script succeeds, check what changed:
```bash
git log -1 --stat
git diff HEAD~1 --stat
```
Make sure the commit was created with the correct message format: `Update <name> from <version>`.
## Step 5: Create the pull request
Create a pull request. Reference issue #${{ env.ISSUE_ID }} in the body but do **NOT** use keywords that auto-close issues (Fix, Close, Resolve).
Use this format for the PR body:
```
## Summary
Upgrade `<module_name>` from CPython $PYTHON_VERSION.
Part of #$ISSUE_ID
## Changes
- Updated `Lib/<module_name>` from CPython
- Migrated test files preserving RustPython markers
- Auto-marked test failures with `@expectedFailure`
```

8
.gitignore vendored
View File

@@ -21,3 +21,11 @@ flamescope.json
extra_tests/snippets/resources
extra_tests/not_impl.py
Lib/_sysconfig_vars*.json
Lib/site-packages/*
!Lib/site-packages/README.txt
Lib/test/data/*
!Lib/test/data/README
cpython/

12
.vscode/launch.json vendored
View File

@@ -16,18 +16,6 @@
},
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'rustpython' without SSL",
"preLaunchTask": "Build RustPython Debug without SSL",
"program": "target/debug/rustpython",
"args": [],
"env": {
"RUST_BACKTRACE": "1"
},
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",

16
.vscode/tasks.json vendored
View File

@@ -1,28 +1,12 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build RustPython Debug without SSL",
"type": "shell",
"command": "cargo",
"args": [
"build",
],
"problemMatcher": [
"$rustc",
],
"group": {
"kind": "build",
"isDefault": true,
},
},
{
"label": "Build RustPython Debug",
"type": "shell",
"command": "cargo",
"args": [
"build",
"--features=ssl"
],
"problemMatcher": [
"$rustc",

View File

@@ -4,7 +4,7 @@ This document provides guidelines for working with GitHub Copilot when contribut
## Project Overview
RustPython is a Python 3 interpreter written in Rust, implementing Python 3.13.0+ compatibility. The project aims to provide:
RustPython is a Python 3 interpreter written in Rust, implementing Python 3.14.0+ compatibility. The project aims to provide:
- A complete Python-3 environment entirely in Rust (not CPython bindings)
- A clean implementation without compatibility hacks
@@ -30,6 +30,14 @@ RustPython is a Python 3 interpreter written in Rust, implementing Python 3.13.0
- `jit/` - Experimental JIT compiler implementation
- `pylib/` - Python standard library packaging (do not modify this directory directly - its contents are generated automatically)
## AI Agent Rules
**CRITICAL: Git Operations**
- NEVER create pull requests directly without explicit user permission
- NEVER push commits to remote without explicit user permission
- Always ask the user before performing any git operations that affect the remote repository
- Commits can be created locally when requested, but pushing and PR creation require explicit approval
## Important Development Notes
### Running Python Code
@@ -44,7 +52,7 @@ cargo run -- script.py
cargo run
# With specific features
cargo run --features ssl
cargo run --features jit
# Release mode (recommended for better performance)
cargo run --release -- script.py
@@ -73,17 +81,28 @@ The `Lib/` directory contains Python standard library files copied from the CPyt
- `unittest.skip("TODO: RustPython <reason>")`
- `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment
### Clean Build
When you modify bytecode instructions, a full clean is required:
```bash
rm -r target/debug/build/rustpython-* && find . | grep -E "\.pyc$" | xargs rm -r
```
### Testing
```bash
# Run Rust unit tests
cargo test --workspace --exclude rustpython_wasm
cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher
# Run Python snippets tests
# Run Python snippets tests (debug mode recommended for faster compilation)
cargo run -- extra_tests/snippets/builtin_bytes.py
# Run all Python snippets tests with pytest
cd extra_tests
pytest -v
# Run the Python test module
# Run the Python test module (release mode recommended for better performance)
cargo run --release -- -m test ${TEST_MODULE}
cargo run --release -- -m test test_unicode # to test test_unicode.py
@@ -91,9 +110,11 @@ cargo run --release -- -m test test_unicode # to test test_unicode.py
cargo run --release -- -m test test_unicode -k test_unicode_escape
```
**Note**: For `extra_tests/snippets` tests, use debug mode (`cargo run`) as compilation is faster. For `unittest` (`-m test`), use release mode (`cargo run --release`) for better runtime performance.
### Determining What to Implement
Run `./whats_left.py` to get a list of unimplemented methods, which is helpful when looking for contribution opportunities.
Run `./scripts/whats_left.py` to get a list of unimplemented methods, which is helpful when looking for contribution opportunities.
## Coding Guidelines
@@ -104,6 +125,36 @@ Run `./whats_left.py` to get a list of unimplemented methods, which is helpful w
- Follow Rust best practices for error handling and memory management
- Use the macro system (`pyclass`, `pymodule`, `pyfunction`, etc.) when implementing Python functionality in Rust
#### Comments
- Do not delete or rewrite existing comments unless they are factually wrong or directly contradict the new code.
- Do not add decorative section separators (e.g. `// -----------`, `// ===`, `/* *** */`). Use `///` doc-comments or short `//` comments only when they add value.
#### Avoid Duplicate Code in Branches
When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once.
**Bad:**
```rust
let result = if condition {
let msg = format!("message A: {x}");
some_function(msg, shared_arg)
} else {
let msg = format!("message B");
some_function(msg, shared_arg)
};
```
**Good:**
```rust
let msg = if condition {
format!("message A: {x}")
} else {
format!("message B")
};
let result = some_function(msg, shared_arg);
```
### Python Code
- **IMPORTANT**: In most cases, Python code should not be edited. Bug fixes should be made through Rust code modifications only
@@ -176,12 +227,15 @@ cargo build --target wasm32-wasip1 --no-default-features --features freeze-stdli
cargo run --features jit
```
### SSL Support
### Linux Build and Debug on macOS
```bash
# Enable SSL support
cargo run --features ssl
```
See the "Testing on Linux from macOS" section in [DEVELOPMENT.md](DEVELOPMENT.md#testing-on-linux-from-macos).
### Building venvlauncher (Windows)
See DEVELOPMENT.md "CPython Version Upgrade Checklist" section.
**IMPORTANT**: All 4 venvlauncher binaries use the same source code. Do NOT add multiple `[[bin]]` entries to Cargo.toml. Build once and copy with different names.
## Test Code Modification Rules
@@ -206,7 +260,7 @@ cargo run --features ssl
## Documentation
- Check the [architecture document](architecture/architecture.md) for a high-level overview
- Read the [development guide](DEVELOPMENT.md) for detailed setup instructions
- Check the [architecture document](/architecture/architecture.md) for a high-level overview
- Read the [development guide](/DEVELOPMENT.md) for detailed setup instructions
- Generate documentation with `cargo doc --no-deps --all`
- Online documentation is available at [docs.rs/rustpython](https://docs.rs/rustpython/)
- Online documentation is available at [docs.rs/rustpython](https://docs.rs/rustpython/)

2728
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,18 +10,21 @@ repository.workspace = true
license.workspace = true
[features]
default = ["threading", "stdlib", "stdio", "importlib"]
default = ["threading", "stdlib", "stdio", "importlib", "ssl-rustls", "host_env"]
host_env = ["rustpython-vm/host_env", "rustpython-stdlib?/host_env"]
importlib = ["rustpython-vm/importlib"]
encodings = ["rustpython-vm/encodings"]
stdio = ["rustpython-vm/stdio"]
stdlib = ["rustpython-stdlib", "rustpython-pylib", "encodings"]
flame-it = ["rustpython-vm/flame-it", "flame", "flamescope"]
flame-it = ["rustpython-vm/flame-it", "rustpython-stdlib/flame-it", "flame", "flamescope"]
freeze-stdlib = ["stdlib", "rustpython-vm/freeze-stdlib", "rustpython-pylib?/freeze-stdlib"]
jit = ["rustpython-vm/jit"]
threading = ["rustpython-vm/threading", "rustpython-stdlib/threading"]
sqlite = ["rustpython-stdlib/sqlite"]
ssl = ["rustpython-stdlib/ssl"]
ssl-vendor = ["ssl", "rustpython-stdlib/ssl-vendor"]
ssl = []
ssl-rustls = ["ssl", "rustpython-stdlib/ssl-rustls"]
ssl-openssl = ["ssl", "rustpython-stdlib/ssl-openssl"]
ssl-vendor = ["ssl-openssl", "rustpython-stdlib/ssl-vendor"]
tkinter = ["rustpython-stdlib/tkinter"]
[build-dependencies]
@@ -31,8 +34,7 @@ winresource = "0.1"
rustpython-compiler = { workspace = true }
rustpython-pylib = { workspace = true, optional = true }
rustpython-stdlib = { workspace = true, optional = true, features = ["compiler"] }
rustpython-vm = { workspace = true, features = ["compiler"] }
ruff_python_parser = { workspace = true }
rustpython-vm = { workspace = true, features = ["compiler", "gc"] }
cfg-if = { workspace = true }
log = { workspace = true }
@@ -51,7 +53,9 @@ rustyline = { workspace = true }
[dev-dependencies]
criterion = { workspace = true }
pyo3 = { version = "0.24", features = ["auto-initialize"] }
pyo3 = { version = "0.28.2", features = ["auto-initialize"] }
rustpython-stdlib = { workspace = true }
ruff_python_parser = { workspace = true }
[[bench]]
name = "execution"
@@ -73,6 +77,21 @@ opt-level = 3
# https://github.com/rust-lang/rust/issues/92869
# lto = "thin"
# Some crates don't change as much but benefit more from
# more expensive optimization passes, so we selectively
# decrease codegen-units in some cases.
[profile.release.package.rustpython-doc]
codegen-units = 1
[profile.release.package.rustpython-literal]
codegen-units = 1
[profile.release.package.rustpython-common]
codegen-units = 1
[profile.release.package.rustpython-wtf8]
codegen-units = 1
[profile.bench]
lto = "thin"
codegen-units = 1
@@ -82,19 +101,10 @@ opt-level = 3
lto = "thin"
[patch.crates-io]
parking_lot_core = { git = "https://github.com/youknowone/parking_lot", branch = "rustpython" }
# REDOX START, Uncomment when you want to compile/check with redoxer
# REDOX END
# Used only on Windows to build the vcpkg dependencies
[package.metadata.vcpkg]
git = "https://github.com/microsoft/vcpkg"
# The revision of the vcpkg repository to use
# https://github.com/microsoft/vcpkg/tags
rev = "2024.02.14"
[package.metadata.vcpkg.target]
x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md", dev-dependencies = ["openssl" ] }
[package.metadata.packager]
product-name = "RustPython"
identifier = "com.rustpython.rustpython"
@@ -117,103 +127,106 @@ template = "installer-config/installer.wxs"
[workspace]
resolver = "2"
members = [
"compiler", "compiler/core", "compiler/codegen", "compiler/literal", "compiler/source",
".", "common", "derive", "jit", "vm", "vm/sre_engine", "pylib", "stdlib", "derive-impl", "wtf8",
"wasm/lib",
".",
"crates/*",
]
exclude = ["pymath"]
[workspace.package]
version = "0.4.0"
version = "0.5.0"
authors = ["RustPython Team"]
edition = "2024"
rust-version = "1.85.0"
rust-version = "1.93.0"
repository = "https://github.com/RustPython/RustPython"
license = "MIT"
[workspace.dependencies]
rustpython-compiler-source = { path = "compiler/source" }
rustpython-compiler-core = { path = "compiler/core", version = "0.4.0" }
rustpython-compiler = { path = "compiler", version = "0.4.0" }
rustpython-codegen = { path = "compiler/codegen", version = "0.4.0" }
rustpython-common = { path = "common", version = "0.4.0" }
rustpython-derive = { path = "derive", version = "0.4.0" }
rustpython-derive-impl = { path = "derive-impl", version = "0.4.0" }
rustpython-jit = { path = "jit", version = "0.4.0" }
rustpython-literal = { path = "compiler/literal", version = "0.4.0" }
rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" }
rustpython-pylib = { path = "pylib", version = "0.4.0" }
rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.0" }
rustpython-sre_engine = { path = "vm/sre_engine", version = "0.4.0" }
rustpython-wtf8 = { path = "wtf8", version = "0.4.0" }
rustpython-doc = { git = "https://github.com/RustPython/__doc__", tag = "0.3.0", version = "0.3.0" }
rustpython-compiler-core = { path = "crates/compiler-core", version = "0.5.0" }
rustpython-compiler = { path = "crates/compiler", version = "0.5.0" }
rustpython-codegen = { path = "crates/codegen", version = "0.5.0" }
rustpython-common = { path = "crates/common", version = "0.5.0" }
rustpython-derive = { path = "crates/derive", version = "0.5.0" }
rustpython-derive-impl = { path = "crates/derive-impl", version = "0.5.0" }
rustpython-jit = { path = "crates/jit", version = "0.5.0" }
rustpython-literal = { path = "crates/literal", version = "0.5.0" }
rustpython-vm = { path = "crates/vm", default-features = false, version = "0.5.0" }
rustpython-pylib = { path = "crates/pylib", version = "0.5.0" }
rustpython-stdlib = { path = "crates/stdlib", default-features = false, version = "0.5.0" }
rustpython-sre_engine = { path = "crates/sre_engine", version = "0.5.0" }
rustpython-wtf8 = { path = "crates/wtf8", version = "0.5.0" }
rustpython-doc = { path = "crates/doc", version = "0.5.0" }
ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.11.0" }
# Ruff tag 0.15.6 is based on commit e4c7f357777a2fdd34dbe6a98b1b7d3e7488f675
# at the time of this capture. We use the commit hash to ensure reproducible builds.
ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", rev = "e4c7f357777a2fdd34dbe6a98b1b7d3e7488f675" }
ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", rev = "e4c7f357777a2fdd34dbe6a98b1b7d3e7488f675" }
ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", rev = "e4c7f357777a2fdd34dbe6a98b1b7d3e7488f675" }
ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", rev = "e4c7f357777a2fdd34dbe6a98b1b7d3e7488f675" }
ahash = "0.8.11"
phf = { version = "0.13.1", default-features = false, features = ["macros"]}
ahash = "0.8.12"
ascii = "1.1"
bitflags = "2.4.2"
bitflags = "2.11.0"
bitflagset = "0.0.3"
bstr = "1"
cfg-if = "1.0"
chrono = "0.4.39"
chrono = { version = "0.4.44", default-features = false, features = ["clock", "oldtime", "std"] }
constant_time_eq = "0.4"
criterion = { version = "0.5", features = ["html_reports"] }
criterion = { version = "0.8", features = ["html_reports"] }
crossbeam-utils = "0.8.21"
flame = "0.2.2"
getrandom = { version = "0.3", features = ["std"] }
glob = "0.3"
hex = "0.4.3"
indexmap = { version = "2.2.6", features = ["std"] }
insta = "1.42"
indexmap = { version = "2.13.0", features = ["std"] }
insta = "1.46"
itertools = "0.14.0"
is-macro = "0.3.7"
junction = "1.2.0"
libc = "0.2.169"
libffi = "4.1"
log = "0.4.27"
nix = { version = "0.29", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] }
malachite-bigint = "0.6"
malachite-q = "0.6"
malachite-base = "0.6"
memchr = "2.7.4"
junction = "1.4.2"
libc = "0.2.183"
libffi = "5"
log = "0.4.29"
nix = { version = "0.30", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] }
malachite-bigint = "0.9.1"
malachite-q = "0.9.1"
malachite-base = "0.9.1"
memchr = "2.8.0"
num-complex = "0.4.6"
num-integer = "0.1.46"
num-traits = "0.2"
num_enum = { version = "0.7", default-features = false }
optional = "0.5"
once_cell = "1.20.3"
parking_lot = "0.12.3"
paste = "1.0.15"
proc-macro2 = "1.0.93"
pymath = "0.0.2"
quote = "1.0.38"
proc-macro2 = "1.0.105"
pymath = { version = "0.2.0", features = ["mul_add", "malachite-bigint", "complex"] }
quote = "1.0.45"
radium = "1.1.1"
rand = "0.9"
rand_core = { version = "0.9", features = ["os_rng"] }
rustix = { version = "1.0", features = ["event"] }
rustyline = "15.0.0"
serde = { version = "1.0.133", default-features = false }
schannel = "0.1.27"
rustix = { version = "1.1", features = ["event"] }
rustyline = "17.0.1"
serde = { package = "serde_core", version = "1.0.225", default-features = false, features = ["alloc"] }
schannel = "0.1.28"
scoped-tls = "1"
scopeguard = "1"
static_assertions = "1.1"
strum = "0.27"
strum_macros = "0.27"
strum_macros = "0.28"
syn = "2"
thiserror = "2.0"
thread_local = "1.1.8"
unicode-casing = "0.1.0"
unicode-casing = "0.1.1"
unic-char-property = "0.9.0"
unic-normal = "0.9.0"
unic-ucd-age = "0.9.0"
unic-ucd-bidi = "0.9.0"
unic-ucd-category = "0.9.0"
unic-ucd-ident = "0.9.0"
unicode_names2 = "1.3.0"
unicode-bidi-mirroring = "0.2"
widestring = "1.1.0"
windows-sys = "0.59.0"
wasm-bindgen = "0.2.100"
unicode_names2 = "2.0.0"
unicode-bidi-mirroring = "0.4"
widestring = "1.2.0"
windows-sys = "0.61.2"
wasm-bindgen = "0.2.106"
# Lints
@@ -223,6 +236,9 @@ unsafe_op_in_unsafe_fn = "deny"
elided_lifetimes_in_paths = "warn"
[workspace.lints.clippy]
alloc_instead_of_core = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
perf = "warn"
style = "warn"
complexity = "warn"

View File

@@ -19,13 +19,13 @@ The contents of the Development Guide include:
RustPython requires the following:
- Rust latest stable version (e.g 1.69.0 as of Apr 20 2023)
- Rust latest stable version (e.g 1.92.0 as of Jan 7 2026)
- To check Rust version: `rustc --version`
- If you have `rustup` on your system, enter to update to the latest
stable version: `rustup update stable`
- If you do not have Rust installed, use [rustup](https://rustup.rs/) to
do so.
- CPython version 3.13 or higher
- CPython version 3.14 or higher
- CPython can be installed by your operating system's package manager,
from the [Python website](https://www.python.org/downloads/), or
using a third-party distribution, such as
@@ -65,7 +65,7 @@ $ pytest -v
Rust unit tests can be run with `cargo`:
```shell
$ cargo test --workspace --exclude rustpython_wasm
$ cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher
```
Python unit tests can be run by compiling RustPython and running the test module:
@@ -95,6 +95,41 @@ To run only `test_cmath` (located at `Lib/test/test_cmath`) verbosely:
$ cargo run --release -- -m test test_cmath -v
```
### Testing on Linux from macOS
You can test RustPython on Linux from macOS using Apple's `container` CLI.
**Setup (one-time):**
```shell
# Install container CLI
$ brew install container
# Disable Rosetta requirement for arm64-only builds
$ defaults write com.apple.container.defaults build.rosetta -bool false
# Build the development image
$ container build --arch arm64 -t rustpython-dev -f .devcontainer/Dockerfile .
```
**Running tests:**
```shell
# Start a persistent container in background (8GB memory, 4 CPUs for compilation)
$ container run -d --name rustpython-test -m 8G -c 4 \
--mount type=bind,source=$(pwd),target=/workspace \
-w /workspace rustpython-dev sleep infinity
# Run tests inside the container
$ container exec rustpython-test sh -c "cargo run --release -- -m test test_ensurepip"
# Run any command
$ container exec rustpython-test sh -c "cargo test --workspace"
# Stop and remove the container when done
$ container rm -f rustpython-test
```
## Profiling
To profile RustPython, build it in `release` mode with the `flame-it` feature.
@@ -118,19 +153,19 @@ exists a raw html viewer which is currently broken, and we welcome a PR to fix i
Understanding a new codebase takes time. Here's a brief view of the
repository's structure:
- `compiler/src`: python compilation to bytecode
- `core/src`: python bytecode representation in rust structures
- `parser/src`: python lexing, parsing and ast
- `derive/src`: Rust language extensions and macros specific to rustpython
- `crates/compiler/src`: python compilation to bytecode
- `crates/compiler-core/src`: python bytecode representation in rust structures
- `crates/derive/src` and `crates/derive-impl/src`: Rust language extensions and macros specific to rustpython
- `Lib`: Carefully selected / copied files from CPython sourcecode. This is
the python side of the standard library.
- `test`: CPython test suite
- `vm/src`: python virtual machine
- `crates/vm/src`: python virtual machine
- `builtins`: Builtin functions and types
- `stdlib`: Standard library parts implemented in rust.
- `src`: using the other subcrates to bring rustpython to life.
- `wasm`: Binary crate and resources for WebAssembly build
- `extra_tests`: extra integration test snippets as a supplement to `Lib/test`
- `crates/wasm`: Binary crate and resources for WebAssembly build
- `extra_tests`: extra integration test snippets as a supplement to `Lib/test`.
Add new RustPython-only regression tests here; do not place new tests under `Lib/test`.
## Understanding Internals
@@ -140,9 +175,9 @@ implementation is found in the `src` directory (specifically, `src/lib.rs`).
The top-level `rustpython` binary depends on several lower-level crates including:
- `rustpython-parser` (implementation in `compiler/parser/src`)
- `rustpython-compiler` (implementation in `compiler/src`)
- `rustpython-vm` (implementation in `vm/src`)
- `ruff_python_parser` and `ruff_python_ast` (external dependencies from the Ruff project)
- `rustpython-compiler` (implementation in `crates/compiler/src`)
- `rustpython-vm` (implementation in `crates/vm/src`)
Together, these crates provide the functions of a programming language and
enable a line of code to go through a series of steps:
@@ -153,31 +188,26 @@ enable a line of code to go through a series of steps:
- compile the AST into bytecode
- execute the bytecode in the virtual machine (VM).
### rustpython-parser
### Parser and AST
This crate contains the lexer and parser to convert a line of code to
an Abstract Syntax Tree (AST):
RustPython uses the Ruff project's parser and AST implementation:
- Lexer: `compiler/parser/src/lexer.rs` converts Python source code into tokens
- Parser: `compiler/parser/src/parser.rs` takes the tokens generated by the lexer and parses
the tokens into an AST (Abstract Syntax Tree) where the nodes of the syntax
tree are Rust structs and enums.
- The Parser relies on `LALRPOP`, a Rust parser generator framework. The
LALRPOP definition of Python's grammar is in `compiler/parser/src/python.lalrpop`.
- More information on parsers and a tutorial can be found in the
[LALRPOP book](https://lalrpop.github.io/lalrpop/).
- AST: `compiler/ast/` implements in Rust the Python types and expressions
represented by the AST nodes.
- Parser: `ruff_python_parser` is used to convert Python source code into tokens
and parse them into an Abstract Syntax Tree (AST)
- AST: `ruff_python_ast` provides the Rust types and expressions represented by
the AST nodes
- These are external dependencies maintained by the Ruff project
- For more information, visit the [Ruff GitHub repository](https://github.com/astral-sh/ruff)
### rustpython-compiler
The `rustpython-compiler` crate's purpose is to transform the AST (Abstract Syntax
Tree) to bytecode. The implementation of the compiler is found in the
`compiler/src` directory. The compiler implements Python's symbol table,
`crates/compiler/src` directory. The compiler implements Python's symbol table,
ast->bytecode compiler, and bytecode optimizer in Rust.
Implementation of bytecode structure in Rust is found in the `compiler/core/src`
directory. `compiler/core/src/bytecode.rs` contains the representation of
Implementation of bytecode structure in Rust is found in the `crates/compiler-core/src`
directory. `crates/compiler-core/src/bytecode.rs` contains the representation of
instructions and operations in Rust. Further information about Python's
bytecode instructions can be found in the
[Python documentation](https://docs.python.org/3/library/dis.html#bytecodes).
@@ -185,14 +215,14 @@ bytecode instructions can be found in the
### rustpython-vm
The `rustpython-vm` crate has the important job of running the virtual machine that
executes Python's instructions. The `vm/src` directory contains code to
executes Python's instructions. The `crates/vm/src` directory contains code to
implement the read and evaluation loop that fetches and dispatches
instructions. This directory also contains the implementation of the
Python Standard Library modules in Rust (`vm/src/stdlib`). In Python
everything can be represented as an object. The `vm/src/builtins` directory holds
Python Standard Library modules in Rust (`crates/vm/src/stdlib`). In Python
everything can be represented as an object. The `crates/vm/src/builtins` directory holds
the Rust code used to represent different Python objects and their methods. The
core implementation of what a Python object is can be found in
`vm/src/object/core.rs`.
`crates/vm/src/object/core.rs`.
### Code generation

View File

@@ -29,15 +29,19 @@ def init_streams(android_log_write, stdout_prio, stderr_prio):
global logcat
logcat = Logcat(android_log_write)
sys.stdout = TextLogStream(
stdout_prio, "python.stdout", sys.stdout.fileno())
sys.stderr = TextLogStream(
stderr_prio, "python.stderr", sys.stderr.fileno())
sys.stdout = TextLogStream(stdout_prio, "python.stdout", sys.stdout)
sys.stderr = TextLogStream(stderr_prio, "python.stderr", sys.stderr)
class TextLogStream(io.TextIOWrapper):
def __init__(self, prio, tag, fileno=None, **kwargs):
def __init__(self, prio, tag, original=None, **kwargs):
# Respect the -u option.
if original:
kwargs.setdefault("write_through", original.write_through)
fileno = original.fileno()
else:
fileno = None
# The default is surrogateescape for stdout and backslashreplace for
# stderr, but in the context of an Android log, readability is more
# important than reversibility.

1161
Lib/_ast_unparse.py vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -485,9 +485,10 @@ class _CallableGenericAlias(GenericAlias):
def __repr__(self):
if len(self.__args__) == 2 and _is_param_expr(self.__args__[0]):
return super().__repr__()
from annotationlib import type_repr
return (f'collections.abc.Callable'
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
f'{_type_repr(self.__args__[-1])}]')
f'[[{", ".join([type_repr(a) for a in self.__args__[:-1]])}], '
f'{type_repr(self.__args__[-1])}]')
def __reduce__(self):
args = self.__args__
@@ -512,10 +513,6 @@ class _CallableGenericAlias(GenericAlias):
new_args = (t_args, t_result)
return _CallableGenericAlias(Callable, tuple(new_args))
# TODO: RUSTPYTHON patch for common call
def __or__(self, other):
super().__or__(other)
def _is_param_expr(obj):
"""Checks if obj matches either a list of types, ``...``, ``ParamSpec`` or
``_ConcatenateGenericAlias`` from typing.py
@@ -528,23 +525,6 @@ def _is_param_expr(obj):
names = ('ParamSpec', '_ConcatenateGenericAlias')
return obj.__module__ == 'typing' and any(obj.__name__ == name for name in names)
def _type_repr(obj):
"""Return the repr() of an object, special-casing types (internal helper).
Copied from :mod:`typing` since collections.abc
shouldn't depend on that module.
(Keep this roughly in sync with the typing version.)
"""
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
return f'{obj.__module__}.{obj.__qualname__}'
if obj is Ellipsis:
return '...'
if isinstance(obj, FunctionType):
return obj.__name__
return repr(obj)
class Callable(metaclass=ABCMeta):
@@ -1077,6 +1057,7 @@ class Sequence(Reversible, Collection):
Sequence.register(tuple)
Sequence.register(str)
Sequence.register(bytes)
Sequence.register(range)
Sequence.register(memoryview)
@@ -1087,7 +1068,7 @@ class _DeprecateByteStringMeta(ABCMeta):
warnings._deprecated(
"collections.abc.ByteString",
remove=(3, 14),
remove=(3, 17),
)
return super().__new__(cls, name, bases, namespace, **kwargs)
@@ -1096,14 +1077,18 @@ class _DeprecateByteStringMeta(ABCMeta):
warnings._deprecated(
"collections.abc.ByteString",
remove=(3, 14),
remove=(3, 17),
)
return super().__instancecheck__(instance)
class ByteString(Sequence, metaclass=_DeprecateByteStringMeta):
"""This unifies bytes and bytearray.
"""Deprecated ABC serving as a common supertype of ``bytes`` and ``bytearray``.
XXX Should add all their methods.
This ABC is scheduled for removal in Python 3.17.
Use ``isinstance(obj, collections.abc.Buffer)`` to test if ``obj``
implements the buffer protocol at runtime. For use in type annotations,
either use ``Buffer`` or a union that explicitly specifies the types your
code supports (e.g., ``bytes | bytearray | memoryview``).
"""
__slots__ = ()
@@ -1179,4 +1164,4 @@ class MutableSequence(Sequence):
MutableSequence.register(list)
MutableSequence.register(bytearray) # Multiply inheriting, see ByteString
MutableSequence.register(bytearray)

263
Lib/_colorize.py vendored
View File

@@ -1,13 +1,16 @@
from __future__ import annotations
import io
import os
import sys
from collections.abc import Callable, Iterator, Mapping
from dataclasses import dataclass, field, Field
COLORIZE = True
# types
if False:
from typing import IO
from typing import IO, Self, ClassVar
_theme: Theme
class ANSIColors:
@@ -17,11 +20,13 @@ class ANSIColors:
BLUE = "\x1b[34m"
CYAN = "\x1b[36m"
GREEN = "\x1b[32m"
GREY = "\x1b[90m"
MAGENTA = "\x1b[35m"
RED = "\x1b[31m"
WHITE = "\x1b[37m" # more like LIGHT GRAY
YELLOW = "\x1b[33m"
BOLD = "\x1b[1m"
BOLD_BLACK = "\x1b[1;30m" # DARK GRAY
BOLD_BLUE = "\x1b[1;34m"
BOLD_CYAN = "\x1b[1;36m"
@@ -60,13 +65,196 @@ class ANSIColors:
INTENSE_BACKGROUND_YELLOW = "\x1b[103m"
ColorCodes = set()
NoColors = ANSIColors()
for attr in dir(NoColors):
for attr, code in ANSIColors.__dict__.items():
if not attr.startswith("__"):
ColorCodes.add(code)
setattr(NoColors, attr, "")
#
# Experimental theming support (see gh-133346)
#
# - Create a theme by copying an existing `Theme` with one or more sections
# replaced, using `default_theme.copy_with()`;
# - create a theme section by copying an existing `ThemeSection` with one or
# more colors replaced, using for example `default_theme.syntax.copy_with()`;
# - create a theme from scratch by instantiating a `Theme` data class with
# the required sections (which are also dataclass instances).
#
# Then call `_colorize.set_theme(your_theme)` to set it.
#
# Put your theme configuration in $PYTHONSTARTUP for the interactive shell,
# or sitecustomize.py in your virtual environment or Python installation for
# other uses. Your applications can call `_colorize.set_theme()` too.
#
# Note that thanks to the dataclasses providing default values for all fields,
# creating a new theme or theme section from scratch is possible without
# specifying all keys.
#
# For example, here's a theme that makes punctuation and operators less prominent:
#
# try:
# from _colorize import set_theme, default_theme, Syntax, ANSIColors
# except ImportError:
# pass
# else:
# theme_with_dim_operators = default_theme.copy_with(
# syntax=Syntax(op=ANSIColors.INTENSE_BLACK),
# )
# set_theme(theme_with_dim_operators)
# del set_theme, default_theme, Syntax, ANSIColors, theme_with_dim_operators
#
# Guarding the import ensures that your .pythonstartup file will still work in
# Python 3.13 and older. Deleting the variables ensures they don't remain in your
# interactive shell's global scope.
class ThemeSection(Mapping[str, str]):
"""A mixin/base class for theme sections.
It enables dictionary access to a section, as well as implements convenience
methods.
"""
# The two types below are just that: types to inform the type checker that the
# mixin will work in context of those fields existing
__dataclass_fields__: ClassVar[dict[str, Field[str]]]
_name_to_value: Callable[[str], str]
def __post_init__(self) -> None:
name_to_value = {}
for color_name in self.__dataclass_fields__:
name_to_value[color_name] = getattr(self, color_name)
super().__setattr__('_name_to_value', name_to_value.__getitem__)
def copy_with(self, **kwargs: str) -> Self:
color_state: dict[str, str] = {}
for color_name in self.__dataclass_fields__:
color_state[color_name] = getattr(self, color_name)
color_state.update(kwargs)
return type(self)(**color_state)
@classmethod
def no_colors(cls) -> Self:
color_state: dict[str, str] = {}
for color_name in cls.__dataclass_fields__:
color_state[color_name] = ""
return cls(**color_state)
def __getitem__(self, key: str) -> str:
return self._name_to_value(key)
def __len__(self) -> int:
return len(self.__dataclass_fields__)
def __iter__(self) -> Iterator[str]:
return iter(self.__dataclass_fields__)
@dataclass(frozen=True, kw_only=True)
class Argparse(ThemeSection):
usage: str = ANSIColors.BOLD_BLUE
prog: str = ANSIColors.BOLD_MAGENTA
prog_extra: str = ANSIColors.MAGENTA
heading: str = ANSIColors.BOLD_BLUE
summary_long_option: str = ANSIColors.CYAN
summary_short_option: str = ANSIColors.GREEN
summary_label: str = ANSIColors.YELLOW
summary_action: str = ANSIColors.GREEN
long_option: str = ANSIColors.BOLD_CYAN
short_option: str = ANSIColors.BOLD_GREEN
label: str = ANSIColors.BOLD_YELLOW
action: str = ANSIColors.BOLD_GREEN
reset: str = ANSIColors.RESET
@dataclass(frozen=True)
class Syntax(ThemeSection):
prompt: str = ANSIColors.BOLD_MAGENTA
keyword: str = ANSIColors.BOLD_BLUE
keyword_constant: str = ANSIColors.BOLD_BLUE
builtin: str = ANSIColors.CYAN
comment: str = ANSIColors.RED
string: str = ANSIColors.GREEN
number: str = ANSIColors.YELLOW
op: str = ANSIColors.RESET
definition: str = ANSIColors.BOLD
soft_keyword: str = ANSIColors.BOLD_BLUE
reset: str = ANSIColors.RESET
@dataclass(frozen=True)
class Traceback(ThemeSection):
type: str = ANSIColors.BOLD_MAGENTA
message: str = ANSIColors.MAGENTA
filename: str = ANSIColors.MAGENTA
line_no: str = ANSIColors.MAGENTA
frame: str = ANSIColors.MAGENTA
error_highlight: str = ANSIColors.BOLD_RED
error_range: str = ANSIColors.RED
reset: str = ANSIColors.RESET
@dataclass(frozen=True)
class Unittest(ThemeSection):
passed: str = ANSIColors.GREEN
warn: str = ANSIColors.YELLOW
fail: str = ANSIColors.RED
fail_info: str = ANSIColors.BOLD_RED
reset: str = ANSIColors.RESET
@dataclass(frozen=True)
class Theme:
"""A suite of themes for all sections of Python.
When adding a new one, remember to also modify `copy_with` and `no_colors`
below.
"""
argparse: Argparse = field(default_factory=Argparse)
syntax: Syntax = field(default_factory=Syntax)
traceback: Traceback = field(default_factory=Traceback)
unittest: Unittest = field(default_factory=Unittest)
def copy_with(
self,
*,
argparse: Argparse | None = None,
syntax: Syntax | None = None,
traceback: Traceback | None = None,
unittest: Unittest | None = None,
) -> Self:
"""Return a new Theme based on this instance with some sections replaced.
Themes are immutable to protect against accidental modifications that
could lead to invalid terminal states.
"""
return type(self)(
argparse=argparse or self.argparse,
syntax=syntax or self.syntax,
traceback=traceback or self.traceback,
unittest=unittest or self.unittest,
)
@classmethod
def no_colors(cls) -> Self:
"""Return a new Theme where colors in all sections are empty strings.
This allows writing user code as if colors are always used. The color
fields will be ANSI color code strings when colorization is desired
and possible, and empty strings otherwise.
"""
return cls(
argparse=Argparse.no_colors(),
syntax=Syntax.no_colors(),
traceback=Traceback.no_colors(),
unittest=Unittest.no_colors(),
)
def get_colors(
colorize: bool = False, *, file: IO[str] | IO[bytes] | None = None
) -> ANSIColors:
@@ -76,22 +264,37 @@ def get_colors(
return NoColors
def decolor(text: str) -> str:
"""Remove ANSI color codes from a string."""
for code in ColorCodes:
text = text.replace(code, "")
return text
def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool:
def _safe_getenv(k: str, fallback: str | None = None) -> str | None:
"""Exception-safe environment retrieval. See gh-128636."""
try:
return os.environ.get(k, fallback)
except Exception:
return fallback
if file is None:
file = sys.stdout
if not sys.flags.ignore_environment:
if os.environ.get("PYTHON_COLORS") == "0":
if _safe_getenv("PYTHON_COLORS") == "0":
return False
if os.environ.get("PYTHON_COLORS") == "1":
if _safe_getenv("PYTHON_COLORS") == "1":
return True
if os.environ.get("NO_COLOR"):
if _safe_getenv("NO_COLOR"):
return False
if not COLORIZE:
return False
if os.environ.get("FORCE_COLOR"):
if _safe_getenv("FORCE_COLOR"):
return True
if os.environ.get("TERM") == "dumb":
if _safe_getenv("TERM") == "dumb":
return False
if not hasattr(file, "fileno"):
@@ -108,5 +311,45 @@ def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool:
try:
return os.isatty(file.fileno())
except io.UnsupportedOperation:
except OSError:
return hasattr(file, "isatty") and file.isatty()
default_theme = Theme()
theme_no_color = default_theme.no_colors()
def get_theme(
*,
tty_file: IO[str] | IO[bytes] | None = None,
force_color: bool = False,
force_no_color: bool = False,
) -> Theme:
"""Returns the currently set theme, potentially in a zero-color variant.
In cases where colorizing is not possible (see `can_colorize`), the returned
theme contains all empty strings in all color definitions.
See `Theme.no_colors()` for more information.
It is recommended not to cache the result of this function for extended
periods of time because the user might influence theme selection by
the interactive shell, a debugger, or application-specific code. The
environment (including environment variable state and console configuration
on Windows) can also change in the course of the application life cycle.
"""
if force_color or (not force_no_color and
can_colorize(file=tty_file)):
return _theme
return theme_no_color
def set_theme(t: Theme) -> None:
global _theme
if not isinstance(t, Theme):
raise ValueError(f"Expected Theme object, found {t}")
_theme = t
set_theme(default_theme)

View File

@@ -22,7 +22,6 @@ IMPORT_MAPPING = {
'tkMessageBox': 'tkinter.messagebox',
'ScrolledText': 'tkinter.scrolledtext',
'Tkconstants': 'tkinter.constants',
'Tix': 'tkinter.tix',
'ttk': 'tkinter.ttk',
'Tkinter': 'tkinter',
'markupbase': '_markupbase',
@@ -257,3 +256,4 @@ PYTHON3_IMPORTERROR_EXCEPTIONS = (
for excname in PYTHON3_IMPORTERROR_EXCEPTIONS:
REVERSE_NAME_MAPPING[('builtins', excname)] = ('exceptions', 'ImportError')
del excname

149
Lib/_dummy_thread.py vendored
View File

@@ -11,15 +11,35 @@ Suggested usage is::
import _dummy_thread as _thread
"""
# Exports only things specified by thread documentation;
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
'interrupt_main', 'LockType', 'RLock',
'_count']
__all__ = [
"error",
"start_new_thread",
"exit",
"get_ident",
"allocate_lock",
"interrupt_main",
"LockType",
"RLock",
"_count",
"start_joinable_thread",
"daemon_threads_allowed",
"_shutdown",
"_make_thread_handle",
"_ThreadHandle",
"_get_main_thread_ident",
"_is_main_interpreter",
"_local",
]
# A dummy value
TIMEOUT_MAX = 2**31
# Main thread ident for dummy implementation
_MAIN_THREAD_IDENT = -1
# NOTE: this module can be imported early in the extension building process,
# and so top level imports of other modules should be avoided. Instead, all
# imports are done when needed on a function-by-function basis. Since threads
@@ -27,6 +47,7 @@ TIMEOUT_MAX = 2**31
error = RuntimeError
def start_new_thread(function, args, kwargs={}):
"""Dummy implementation of _thread.start_new_thread().
@@ -52,6 +73,7 @@ def start_new_thread(function, args, kwargs={}):
pass
except:
import traceback
traceback.print_exc()
_main = True
global _interrupt
@@ -59,10 +81,58 @@ def start_new_thread(function, args, kwargs={}):
_interrupt = False
raise KeyboardInterrupt
def start_joinable_thread(function, handle=None, daemon=True):
"""Dummy implementation of _thread.start_joinable_thread().
In dummy thread, we just run the function synchronously.
"""
if handle is None:
handle = _ThreadHandle()
try:
function()
except SystemExit:
pass
except:
import traceback
traceback.print_exc()
handle._set_done()
return handle
def daemon_threads_allowed():
"""Dummy implementation of _thread.daemon_threads_allowed()."""
return True
def _shutdown():
"""Dummy implementation of _thread._shutdown()."""
pass
def _make_thread_handle(ident):
"""Dummy implementation of _thread._make_thread_handle()."""
handle = _ThreadHandle()
handle._ident = ident
return handle
def _get_main_thread_ident():
"""Dummy implementation of _thread._get_main_thread_ident()."""
return _MAIN_THREAD_IDENT
def _is_main_interpreter():
"""Dummy implementation of _thread._is_main_interpreter()."""
return True
def exit():
"""Dummy implementation of _thread.exit()."""
raise SystemExit
def get_ident():
"""Dummy implementation of _thread.get_ident().
@@ -70,26 +140,31 @@ def get_ident():
available, it is safe to assume that the current process is the
only thread. Thus a constant can be safely returned.
"""
return -1
return _MAIN_THREAD_IDENT
def allocate_lock():
"""Dummy implementation of _thread.allocate_lock()."""
return LockType()
def stack_size(size=None):
"""Dummy implementation of _thread.stack_size()."""
if size is not None:
raise error("setting thread stack size not supported")
return 0
def _set_sentinel():
"""Dummy implementation of _thread._set_sentinel()."""
return LockType()
def _count():
"""Dummy implementation of _thread._count()."""
return 0
class LockType(object):
"""Class implementing dummy implementation of _thread.LockType.
@@ -125,6 +200,7 @@ class LockType(object):
else:
if timeout > 0:
import time
time.sleep(timeout)
return False
@@ -153,14 +229,41 @@ class LockType(object):
"locked" if self.locked_status else "unlocked",
self.__class__.__module__,
self.__class__.__qualname__,
hex(id(self))
hex(id(self)),
)
class _ThreadHandle:
"""Dummy implementation of _thread._ThreadHandle."""
def __init__(self):
self._ident = _MAIN_THREAD_IDENT
self._done = False
@property
def ident(self):
return self._ident
def _set_done(self):
self._done = True
def is_done(self):
return self._done
def join(self, timeout=None):
# In dummy thread, thread is always done
return
def __repr__(self):
return f"<_ThreadHandle ident={self._ident}>"
# Used to signal that interrupt_main was called in a "thread"
_interrupt = False
# True when not executing in a "thread"
_main = True
def interrupt_main():
"""Set _interrupt flag to True to have start_new_thread raise
KeyboardInterrupt upon exiting."""
@@ -170,6 +273,7 @@ def interrupt_main():
global _interrupt
_interrupt = True
class RLock:
def __init__(self):
self.locked_count = 0
@@ -190,7 +294,7 @@ class RLock:
return True
def locked(self):
return self.locked_status != 0
return self.locked_count != 0
def __repr__(self):
return "<%s %s.%s object owner=%s count=%s at %s>" % (
@@ -199,5 +303,36 @@ class RLock:
self.__class__.__qualname__,
get_ident() if self.locked_count else 0,
self.locked_count,
hex(id(self))
hex(id(self)),
)
class _local:
"""Dummy implementation of _thread._local (thread-local storage)."""
def __init__(self):
object.__setattr__(self, "_local__impl", {})
def __getattribute__(self, name):
if name.startswith("_local__"):
return object.__getattribute__(self, name)
impl = object.__getattribute__(self, "_local__impl")
try:
return impl[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if name.startswith("_local__"):
return object.__setattr__(self, name, value)
impl = object.__getattribute__(self, "_local__impl")
impl[name] = value
def __delattr__(self, name):
if name.startswith("_local__"):
return object.__delattr__(self, name)
impl = object.__getattribute__(self, "_local__impl")
try:
del impl[name]
except KeyError:
raise AttributeError(name)

2
Lib/_markupbase.py vendored
View File

@@ -13,7 +13,7 @@ _commentclose = re.compile(r'--\s*>')
_markedsectionclose = re.compile(r']\s*]\s*>')
# An analysis of the MS-Word extensions is available at
# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf
# http://web.archive.org/web/20060321153828/http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf
_msmarkedsectionclose = re.compile(r']\s*>')

371
Lib/_opcode_metadata.py generated vendored Normal file
View File

@@ -0,0 +1,371 @@
# This file is generated by scripts/generate_opcode_metadata.py
# for RustPython bytecode format (CPython 3.14 compatible opcode numbers).
# Do not edit!
_specializations = {
"RESUME": [
"RESUME_CHECK",
],
"LOAD_CONST": [
"LOAD_CONST_MORTAL",
"LOAD_CONST_IMMORTAL",
],
"TO_BOOL": [
"TO_BOOL_ALWAYS_TRUE",
"TO_BOOL_BOOL",
"TO_BOOL_INT",
"TO_BOOL_LIST",
"TO_BOOL_NONE",
"TO_BOOL_STR",
],
"BINARY_OP": [
"BINARY_OP_MULTIPLY_INT",
"BINARY_OP_ADD_INT",
"BINARY_OP_SUBTRACT_INT",
"BINARY_OP_MULTIPLY_FLOAT",
"BINARY_OP_ADD_FLOAT",
"BINARY_OP_SUBTRACT_FLOAT",
"BINARY_OP_ADD_UNICODE",
"BINARY_OP_SUBSCR_LIST_INT",
"BINARY_OP_SUBSCR_LIST_SLICE",
"BINARY_OP_SUBSCR_TUPLE_INT",
"BINARY_OP_SUBSCR_STR_INT",
"BINARY_OP_SUBSCR_DICT",
"BINARY_OP_SUBSCR_GETITEM",
"BINARY_OP_EXTEND",
"BINARY_OP_INPLACE_ADD_UNICODE",
],
"STORE_SUBSCR": [
"STORE_SUBSCR_DICT",
"STORE_SUBSCR_LIST_INT",
],
"SEND": [
"SEND_GEN",
],
"UNPACK_SEQUENCE": [
"UNPACK_SEQUENCE_TWO_TUPLE",
"UNPACK_SEQUENCE_TUPLE",
"UNPACK_SEQUENCE_LIST",
],
"STORE_ATTR": [
"STORE_ATTR_INSTANCE_VALUE",
"STORE_ATTR_SLOT",
"STORE_ATTR_WITH_HINT",
],
"LOAD_GLOBAL": [
"LOAD_GLOBAL_MODULE",
"LOAD_GLOBAL_BUILTIN",
],
"LOAD_SUPER_ATTR": [
"LOAD_SUPER_ATTR_ATTR",
"LOAD_SUPER_ATTR_METHOD",
],
"LOAD_ATTR": [
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_WITH_HINT",
"LOAD_ATTR_SLOT",
"LOAD_ATTR_CLASS",
"LOAD_ATTR_CLASS_WITH_METACLASS_CHECK",
"LOAD_ATTR_PROPERTY",
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
"LOAD_ATTR_METHOD_WITH_VALUES",
"LOAD_ATTR_METHOD_NO_DICT",
"LOAD_ATTR_METHOD_LAZY_DICT",
"LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES",
"LOAD_ATTR_NONDESCRIPTOR_NO_DICT",
],
"COMPARE_OP": [
"COMPARE_OP_FLOAT",
"COMPARE_OP_INT",
"COMPARE_OP_STR",
],
"CONTAINS_OP": [
"CONTAINS_OP_SET",
"CONTAINS_OP_DICT",
],
"JUMP_BACKWARD": [
"JUMP_BACKWARD_NO_JIT",
"JUMP_BACKWARD_JIT",
],
"FOR_ITER": [
"FOR_ITER_LIST",
"FOR_ITER_TUPLE",
"FOR_ITER_RANGE",
"FOR_ITER_GEN",
],
"CALL": [
"CALL_BOUND_METHOD_EXACT_ARGS",
"CALL_PY_EXACT_ARGS",
"CALL_TYPE_1",
"CALL_STR_1",
"CALL_TUPLE_1",
"CALL_BUILTIN_CLASS",
"CALL_BUILTIN_O",
"CALL_BUILTIN_FAST",
"CALL_BUILTIN_FAST_WITH_KEYWORDS",
"CALL_LEN",
"CALL_ISINSTANCE",
"CALL_LIST_APPEND",
"CALL_METHOD_DESCRIPTOR_O",
"CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS",
"CALL_METHOD_DESCRIPTOR_NOARGS",
"CALL_METHOD_DESCRIPTOR_FAST",
"CALL_ALLOC_AND_ENTER_INIT",
"CALL_PY_GENERAL",
"CALL_BOUND_METHOD_GENERAL",
"CALL_NON_PY_GENERAL",
],
"CALL_KW": [
"CALL_KW_BOUND_METHOD",
"CALL_KW_PY",
"CALL_KW_NON_PY",
],
}
_specialized_opmap = {
'BINARY_OP_ADD_FLOAT': 129,
'BINARY_OP_ADD_INT': 130,
'BINARY_OP_ADD_UNICODE': 131,
'BINARY_OP_EXTEND': 132,
'BINARY_OP_INPLACE_ADD_UNICODE': 3,
'BINARY_OP_MULTIPLY_FLOAT': 133,
'BINARY_OP_MULTIPLY_INT': 134,
'BINARY_OP_SUBSCR_DICT': 135,
'BINARY_OP_SUBSCR_GETITEM': 136,
'BINARY_OP_SUBSCR_LIST_INT': 137,
'BINARY_OP_SUBSCR_LIST_SLICE': 138,
'BINARY_OP_SUBSCR_STR_INT': 139,
'BINARY_OP_SUBSCR_TUPLE_INT': 140,
'BINARY_OP_SUBTRACT_FLOAT': 141,
'BINARY_OP_SUBTRACT_INT': 142,
'CALL_ALLOC_AND_ENTER_INIT': 143,
'CALL_BOUND_METHOD_EXACT_ARGS': 144,
'CALL_BOUND_METHOD_GENERAL': 145,
'CALL_BUILTIN_CLASS': 146,
'CALL_BUILTIN_FAST': 147,
'CALL_BUILTIN_FAST_WITH_KEYWORDS': 148,
'CALL_BUILTIN_O': 149,
'CALL_ISINSTANCE': 150,
'CALL_KW_BOUND_METHOD': 151,
'CALL_KW_NON_PY': 152,
'CALL_KW_PY': 153,
'CALL_LEN': 154,
'CALL_LIST_APPEND': 155,
'CALL_METHOD_DESCRIPTOR_FAST': 156,
'CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS': 157,
'CALL_METHOD_DESCRIPTOR_NOARGS': 158,
'CALL_METHOD_DESCRIPTOR_O': 159,
'CALL_NON_PY_GENERAL': 160,
'CALL_PY_EXACT_ARGS': 161,
'CALL_PY_GENERAL': 162,
'CALL_STR_1': 163,
'CALL_TUPLE_1': 164,
'CALL_TYPE_1': 165,
'COMPARE_OP_FLOAT': 166,
'COMPARE_OP_INT': 167,
'COMPARE_OP_STR': 168,
'CONTAINS_OP_DICT': 169,
'CONTAINS_OP_SET': 170,
'FOR_ITER_GEN': 171,
'FOR_ITER_LIST': 172,
'FOR_ITER_RANGE': 173,
'FOR_ITER_TUPLE': 174,
'JUMP_BACKWARD_JIT': 175,
'JUMP_BACKWARD_NO_JIT': 176,
'LOAD_ATTR_CLASS': 177,
'LOAD_ATTR_CLASS_WITH_METACLASS_CHECK': 178,
'LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN': 179,
'LOAD_ATTR_INSTANCE_VALUE': 180,
'LOAD_ATTR_METHOD_LAZY_DICT': 181,
'LOAD_ATTR_METHOD_NO_DICT': 182,
'LOAD_ATTR_METHOD_WITH_VALUES': 183,
'LOAD_ATTR_MODULE': 184,
'LOAD_ATTR_NONDESCRIPTOR_NO_DICT': 185,
'LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES': 186,
'LOAD_ATTR_PROPERTY': 187,
'LOAD_ATTR_SLOT': 188,
'LOAD_ATTR_WITH_HINT': 189,
'LOAD_CONST_IMMORTAL': 190,
'LOAD_CONST_MORTAL': 191,
'LOAD_GLOBAL_BUILTIN': 192,
'LOAD_GLOBAL_MODULE': 193,
'LOAD_SUPER_ATTR_ATTR': 194,
'LOAD_SUPER_ATTR_METHOD': 195,
'RESUME_CHECK': 196,
'SEND_GEN': 197,
'STORE_ATTR_INSTANCE_VALUE': 198,
'STORE_ATTR_SLOT': 199,
'STORE_ATTR_WITH_HINT': 200,
'STORE_SUBSCR_DICT': 201,
'STORE_SUBSCR_LIST_INT': 202,
'TO_BOOL_ALWAYS_TRUE': 203,
'TO_BOOL_BOOL': 204,
'TO_BOOL_INT': 205,
'TO_BOOL_LIST': 206,
'TO_BOOL_NONE': 207,
'TO_BOOL_STR': 208,
'UNPACK_SEQUENCE_LIST': 209,
'UNPACK_SEQUENCE_TUPLE': 210,
'UNPACK_SEQUENCE_TWO_TUPLE': 211,
}
opmap = {
'CACHE': 0,
'RESERVED': 17,
'RESUME': 128,
'INSTRUMENTED_LINE': 254,
'ENTER_EXECUTOR': 255,
'BINARY_SLICE': 1,
'BUILD_TEMPLATE': 2,
'CALL_FUNCTION_EX': 4,
'CHECK_EG_MATCH': 5,
'CHECK_EXC_MATCH': 6,
'CLEANUP_THROW': 7,
'DELETE_SUBSCR': 8,
'END_FOR': 9,
'END_SEND': 10,
'EXIT_INIT_CHECK': 11,
'FORMAT_SIMPLE': 12,
'FORMAT_WITH_SPEC': 13,
'GET_AITER': 14,
'GET_ANEXT': 15,
'GET_ITER': 16,
'GET_LEN': 18,
'GET_YIELD_FROM_ITER': 19,
'INTERPRETER_EXIT': 20,
'LOAD_BUILD_CLASS': 21,
'LOAD_LOCALS': 22,
'MAKE_FUNCTION': 23,
'MATCH_KEYS': 24,
'MATCH_MAPPING': 25,
'MATCH_SEQUENCE': 26,
'NOP': 27,
'NOT_TAKEN': 28,
'POP_EXCEPT': 29,
'POP_ITER': 30,
'POP_TOP': 31,
'PUSH_EXC_INFO': 32,
'PUSH_NULL': 33,
'RETURN_GENERATOR': 34,
'RETURN_VALUE': 35,
'SETUP_ANNOTATIONS': 36,
'STORE_SLICE': 37,
'STORE_SUBSCR': 38,
'TO_BOOL': 39,
'UNARY_INVERT': 40,
'UNARY_NEGATIVE': 41,
'UNARY_NOT': 42,
'WITH_EXCEPT_START': 43,
'BINARY_OP': 44,
'BUILD_INTERPOLATION': 45,
'BUILD_LIST': 46,
'BUILD_MAP': 47,
'BUILD_SET': 48,
'BUILD_SLICE': 49,
'BUILD_STRING': 50,
'BUILD_TUPLE': 51,
'CALL': 52,
'CALL_INTRINSIC_1': 53,
'CALL_INTRINSIC_2': 54,
'CALL_KW': 55,
'COMPARE_OP': 56,
'CONTAINS_OP': 57,
'CONVERT_VALUE': 58,
'COPY': 59,
'COPY_FREE_VARS': 60,
'DELETE_ATTR': 61,
'DELETE_DEREF': 62,
'DELETE_FAST': 63,
'DELETE_GLOBAL': 64,
'DELETE_NAME': 65,
'DICT_MERGE': 66,
'DICT_UPDATE': 67,
'END_ASYNC_FOR': 68,
'EXTENDED_ARG': 69,
'FOR_ITER': 70,
'GET_AWAITABLE': 71,
'IMPORT_FROM': 72,
'IMPORT_NAME': 73,
'IS_OP': 74,
'JUMP_BACKWARD': 75,
'JUMP_BACKWARD_NO_INTERRUPT': 76,
'JUMP_FORWARD': 77,
'LIST_APPEND': 78,
'LIST_EXTEND': 79,
'LOAD_ATTR': 80,
'LOAD_COMMON_CONSTANT': 81,
'LOAD_CONST': 82,
'LOAD_DEREF': 83,
'LOAD_FAST': 84,
'LOAD_FAST_AND_CLEAR': 85,
'LOAD_FAST_BORROW': 86,
'LOAD_FAST_BORROW_LOAD_FAST_BORROW': 87,
'LOAD_FAST_CHECK': 88,
'LOAD_FAST_LOAD_FAST': 89,
'LOAD_FROM_DICT_OR_DEREF': 90,
'LOAD_FROM_DICT_OR_GLOBALS': 91,
'LOAD_GLOBAL': 92,
'LOAD_NAME': 93,
'LOAD_SMALL_INT': 94,
'LOAD_SPECIAL': 95,
'LOAD_SUPER_ATTR': 96,
'MAKE_CELL': 97,
'MAP_ADD': 98,
'MATCH_CLASS': 99,
'POP_JUMP_IF_FALSE': 100,
'POP_JUMP_IF_NONE': 101,
'POP_JUMP_IF_NOT_NONE': 102,
'POP_JUMP_IF_TRUE': 103,
'RAISE_VARARGS': 104,
'RERAISE': 105,
'SEND': 106,
'SET_ADD': 107,
'SET_FUNCTION_ATTRIBUTE': 108,
'SET_UPDATE': 109,
'STORE_ATTR': 110,
'STORE_DEREF': 111,
'STORE_FAST': 112,
'STORE_FAST_LOAD_FAST': 113,
'STORE_FAST_STORE_FAST': 114,
'STORE_GLOBAL': 115,
'STORE_NAME': 116,
'SWAP': 117,
'UNPACK_EX': 118,
'UNPACK_SEQUENCE': 119,
'YIELD_VALUE': 120,
'INSTRUMENTED_END_FOR': 234,
'INSTRUMENTED_POP_ITER': 235,
'INSTRUMENTED_END_SEND': 236,
'INSTRUMENTED_FOR_ITER': 237,
'INSTRUMENTED_INSTRUCTION': 238,
'INSTRUMENTED_JUMP_FORWARD': 239,
'INSTRUMENTED_NOT_TAKEN': 240,
'INSTRUMENTED_POP_JUMP_IF_TRUE': 241,
'INSTRUMENTED_POP_JUMP_IF_FALSE': 242,
'INSTRUMENTED_POP_JUMP_IF_NONE': 243,
'INSTRUMENTED_POP_JUMP_IF_NOT_NONE': 244,
'INSTRUMENTED_RESUME': 245,
'INSTRUMENTED_RETURN_VALUE': 246,
'INSTRUMENTED_YIELD_VALUE': 247,
'INSTRUMENTED_END_ASYNC_FOR': 248,
'INSTRUMENTED_LOAD_SUPER_ATTR': 249,
'INSTRUMENTED_CALL': 250,
'INSTRUMENTED_CALL_KW': 251,
'INSTRUMENTED_CALL_FUNCTION_EX': 252,
'INSTRUMENTED_JUMP_BACKWARD': 253,
'ANNOTATIONS_PLACEHOLDER': 256,
'JUMP': 257,
'JUMP_IF_FALSE': 258,
'JUMP_IF_TRUE': 259,
'JUMP_NO_INTERRUPT': 260,
'LOAD_CLOSURE': 261,
'POP_BLOCK': 262,
'SETUP_CLEANUP': 263,
'SETUP_FINALLY': 264,
'SETUP_WITH': 265,
'STORE_FAST_MAYBE_NULL': 266,
}
HAVE_ARGUMENT = 43
MIN_INSTRUMENTED_OPCODE = 234

869
Lib/_py_warnings.py vendored Normal file
View File

@@ -0,0 +1,869 @@
"""Python part of the warnings subsystem."""
import sys
import _contextvars
import _thread
__all__ = ["warn", "warn_explicit", "showwarning",
"formatwarning", "filterwarnings", "simplefilter",
"resetwarnings", "catch_warnings", "deprecated"]
# Normally '_wm' is sys.modules['warnings'] but for unit tests it can be
# a different module. User code is allowed to reassign global attributes
# of the 'warnings' module, commonly 'filters' or 'showwarning'. So we
# need to lookup these global attributes dynamically on the '_wm' object,
# rather than binding them earlier. The code in this module consistently uses
# '_wm.<something>' rather than using the globals of this module. If the
# '_warnings' C extension is in use, some globals are replaced by functions
# and variables defined in that extension.
_wm = None
def _set_module(module):
global _wm
_wm = module
# filters contains a sequence of filter 5-tuples
# The components of the 5-tuple are:
# - an action: error, ignore, always, all, default, module, or once
# - a compiled regex that must match the warning message
# - a class representing the warning category
# - a compiled regex that must match the module that is being warned
# - a line number for the line being warning, or 0 to mean any line
# If either if the compiled regexs are None, match anything.
filters = []
defaultaction = "default"
onceregistry = {}
_lock = _thread.RLock()
_filters_version = 1
# If true, catch_warnings() will use a context var to hold the modified
# filters list. Otherwise, catch_warnings() will operate on the 'filters'
# global of the warnings module.
_use_context = sys.flags.context_aware_warnings
class _Context:
def __init__(self, filters):
self._filters = filters
self.log = None # if set to a list, logging is enabled
def copy(self):
context = _Context(self._filters[:])
if self.log is not None:
context.log = self.log
return context
def _record_warning(self, msg):
self.log.append(msg)
class _GlobalContext(_Context):
def __init__(self):
self.log = None
@property
def _filters(self):
# Since there is quite a lot of code that assigns to
# warnings.filters, this needs to return the current value of
# the module global.
try:
return _wm.filters
except AttributeError:
# 'filters' global was deleted. Do we need to actually handle this case?
return []
_global_context = _GlobalContext()
_warnings_context = _contextvars.ContextVar('warnings_context')
def _get_context():
if not _use_context:
return _global_context
try:
return _wm._warnings_context.get()
except LookupError:
return _global_context
def _set_context(context):
assert _use_context
_wm._warnings_context.set(context)
def _new_context():
assert _use_context
old_context = _wm._get_context()
new_context = old_context.copy()
_wm._set_context(new_context)
return old_context, new_context
def _get_filters():
"""Return the current list of filters. This is a non-public API used by
module functions and by the unit tests."""
return _wm._get_context()._filters
def _filters_mutated_lock_held():
_wm._filters_version += 1
def showwarning(message, category, filename, lineno, file=None, line=None):
"""Hook to write a warning to a file; replace if you like."""
msg = _wm.WarningMessage(message, category, filename, lineno, file, line)
_wm._showwarnmsg_impl(msg)
def formatwarning(message, category, filename, lineno, line=None):
"""Function to format a warning the standard way."""
msg = _wm.WarningMessage(message, category, filename, lineno, None, line)
return _wm._formatwarnmsg_impl(msg)
def _showwarnmsg_impl(msg):
context = _wm._get_context()
if context.log is not None:
context._record_warning(msg)
return
file = msg.file
if file is None:
file = sys.stderr
if file is None:
# sys.stderr is None when run with pythonw.exe:
# warnings get lost
return
text = _wm._formatwarnmsg(msg)
try:
file.write(text)
except OSError:
# the file (probably stderr) is invalid - this warning gets lost.
pass
def _formatwarnmsg_impl(msg):
category = msg.category.__name__
s = f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n"
if msg.line is None:
try:
import linecache
line = linecache.getline(msg.filename, msg.lineno)
except Exception:
# When a warning is logged during Python shutdown, linecache
# and the import machinery don't work anymore
line = None
linecache = None
else:
line = msg.line
if line:
line = line.strip()
s += " %s\n" % line
if msg.source is not None:
try:
import tracemalloc
# Logging a warning should not raise a new exception:
# catch Exception, not only ImportError and RecursionError.
except Exception:
# don't suggest to enable tracemalloc if it's not available
suggest_tracemalloc = False
tb = None
else:
try:
suggest_tracemalloc = not tracemalloc.is_tracing()
tb = tracemalloc.get_object_traceback(msg.source)
except Exception:
# When a warning is logged during Python shutdown, tracemalloc
# and the import machinery don't work anymore
suggest_tracemalloc = False
tb = None
if tb is not None:
s += 'Object allocated at (most recent call last):\n'
for frame in tb:
s += (' File "%s", lineno %s\n'
% (frame.filename, frame.lineno))
try:
if linecache is not None:
line = linecache.getline(frame.filename, frame.lineno)
else:
line = None
except Exception:
line = None
if line:
line = line.strip()
s += ' %s\n' % line
elif suggest_tracemalloc:
s += (f'{category}: Enable tracemalloc to get the object '
f'allocation traceback\n')
return s
# Keep a reference to check if the function was replaced
_showwarning_orig = showwarning
def _showwarnmsg(msg):
"""Hook to write a warning to a file; replace if you like."""
try:
sw = _wm.showwarning
except AttributeError:
pass
else:
if sw is not _showwarning_orig:
# warnings.showwarning() was replaced
if not callable(sw):
raise TypeError("warnings.showwarning() must be set to a "
"function or method")
sw(msg.message, msg.category, msg.filename, msg.lineno,
msg.file, msg.line)
return
_wm._showwarnmsg_impl(msg)
# Keep a reference to check if the function was replaced
_formatwarning_orig = formatwarning
def _formatwarnmsg(msg):
"""Function to format a warning the standard way."""
try:
fw = _wm.formatwarning
except AttributeError:
pass
else:
if fw is not _formatwarning_orig:
# warnings.formatwarning() was replaced
return fw(msg.message, msg.category,
msg.filename, msg.lineno, msg.line)
return _wm._formatwarnmsg_impl(msg)
def filterwarnings(action, message="", category=Warning, module="", lineno=0,
append=False):
"""Insert an entry into the list of warnings filters (at the front).
'action' -- one of "error", "ignore", "always", "all", "default", "module",
or "once"
'message' -- a regex that the warning message must match
'category' -- a class that the warning must be a subclass of
'module' -- a regex that the module name must match
'lineno' -- an integer line number, 0 matches all warnings
'append' -- if true, append to the list of filters
"""
if action not in {"error", "ignore", "always", "all", "default", "module", "once"}:
raise ValueError(f"invalid action: {action!r}")
if not isinstance(message, str):
raise TypeError("message must be a string")
if not isinstance(category, type) or not issubclass(category, Warning):
raise TypeError("category must be a Warning subclass")
if not isinstance(module, str):
raise TypeError("module must be a string")
if not isinstance(lineno, int):
raise TypeError("lineno must be an int")
if lineno < 0:
raise ValueError("lineno must be an int >= 0")
if message or module:
import re
if message:
message = re.compile(message, re.I)
else:
message = None
if module:
module = re.compile(module)
else:
module = None
_wm._add_filter(action, message, category, module, lineno, append=append)
def simplefilter(action, category=Warning, lineno=0, append=False):
"""Insert a simple entry into the list of warnings filters (at the front).
A simple filter matches all modules and messages.
'action' -- one of "error", "ignore", "always", "all", "default", "module",
or "once"
'category' -- a class that the warning must be a subclass of
'lineno' -- an integer line number, 0 matches all warnings
'append' -- if true, append to the list of filters
"""
if action not in {"error", "ignore", "always", "all", "default", "module", "once"}:
raise ValueError(f"invalid action: {action!r}")
if not isinstance(lineno, int):
raise TypeError("lineno must be an int")
if lineno < 0:
raise ValueError("lineno must be an int >= 0")
_wm._add_filter(action, None, category, None, lineno, append=append)
def _filters_mutated():
# Even though this function is not part of the public API, it's used by
# a fair amount of user code.
with _wm._lock:
_wm._filters_mutated_lock_held()
def _add_filter(*item, append):
with _wm._lock:
filters = _wm._get_filters()
if not append:
# Remove possible duplicate filters, so new one will be placed
# in correct place. If append=True and duplicate exists, do nothing.
try:
filters.remove(item)
except ValueError:
pass
filters.insert(0, item)
else:
if item not in filters:
filters.append(item)
_wm._filters_mutated_lock_held()
def resetwarnings():
"""Clear the list of warning filters, so that no filters are active."""
with _wm._lock:
del _wm._get_filters()[:]
_wm._filters_mutated_lock_held()
class _OptionError(Exception):
"""Exception used by option processing helpers."""
pass
# Helper to process -W options passed via sys.warnoptions
def _processoptions(args):
for arg in args:
try:
_wm._setoption(arg)
except _wm._OptionError as msg:
print("Invalid -W option ignored:", msg, file=sys.stderr)
# Helper for _processoptions()
def _setoption(arg):
parts = arg.split(':')
if len(parts) > 5:
raise _wm._OptionError("too many fields (max 5): %r" % (arg,))
while len(parts) < 5:
parts.append('')
action, message, category, module, lineno = [s.strip()
for s in parts]
action = _wm._getaction(action)
category = _wm._getcategory(category)
if message or module:
import re
if message:
message = re.escape(message)
if module:
module = re.escape(module) + r'\z'
if lineno:
try:
lineno = int(lineno)
if lineno < 0:
raise ValueError
except (ValueError, OverflowError):
raise _wm._OptionError("invalid lineno %r" % (lineno,)) from None
else:
lineno = 0
_wm.filterwarnings(action, message, category, module, lineno)
# Helper for _setoption()
def _getaction(action):
if not action:
return "default"
for a in ('default', 'always', 'all', 'ignore', 'module', 'once', 'error'):
if a.startswith(action):
return a
raise _wm._OptionError("invalid action: %r" % (action,))
# Helper for _setoption()
def _getcategory(category):
if not category:
return Warning
if '.' not in category:
import builtins as m
klass = category
else:
module, _, klass = category.rpartition('.')
try:
m = __import__(module, None, None, [klass])
except ImportError:
raise _wm._OptionError("invalid module name: %r" % (module,)) from None
try:
cat = getattr(m, klass)
except AttributeError:
raise _wm._OptionError("unknown warning category: %r" % (category,)) from None
if not issubclass(cat, Warning):
raise _wm._OptionError("invalid warning category: %r" % (category,))
return cat
def _is_internal_filename(filename):
return 'importlib' in filename and '_bootstrap' in filename
def _is_filename_to_skip(filename, skip_file_prefixes):
return any(filename.startswith(prefix) for prefix in skip_file_prefixes)
def _is_internal_frame(frame):
"""Signal whether the frame is an internal CPython implementation detail."""
return _is_internal_filename(frame.f_code.co_filename)
def _next_external_frame(frame, skip_file_prefixes):
"""Find the next frame that doesn't involve Python or user internals."""
frame = frame.f_back
while frame is not None and (
_is_internal_filename(filename := frame.f_code.co_filename) or
_is_filename_to_skip(filename, skip_file_prefixes)):
frame = frame.f_back
return frame
# Code typically replaced by _warnings
def warn(message, category=None, stacklevel=1, source=None,
*, skip_file_prefixes=()):
"""Issue a warning, or maybe ignore it or raise an exception."""
# Check if message is already a Warning object
if isinstance(message, Warning):
category = message.__class__
# Check category argument
if category is None:
category = UserWarning
if not (isinstance(category, type) and issubclass(category, Warning)):
raise TypeError("category must be a Warning subclass, "
"not '{:s}'".format(type(category).__name__))
if not isinstance(skip_file_prefixes, tuple):
# The C version demands a tuple for implementation performance.
raise TypeError('skip_file_prefixes must be a tuple of strs.')
if skip_file_prefixes:
stacklevel = max(2, stacklevel)
# Get context information
try:
if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
# If frame is too small to care or if the warning originated in
# internal code, then do not try to hide any frames.
frame = sys._getframe(stacklevel)
else:
frame = sys._getframe(1)
# Look for one frame less since the above line starts us off.
for x in range(stacklevel-1):
frame = _next_external_frame(frame, skip_file_prefixes)
if frame is None:
raise ValueError
except ValueError:
globals = sys.__dict__
filename = "<sys>"
lineno = 0
else:
globals = frame.f_globals
filename = frame.f_code.co_filename
lineno = frame.f_lineno
if '__name__' in globals:
module = globals['__name__']
else:
module = "<string>"
registry = globals.setdefault("__warningregistry__", {})
_wm.warn_explicit(
message,
category,
filename,
lineno,
module,
registry,
globals,
source=source,
)
def warn_explicit(message, category, filename, lineno,
module=None, registry=None, module_globals=None,
source=None):
lineno = int(lineno)
if module is None:
module = filename or "<unknown>"
if module[-3:].lower() == ".py":
module = module[:-3] # XXX What about leading pathname?
if isinstance(message, Warning):
text = str(message)
category = message.__class__
else:
text = message
message = category(message)
key = (text, category, lineno)
with _wm._lock:
if registry is None:
registry = {}
if registry.get('version', 0) != _wm._filters_version:
registry.clear()
registry['version'] = _wm._filters_version
# Quick test for common case
if registry.get(key):
return
# Search the filters
for item in _wm._get_filters():
action, msg, cat, mod, ln = item
if ((msg is None or msg.match(text)) and
issubclass(category, cat) and
(mod is None or mod.match(module)) and
(ln == 0 or lineno == ln)):
break
else:
action = _wm.defaultaction
# Early exit actions
if action == "ignore":
return
if action == "error":
raise message
# Other actions
if action == "once":
registry[key] = 1
oncekey = (text, category)
if _wm.onceregistry.get(oncekey):
return
_wm.onceregistry[oncekey] = 1
elif action in {"always", "all"}:
pass
elif action == "module":
registry[key] = 1
altkey = (text, category, 0)
if registry.get(altkey):
return
registry[altkey] = 1
elif action == "default":
registry[key] = 1
else:
# Unrecognized actions are errors
raise RuntimeError(
"Unrecognized action (%r) in warnings.filters:\n %s" %
(action, item))
# Prime the linecache for formatting, in case the
# "file" is actually in a zipfile or something.
import linecache
linecache.getlines(filename, module_globals)
# Print message and context
msg = _wm.WarningMessage(message, category, filename, lineno, source=source)
_wm._showwarnmsg(msg)
class WarningMessage(object):
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
"line", "source")
def __init__(self, message, category, filename, lineno, file=None,
line=None, source=None):
self.message = message
self.category = category
self.filename = filename
self.lineno = lineno
self.file = file
self.line = line
self.source = source
self._category_name = category.__name__ if category else None
def __str__(self):
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
"line : %r}" % (self.message, self._category_name,
self.filename, self.lineno, self.line))
def __repr__(self):
return f'<{type(self).__qualname__} {self}>'
class catch_warnings(object):
"""A context manager that copies and restores the warnings filter upon
exiting the context.
The 'record' argument specifies whether warnings should be captured by a
custom implementation of warnings.showwarning() and be appended to a list
returned by the context manager. Otherwise None is returned by the context
manager. The objects appended to the list are arguments whose attributes
mirror the arguments to showwarning().
The 'module' argument is to specify an alternative module to the module
named 'warnings' and imported under that name. This argument is only useful
when testing the warnings module itself.
If the 'action' argument is not None, the remaining arguments are passed
to warnings.simplefilter() as if it were called immediately on entering the
context.
"""
def __init__(self, *, record=False, module=None,
action=None, category=Warning, lineno=0, append=False):
"""Specify whether to record warnings and if an alternative module
should be used other than sys.modules['warnings'].
"""
self._record = record
self._module = sys.modules['warnings'] if module is None else module
self._entered = False
if action is None:
self._filter = None
else:
self._filter = (action, category, lineno, append)
def __repr__(self):
args = []
if self._record:
args.append("record=True")
if self._module is not sys.modules['warnings']:
args.append("module=%r" % self._module)
name = type(self).__name__
return "%s(%s)" % (name, ", ".join(args))
def __enter__(self):
if self._entered:
raise RuntimeError("Cannot enter %r twice" % self)
self._entered = True
with _wm._lock:
if _use_context:
self._saved_context, context = self._module._new_context()
else:
context = None
self._filters = self._module.filters
self._module.filters = self._filters[:]
self._showwarning = self._module.showwarning
self._showwarnmsg_impl = self._module._showwarnmsg_impl
self._module._filters_mutated_lock_held()
if self._record:
if _use_context:
context.log = log = []
else:
log = []
self._module._showwarnmsg_impl = log.append
# Reset showwarning() to the default implementation to make sure
# that _showwarnmsg() calls _showwarnmsg_impl()
self._module.showwarning = self._module._showwarning_orig
else:
log = None
if self._filter is not None:
self._module.simplefilter(*self._filter)
return log
def __exit__(self, *exc_info):
if not self._entered:
raise RuntimeError("Cannot exit %r without entering first" % self)
with _wm._lock:
if _use_context:
self._module._warnings_context.set(self._saved_context)
else:
self._module.filters = self._filters
self._module.showwarning = self._showwarning
self._module._showwarnmsg_impl = self._showwarnmsg_impl
self._module._filters_mutated_lock_held()
class deprecated:
"""Indicate that a class, function or overload is deprecated.
When this decorator is applied to an object, the type checker
will generate a diagnostic on usage of the deprecated object.
Usage:
@deprecated("Use B instead")
class A:
pass
@deprecated("Use g instead")
def f():
pass
@overload
@deprecated("int support is deprecated")
def g(x: int) -> int: ...
@overload
def g(x: str) -> int: ...
The warning specified by *category* will be emitted at runtime
on use of deprecated objects. For functions, that happens on calls;
for classes, on instantiation and on creation of subclasses.
If the *category* is ``None``, no warning is emitted at runtime.
The *stacklevel* determines where the
warning is emitted. If it is ``1`` (the default), the warning
is emitted at the direct caller of the deprecated object; if it
is higher, it is emitted further up the stack.
Static type checker behavior is not affected by the *category*
and *stacklevel* arguments.
The deprecation message passed to the decorator is saved in the
``__deprecated__`` attribute on the decorated object.
If applied to an overload, the decorator
must be after the ``@overload`` decorator for the attribute to
exist on the overload as returned by ``get_overloads()``.
See PEP 702 for details.
"""
def __init__(
self,
message: str,
/,
*,
category: type[Warning] | None = DeprecationWarning,
stacklevel: int = 1,
) -> None:
if not isinstance(message, str):
raise TypeError(
f"Expected an object of type str for 'message', not {type(message).__name__!r}"
)
self.message = message
self.category = category
self.stacklevel = stacklevel
def __call__(self, arg, /):
# Make sure the inner functions created below don't
# retain a reference to self.
msg = self.message
category = self.category
stacklevel = self.stacklevel
if category is None:
arg.__deprecated__ = msg
return arg
elif isinstance(arg, type):
import functools
from types import MethodType
original_new = arg.__new__
@functools.wraps(original_new)
def __new__(cls, /, *args, **kwargs):
if cls is arg:
_wm.warn(msg, category=category, stacklevel=stacklevel + 1)
if original_new is not object.__new__:
return original_new(cls, *args, **kwargs)
# Mirrors a similar check in object.__new__.
elif cls.__init__ is object.__init__ and (args or kwargs):
raise TypeError(f"{cls.__name__}() takes no arguments")
else:
return original_new(cls)
arg.__new__ = staticmethod(__new__)
if "__init_subclass__" in arg.__dict__:
# __init_subclass__ is directly present on the decorated class.
# Synthesize a wrapper that calls this method directly.
original_init_subclass = arg.__init_subclass__
# We need slightly different behavior if __init_subclass__
# is a bound method (likely if it was implemented in Python).
# Otherwise, it likely means it's a builtin such as
# object's implementation of __init_subclass__.
if isinstance(original_init_subclass, MethodType):
original_init_subclass = original_init_subclass.__func__
@functools.wraps(original_init_subclass)
def __init_subclass__(*args, **kwargs):
_wm.warn(msg, category=category, stacklevel=stacklevel + 1)
return original_init_subclass(*args, **kwargs)
else:
def __init_subclass__(cls, *args, **kwargs):
_wm.warn(msg, category=category, stacklevel=stacklevel + 1)
return super(arg, cls).__init_subclass__(*args, **kwargs)
arg.__init_subclass__ = classmethod(__init_subclass__)
arg.__deprecated__ = __new__.__deprecated__ = msg
__init_subclass__.__deprecated__ = msg
return arg
elif callable(arg):
import functools
import inspect
@functools.wraps(arg)
def wrapper(*args, **kwargs):
_wm.warn(msg, category=category, stacklevel=stacklevel + 1)
return arg(*args, **kwargs)
if inspect.iscoroutinefunction(arg):
wrapper = inspect.markcoroutinefunction(wrapper)
arg.__deprecated__ = wrapper.__deprecated__ = msg
return wrapper
else:
raise TypeError(
"@deprecated decorator with non-None category must be applied to "
f"a class or callable, not {arg!r}"
)
_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}"
def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
"""Warn that *name* is deprecated or should be removed.
RuntimeError is raised if *remove* specifies a major/minor tuple older than
the current Python version or the same version but past the alpha.
The *message* argument is formatted with *name* and *remove* as a Python
version tuple (e.g. (3, 11)).
"""
remove_formatted = f"{remove[0]}.{remove[1]}"
if (_version[:2] > remove) or (_version[:2] == remove and _version[3] != "alpha"):
msg = f"{name!r} was slated for removal after Python {remove_formatted} alpha"
raise RuntimeError(msg)
else:
msg = message.format(name=name, remove=remove_formatted)
_wm.warn(msg, DeprecationWarning, stacklevel=3)
# Private utility function called by _PyErr_WarnUnawaitedCoroutine
def _warn_unawaited_coroutine(coro):
msg_lines = [
f"coroutine '{coro.__qualname__}' was never awaited\n"
]
if coro.cr_origin is not None:
import linecache, traceback
def extract():
for filename, lineno, funcname in reversed(coro.cr_origin):
line = linecache.getline(filename, lineno)
yield (filename, lineno, funcname, line)
msg_lines.append("Coroutine created at (most recent call last)\n")
msg_lines += traceback.format_list(list(extract()))
msg = "".join(msg_lines).rstrip("\n")
# Passing source= here means that if the user happens to have tracemalloc
# enabled and tracking where the coroutine was created, the warning will
# contain that traceback. This does mean that if they have *both*
# coroutine origin tracking *and* tracemalloc enabled, they'll get two
# partially-redundant tracebacks. If we wanted to be clever we could
# probably detect this case and avoid it, but for now we don't bother.
_wm.warn(
msg, category=RuntimeWarning, stacklevel=2, source=coro
)
def _setup_defaults():
# Several warning categories are ignored by default in regular builds
if hasattr(sys, 'gettotalrefcount'):
return
_wm.filterwarnings("default", category=DeprecationWarning, module="__main__", append=1)
_wm.simplefilter("ignore", category=DeprecationWarning, append=1)
_wm.simplefilter("ignore", category=PendingDeprecationWarning, append=1)
_wm.simplefilter("ignore", category=ImportWarning, append=1)
_wm.simplefilter("ignore", category=ResourceWarning, append=1)

2006
Lib/_pycodecs.py vendored

File diff suppressed because it is too large Load Diff

254
Lib/_pydatetime.py vendored
View File

@@ -1,12 +1,10 @@
"""Concrete date/time and related types.
See http://www.iana.org/time-zones/repository/tz-link.html for
time zone and DST data sources.
"""
"""Pure Python implementation of the datetime module."""
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo",
"MINYEAR", "MAXYEAR", "UTC")
__name__ = "datetime"
import time as _time
import math as _math
@@ -18,10 +16,10 @@ def _cmp(x, y):
def _get_class_module(self):
module_name = self.__class__.__module__
if module_name == '_pydatetime':
return 'datetime'
if module_name == 'datetime':
return 'datetime.'
else:
return module_name
return ''
MINYEAR = 1
MAXYEAR = 9999
@@ -64,14 +62,14 @@ def _days_in_month(year, month):
def _days_before_month(year, month):
"year, month -> number of days in year preceding first day of month."
assert 1 <= month <= 12, 'month must be in 1..12'
assert 1 <= month <= 12, f"month must be in 1..12, not {month}"
return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
def _ymd2ord(year, month, day):
"year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
assert 1 <= month <= 12, 'month must be in 1..12'
assert 1 <= month <= 12, f"month must be in 1..12, not {month}"
dim = _days_in_month(year, month)
assert 1 <= day <= dim, ('day must be in 1..%d' % dim)
assert 1 <= day <= dim, f"day must be in 1..{dim}, not {day}"
return (_days_before_year(year) +
_days_before_month(year, month) +
day)
@@ -204,6 +202,17 @@ def _format_offset(off, sep=':'):
s += '.%06d' % ss.microseconds
return s
_normalize_century = None
def _need_normalize_century():
global _normalize_century
if _normalize_century is None:
try:
_normalize_century = (
_time.strftime("%Y", (99, 1, 1, 0, 0, 0, 0, 1, 0)) != "0099")
except ValueError:
_normalize_century = True
return _normalize_century
# Correctly substitute for %z and %Z escapes in strftime formats.
def _wrap_strftime(object, format, timetuple):
# Don't call utcoffset() or tzname() unless actually needed.
@@ -261,6 +270,20 @@ def _wrap_strftime(object, format, timetuple):
# strftime is going to have at this: escape %
Zreplace = s.replace('%', '%%')
newformat.append(Zreplace)
# Note that datetime(1000, 1, 1).strftime('%G') == '1000' so
# year 1000 for %G can go on the fast path.
elif ((ch in 'YG' or ch in 'FC') and
object.year < 1000 and _need_normalize_century()):
if ch == 'G':
year = int(_time.strftime("%G", timetuple))
else:
year = object.year
if ch == 'C':
push('{:02}'.format(year // 100))
else:
push('{:04}'.format(year))
if ch == 'F':
push('-{:02}-{:02}'.format(*timetuple[1:3]))
else:
push('%')
push(ch)
@@ -399,9 +422,11 @@ def _parse_hh_mm_ss_ff(tstr):
if pos < len_str:
if tstr[pos] not in '.,':
raise ValueError("Invalid microsecond component")
raise ValueError("Invalid microsecond separator")
else:
pos += 1
if not all(map(_is_ascii_digit, tstr[pos:])):
raise ValueError("Non-digit values in fraction")
len_remainder = len_str - pos
@@ -413,9 +438,6 @@ def _parse_hh_mm_ss_ff(tstr):
time_comps[3] = int(tstr[pos:(pos+to_parse)])
if to_parse < 6:
time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]
if (len_remainder > to_parse
and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))):
raise ValueError("Non-digit values in unparsed fraction")
return time_comps
@@ -431,6 +453,17 @@ def _parse_isoformat_time(tstr):
time_comps = _parse_hh_mm_ss_ff(timestr)
hour, minute, second, microsecond = time_comps
became_next_day = False
error_from_components = False
if (hour == 24):
if all(time_comp == 0 for time_comp in time_comps[1:]):
hour = 0
time_comps[0] = hour
became_next_day = True
else:
error_from_components = True
tzi = None
if tz_pos == len_str and tstr[-1] == 'Z':
tzi = timezone.utc
@@ -446,7 +479,7 @@ def _parse_isoformat_time(tstr):
# HH:MM:SS len: 8
# HH:MM:SS.f+ len: 10+
if len(tzstr) in (0, 1, 3):
if len(tzstr) in (0, 1, 3) or tstr[tz_pos-1] == 'Z':
raise ValueError("Malformed time zone string")
tz_comps = _parse_hh_mm_ss_ff(tzstr)
@@ -463,13 +496,13 @@ def _parse_isoformat_time(tstr):
time_comps.append(tzi)
return time_comps
return time_comps, became_next_day, error_from_components
# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar
def _isoweek_to_gregorian(year, week, day):
# Year is bounded this way because 9999-12-31 is (9999, 52, 5)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError(f"Year is out of range: {year}")
raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}")
if not 0 < week < 53:
out_of_range = True
@@ -502,7 +535,7 @@ def _isoweek_to_gregorian(year, week, day):
def _check_tzname(name):
if name is not None and not isinstance(name, str):
raise TypeError("tzinfo.tzname() must return None or string, "
"not '%s'" % type(name))
f"not {type(name).__name__!r}")
# name is the offset-producing method, "utcoffset" or "dst".
# offset is what it returned.
@@ -515,24 +548,24 @@ def _check_utc_offset(name, offset):
if offset is None:
return
if not isinstance(offset, timedelta):
raise TypeError("tzinfo.%s() must return None "
"or timedelta, not '%s'" % (name, type(offset)))
raise TypeError(f"tzinfo.{name}() must return None "
f"or timedelta, not {type(offset).__name__!r}")
if not -timedelta(1) < offset < timedelta(1):
raise ValueError("%s()=%s, must be strictly between "
"-timedelta(hours=24) and timedelta(hours=24)" %
(name, offset))
raise ValueError("offset must be a timedelta "
"strictly between -timedelta(hours=24) and "
f"timedelta(hours=24), not {offset!r}")
def _check_date_fields(year, month, day):
year = _index(year)
month = _index(month)
day = _index(day)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}")
if not 1 <= month <= 12:
raise ValueError('month must be in 1..12', month)
raise ValueError(f"month must be in 1..12, not {month}")
dim = _days_in_month(year, month)
if not 1 <= day <= dim:
raise ValueError('day must be in 1..%d' % dim, day)
raise ValueError(f"day {day} must be in range 1..{dim} for month {month} in year {year}")
return year, month, day
def _check_time_fields(hour, minute, second, microsecond, fold):
@@ -541,24 +574,23 @@ def _check_time_fields(hour, minute, second, microsecond, fold):
second = _index(second)
microsecond = _index(microsecond)
if not 0 <= hour <= 23:
raise ValueError('hour must be in 0..23', hour)
raise ValueError(f"hour must be in 0..23, not {hour}")
if not 0 <= minute <= 59:
raise ValueError('minute must be in 0..59', minute)
raise ValueError(f"minute must be in 0..59, not {minute}")
if not 0 <= second <= 59:
raise ValueError('second must be in 0..59', second)
raise ValueError(f"second must be in 0..59, not {second}")
if not 0 <= microsecond <= 999999:
raise ValueError('microsecond must be in 0..999999', microsecond)
raise ValueError(f"microsecond must be in 0..999999, not {microsecond}")
if fold not in (0, 1):
raise ValueError('fold must be either 0 or 1', fold)
raise ValueError(f"fold must be either 0 or 1, not {fold}")
return hour, minute, second, microsecond, fold
def _check_tzinfo_arg(tz):
if tz is not None and not isinstance(tz, tzinfo):
raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
def _cmperror(x, y):
raise TypeError("can't compare '%s' to '%s'" % (
type(x).__name__, type(y).__name__))
raise TypeError(
"tzinfo argument must be None or of a tzinfo subclass, "
f"not {type(tz).__name__!r}"
)
def _divide_and_round(a, b):
"""divide a by b and round result to the nearest integer
@@ -612,7 +644,19 @@ class timedelta:
# guide the C implementation; it's way more convoluted than speed-
# ignoring auto-overflow-to-long idiomatic Python could be.
# XXX Check that all inputs are ints or floats.
for name, value in (
("days", days),
("seconds", seconds),
("microseconds", microseconds),
("milliseconds", milliseconds),
("minutes", minutes),
("hours", hours),
("weeks", weeks)
):
if not isinstance(value, (int, float)):
raise TypeError(
f"unsupported type for timedelta {name} component: {type(value).__name__}"
)
# Final values, all integer.
# s and us fit in 32-bit signed ints; d isn't bounded.
@@ -713,9 +757,9 @@ class timedelta:
args.append("microseconds=%d" % self._microseconds)
if not args:
args.append('0')
return "%s.%s(%s)" % (_get_class_module(self),
self.__class__.__qualname__,
', '.join(args))
return "%s%s(%s)" % (_get_class_module(self),
self.__class__.__qualname__,
', '.join(args))
def __str__(self):
mm, ss = divmod(self._seconds, 60)
@@ -912,6 +956,7 @@ class date:
fromtimestamp()
today()
fromordinal()
strptime()
Operators:
@@ -970,6 +1015,8 @@ class date:
@classmethod
def fromtimestamp(cls, t):
"Construct a date from a POSIX timestamp (like time.time())."
if t is None:
raise TypeError("'NoneType' object cannot be interpreted as an integer")
y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
return cls(y, m, d)
@@ -992,8 +1039,12 @@ class date:
@classmethod
def fromisoformat(cls, date_string):
"""Construct a date from a string in ISO 8601 format."""
if not isinstance(date_string, str):
raise TypeError('fromisoformat: argument must be str')
raise TypeError('Argument must be a str')
if not date_string.isascii():
raise ValueError('Argument must be an ASCII str')
if len(date_string) not in (7, 8, 10):
raise ValueError(f'Invalid isoformat string: {date_string!r}')
@@ -1010,6 +1061,12 @@ class date:
This is the inverse of the date.isocalendar() function"""
return cls(*_isoweek_to_gregorian(year, week, day))
@classmethod
def strptime(cls, date_string, format):
"""Parse a date string according to the given format (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_date(cls, date_string, format)
# Conversions to string
def __repr__(self):
@@ -1019,11 +1076,11 @@ class date:
>>> repr(d)
'datetime.date(2010, 1, 1)'
"""
return "%s.%s(%d, %d, %d)" % (_get_class_module(self),
self.__class__.__qualname__,
self._year,
self._month,
self._day)
return "%s%s(%d, %d, %d)" % (_get_class_module(self),
self.__class__.__qualname__,
self._year,
self._month,
self._day)
# XXX These shouldn't depend on time.localtime(), because that
# clips the usable dates to [1970 .. 2038). At least ctime() is
# easily done without using strftime() -- that's better too because
@@ -1059,8 +1116,8 @@ class date:
This is 'YYYY-MM-DD'.
References:
- http://www.w3.org/TR/NOTE-datetime
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- https://www.w3.org/TR/NOTE-datetime
- https://www.cl.cam.ac.uk/~mgk25/iso-time.html
"""
return "%04d-%02d-%02d" % (self._year, self._month, self._day)
@@ -1108,35 +1165,38 @@ class date:
day = self._day
return type(self)(year, month, day)
__replace__ = replace
# Comparisons of date objects with other.
def __eq__(self, other):
if isinstance(other, date):
if isinstance(other, date) and not isinstance(other, datetime):
return self._cmp(other) == 0
return NotImplemented
def __le__(self, other):
if isinstance(other, date):
if isinstance(other, date) and not isinstance(other, datetime):
return self._cmp(other) <= 0
return NotImplemented
def __lt__(self, other):
if isinstance(other, date):
if isinstance(other, date) and not isinstance(other, datetime):
return self._cmp(other) < 0
return NotImplemented
def __ge__(self, other):
if isinstance(other, date):
if isinstance(other, date) and not isinstance(other, datetime):
return self._cmp(other) >= 0
return NotImplemented
def __gt__(self, other):
if isinstance(other, date):
if isinstance(other, date) and not isinstance(other, datetime):
return self._cmp(other) > 0
return NotImplemented
def _cmp(self, other):
assert isinstance(other, date)
assert not isinstance(other, datetime)
y, m, d = self._year, self._month, self._day
y2, m2, d2 = other._year, other._month, other._day
return _cmp((y, m, d), (y2, m2, d2))
@@ -1191,7 +1251,7 @@ class date:
The first week is 1; Monday is 1 ... Sunday is 7.
ISO calendar algorithm taken from
http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
https://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
(used with permission)
"""
year = self._year
@@ -1327,6 +1387,7 @@ class time:
Constructors:
__new__()
strptime()
Operators:
@@ -1385,6 +1446,12 @@ class time:
self._fold = fold
return self
@classmethod
def strptime(cls, date_string, format):
"""string, format -> new time parsed from a string (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_time(cls, date_string, format)
# Read-only field accessors
@property
def hour(self):
@@ -1513,7 +1580,7 @@ class time:
s = ", %d" % self._second
else:
s = ""
s= "%s.%s(%d, %d%s)" % (_get_class_module(self),
s = "%s%s(%d, %d%s)" % (_get_class_module(self),
self.__class__.__qualname__,
self._hour, self._minute, s)
if self._tzinfo is not None:
@@ -1555,7 +1622,7 @@ class time:
time_string = time_string.removeprefix('T')
try:
return cls(*_parse_isoformat_time(time_string))
return cls(*_parse_isoformat_time(time_string)[0])
except Exception:
raise ValueError(f'Invalid isoformat string: {time_string!r}')
@@ -1633,6 +1700,8 @@ class time:
fold = self._fold
return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold)
__replace__ = replace
# Pickle support.
def _getstate(self, protocol=3):
@@ -1680,7 +1749,7 @@ class datetime(date):
The year, month and day arguments are required. tzinfo may be None, or an
instance of a tzinfo subclass. The remaining arguments may be ints.
"""
__slots__ = date.__slots__ + time.__slots__
__slots__ = time.__slots__
def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
microsecond=0, tzinfo=None, *, fold=0):
@@ -1867,10 +1936,27 @@ class datetime(date):
if tstr:
try:
time_components = _parse_isoformat_time(tstr)
time_components, became_next_day, error_from_components = _parse_isoformat_time(tstr)
except ValueError:
raise ValueError(
f'Invalid isoformat string: {date_string!r}') from None
else:
if error_from_components:
raise ValueError("minute, second, and microsecond must be 0 when hour is 24")
if became_next_day:
year, month, day = date_components
# Only wrap day/month when it was previously valid
if month <= 12 and day <= (days_in_month := _days_in_month(year, month)):
# Calculate midnight of the next day
day += 1
if day > days_in_month:
day = 1
month += 1
if month > 12:
month = 1
year += 1
date_components = [year, month, day]
else:
time_components = [0, 0, 0, 0, None]
@@ -1979,6 +2065,8 @@ class datetime(date):
return type(self)(year, month, day, hour, minute, second,
microsecond, tzinfo, fold=fold)
__replace__ = replace
def _local_timezone(self):
if self.tzinfo is None:
ts = self._mktime()
@@ -2040,7 +2128,7 @@ class datetime(date):
By default, the fractional part is omitted if self.microsecond == 0.
If self.tzinfo is not None, the UTC offset is also attached, giving
giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.
a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.
Optional argument sep specifies the separator between date and
time, default 'T'.
@@ -2068,9 +2156,9 @@ class datetime(date):
del L[-1]
if L[-1] == 0:
del L[-1]
s = "%s.%s(%s)" % (_get_class_module(self),
self.__class__.__qualname__,
", ".join(map(str, L)))
s = "%s%s(%s)" % (_get_class_module(self),
self.__class__.__qualname__,
", ".join(map(str, L)))
if self._tzinfo is not None:
assert s[-1:] == ")"
s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
@@ -2087,7 +2175,7 @@ class datetime(date):
def strptime(cls, date_string, format):
'string, format -> new datetime parsed from a string (like time.strptime()).'
import _strptime
return _strptime._strptime_datetime(cls, date_string, format)
return _strptime._strptime_datetime_datetime(cls, date_string, format)
def utcoffset(self):
"""Return the timezone offset as timedelta positive east of UTC (negative west of
@@ -2131,42 +2219,32 @@ class datetime(date):
def __eq__(self, other):
if isinstance(other, datetime):
return self._cmp(other, allow_mixed=True) == 0
elif not isinstance(other, date):
return NotImplemented
else:
return False
return NotImplemented
def __le__(self, other):
if isinstance(other, datetime):
return self._cmp(other) <= 0
elif not isinstance(other, date):
return NotImplemented
else:
_cmperror(self, other)
return NotImplemented
def __lt__(self, other):
if isinstance(other, datetime):
return self._cmp(other) < 0
elif not isinstance(other, date):
return NotImplemented
else:
_cmperror(self, other)
return NotImplemented
def __ge__(self, other):
if isinstance(other, datetime):
return self._cmp(other) >= 0
elif not isinstance(other, date):
return NotImplemented
else:
_cmperror(self, other)
return NotImplemented
def __gt__(self, other):
if isinstance(other, datetime):
return self._cmp(other) > 0
elif not isinstance(other, date):
return NotImplemented
else:
_cmperror(self, other)
return NotImplemented
def _cmp(self, other, allow_mixed=False):
assert isinstance(other, datetime)
@@ -2311,7 +2389,6 @@ datetime.resolution = timedelta(microseconds=1)
def _isoweek1monday(year):
# Helper to calculate the day number of the Monday starting week 1
# XXX This could be done more efficiently
THURSDAY = 3
firstday = _ymd2ord(year, 1, 1)
firstweekday = (firstday + 6) % 7 # See weekday() above
@@ -2338,9 +2415,12 @@ class timezone(tzinfo):
if not cls._minoffset <= offset <= cls._maxoffset:
raise ValueError("offset must be a timedelta "
"strictly between -timedelta(hours=24) and "
"timedelta(hours=24).")
f"timedelta(hours=24), not {offset!r}")
return cls._create(offset, name)
def __init_subclass__(cls):
raise TypeError("type 'datetime.timezone' is not an acceptable base type")
@classmethod
def _create(cls, offset, name=None):
self = tzinfo.__new__(cls)
@@ -2375,12 +2455,12 @@ class timezone(tzinfo):
if self is self.utc:
return 'datetime.timezone.utc'
if self._name is None:
return "%s.%s(%r)" % (_get_class_module(self),
self.__class__.__qualname__,
self._offset)
return "%s.%s(%r, %r)" % (_get_class_module(self),
self.__class__.__qualname__,
self._offset, self._name)
return "%s%s(%r)" % (_get_class_module(self),
self.__class__.__qualname__,
self._offset)
return "%s%s(%r, %r)" % (_get_class_module(self),
self.__class__.__qualname__,
self._offset, self._name)
def __str__(self):
return self.tzname(None)

347
Lib/_pydecimal.py vendored
View File

@@ -13,104 +13,7 @@
# bug) and will be backported. At this point the spec is stabilizing
# and the updates are becoming fewer, smaller, and less significant.
"""
This is an implementation of decimal floating point arithmetic based on
the General Decimal Arithmetic Specification:
http://speleotrove.com/decimal/decarith.html
and IEEE standard 854-1987:
http://en.wikipedia.org/wiki/IEEE_854-1987
Decimal floating point has finite precision with arbitrarily large bounds.
The purpose of this module is to support arithmetic using familiar
"schoolhouse" rules and to avoid some of the tricky representation
issues associated with binary floating point. The package is especially
useful for financial applications or for contexts where users have
expectations that are at odds with binary floating point (for instance,
in binary floating point, 1.00 % 0.1 gives 0.09999999999999995 instead
of 0.0; Decimal('1.00') % Decimal('0.1') returns the expected
Decimal('0.00')).
Here are some examples of using the decimal module:
>>> from decimal import *
>>> setcontext(ExtendedContext)
>>> Decimal(0)
Decimal('0')
>>> Decimal('1')
Decimal('1')
>>> Decimal('-.0123')
Decimal('-0.0123')
>>> Decimal(123456)
Decimal('123456')
>>> Decimal('123.45e12345678')
Decimal('1.2345E+12345680')
>>> Decimal('1.33') + Decimal('1.27')
Decimal('2.60')
>>> Decimal('12.34') + Decimal('3.87') - Decimal('18.41')
Decimal('-2.20')
>>> dig = Decimal(1)
>>> print(dig / Decimal(3))
0.333333333
>>> getcontext().prec = 18
>>> print(dig / Decimal(3))
0.333333333333333333
>>> print(dig.sqrt())
1
>>> print(Decimal(3).sqrt())
1.73205080756887729
>>> print(Decimal(3) ** 123)
4.85192780976896427E+58
>>> inf = Decimal(1) / Decimal(0)
>>> print(inf)
Infinity
>>> neginf = Decimal(-1) / Decimal(0)
>>> print(neginf)
-Infinity
>>> print(neginf + inf)
NaN
>>> print(neginf * inf)
-Infinity
>>> print(dig / 0)
Infinity
>>> getcontext().traps[DivisionByZero] = 1
>>> print(dig / 0)
Traceback (most recent call last):
...
...
...
decimal.DivisionByZero: x / 0
>>> c = Context()
>>> c.traps[InvalidOperation] = 0
>>> print(c.flags[InvalidOperation])
0
>>> c.divide(Decimal(0), Decimal(0))
Decimal('NaN')
>>> c.traps[InvalidOperation] = 1
>>> print(c.flags[InvalidOperation])
1
>>> c.flags[InvalidOperation] = 0
>>> print(c.flags[InvalidOperation])
0
>>> print(c.divide(Decimal(0), Decimal(0)))
Traceback (most recent call last):
...
...
...
decimal.InvalidOperation: 0 / 0
>>> print(c.flags[InvalidOperation])
1
>>> c.flags[InvalidOperation] = 0
>>> c.traps[InvalidOperation] = 0
>>> print(c.divide(Decimal(0), Decimal(0)))
NaN
>>> print(c.flags[InvalidOperation])
1
>>>
"""
"""Python decimal arithmetic module"""
__all__ = [
# Two major classes
@@ -135,13 +38,16 @@ __all__ = [
'ROUND_FLOOR', 'ROUND_UP', 'ROUND_HALF_DOWN', 'ROUND_05UP',
# Functions for manipulating contexts
'setcontext', 'getcontext', 'localcontext',
'setcontext', 'getcontext', 'localcontext', 'IEEEContext',
# Limits for the C version for compatibility
'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY',
'MAX_PREC', 'MAX_EMAX', 'MIN_EMIN', 'MIN_ETINY', 'IEEE_CONTEXT_MAX_BITS',
# C version: compile time choice that enables the thread local context
'HAVE_THREADS'
# C version: compile time choice that enables the thread local context (deprecated, now always true)
'HAVE_THREADS',
# C version: compile time choice that enables the coroutine local context
'HAVE_CONTEXTVAR'
]
__xname__ = __name__ # sys.modules lookup (--without-threads)
@@ -156,7 +62,7 @@ import sys
try:
from collections import namedtuple as _namedtuple
DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent')
DecimalTuple = _namedtuple('DecimalTuple', 'sign digits exponent', module='decimal')
except ImportError:
DecimalTuple = lambda *args: args
@@ -172,14 +78,17 @@ ROUND_05UP = 'ROUND_05UP'
# Compatibility with the C version
HAVE_THREADS = True
HAVE_CONTEXTVAR = True
if sys.maxsize == 2**63-1:
MAX_PREC = 999999999999999999
MAX_EMAX = 999999999999999999
MIN_EMIN = -999999999999999999
IEEE_CONTEXT_MAX_BITS = 512
else:
MAX_PREC = 425000000
MAX_EMAX = 425000000
MIN_EMIN = -425000000
IEEE_CONTEXT_MAX_BITS = 256
MIN_ETINY = MIN_EMIN - (MAX_PREC-1)
@@ -190,7 +99,7 @@ class DecimalException(ArithmeticError):
Used exceptions derive from this.
If an exception derives from another exception besides this (such as
Underflow (Inexact, Rounded, Subnormal) that indicates that it is only
Underflow (Inexact, Rounded, Subnormal)) that indicates that it is only
called if the others are present. This isn't actually used for
anything, though.
@@ -238,7 +147,7 @@ class InvalidOperation(DecimalException):
x ** (+-)INF
An operand is invalid
The result of the operation after these is a quiet positive NaN,
The result of the operation after this is a quiet positive NaN,
except when the cause is a signaling NaN, in which case the result is
also a quiet NaN, but with the original sign, and an optional
diagnostic information.
@@ -431,82 +340,40 @@ _rounding_modes = (ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_CEILING,
##### Context Functions ##################################################
# The getcontext() and setcontext() function manage access to a thread-local
# current context. Py2.4 offers direct support for thread locals. If that
# is not available, use threading.current_thread() which is slower but will
# work for older Pythons. If threads are not part of the build, create a
# mock threading object with threading.local() returning the module namespace.
# current context.
try:
import threading
except ImportError:
# Python was compiled without threads; create a mock object instead
class MockThreading(object):
def local(self, sys=sys):
return sys.modules[__xname__]
threading = MockThreading()
del MockThreading
import contextvars
try:
threading.local
_current_context_var = contextvars.ContextVar('decimal_context')
except AttributeError:
_context_attributes = frozenset(
['prec', 'Emin', 'Emax', 'capitals', 'clamp', 'rounding', 'flags', 'traps']
)
# To fix reloading, force it to create a new context
# Old contexts have different exceptions in their dicts, making problems.
if hasattr(threading.current_thread(), '__decimal_context__'):
del threading.current_thread().__decimal_context__
def getcontext():
"""Returns this thread's context.
def setcontext(context):
"""Set this thread's context to context."""
if context in (DefaultContext, BasicContext, ExtendedContext):
context = context.copy()
context.clear_flags()
threading.current_thread().__decimal_context__ = context
If this thread does not yet have a context, returns
a new context and sets this thread's context.
New contexts are copies of DefaultContext.
"""
try:
return _current_context_var.get()
except LookupError:
context = Context()
_current_context_var.set(context)
return context
def getcontext():
"""Returns this thread's context.
def setcontext(context):
"""Set this thread's context to context."""
if context in (DefaultContext, BasicContext, ExtendedContext):
context = context.copy()
context.clear_flags()
_current_context_var.set(context)
If this thread does not yet have a context, returns
a new context and sets this thread's context.
New contexts are copies of DefaultContext.
"""
try:
return threading.current_thread().__decimal_context__
except AttributeError:
context = Context()
threading.current_thread().__decimal_context__ = context
return context
del contextvars # Don't contaminate the namespace
else:
local = threading.local()
if hasattr(local, '__decimal_context__'):
del local.__decimal_context__
def getcontext(_local=local):
"""Returns this thread's context.
If this thread does not yet have a context, returns
a new context and sets this thread's context.
New contexts are copies of DefaultContext.
"""
try:
return _local.__decimal_context__
except AttributeError:
context = Context()
_local.__decimal_context__ = context
return context
def setcontext(context, _local=local):
"""Set this thread's context to context."""
if context in (DefaultContext, BasicContext, ExtendedContext):
context = context.copy()
context.clear_flags()
_local.__decimal_context__ = context
del threading, local # Don't contaminate the namespace
def localcontext(ctx=None):
def localcontext(ctx=None, **kwargs):
"""Return a context manager for a copy of the supplied context
Uses a copy of the current context if no context is specified
@@ -542,8 +409,35 @@ def localcontext(ctx=None):
>>> print(getcontext().prec)
28
"""
if ctx is None: ctx = getcontext()
return _ContextManager(ctx)
if ctx is None:
ctx = getcontext()
ctx_manager = _ContextManager(ctx)
for key, value in kwargs.items():
if key not in _context_attributes:
raise TypeError(f"'{key}' is an invalid keyword argument for this function")
setattr(ctx_manager.new_context, key, value)
return ctx_manager
def IEEEContext(bits, /):
"""
Return a context object initialized to the proper values for one of the
IEEE interchange formats. The argument must be a multiple of 32 and less
than IEEE_CONTEXT_MAX_BITS.
"""
if bits <= 0 or bits > IEEE_CONTEXT_MAX_BITS or bits % 32:
raise ValueError("argument must be a multiple of 32, "
f"with a maximum of {IEEE_CONTEXT_MAX_BITS}")
ctx = Context()
ctx.prec = 9 * (bits//32) - 2
ctx.Emax = 3 * (1 << (bits//16 + 3))
ctx.Emin = 1 - ctx.Emax
ctx.rounding = ROUND_HALF_EVEN
ctx.clamp = 1
ctx.traps = dict.fromkeys(_signals, False)
return ctx
##### Decimal class #######################################################
@@ -553,7 +447,7 @@ def localcontext(ctx=None):
# numbers.py for more detail.
class Decimal(object):
"""Floating point class for decimal arithmetic."""
"""Floating-point class for decimal arithmetic."""
__slots__ = ('_exp','_int','_sign', '_is_special')
# Generally, the value of the Decimal instance is given by
@@ -711,6 +605,21 @@ class Decimal(object):
raise TypeError("Cannot convert %r to Decimal" % value)
@classmethod
def from_number(cls, number):
"""Converts a real number to a decimal number, exactly.
>>> Decimal.from_number(314) # int
Decimal('314')
>>> Decimal.from_number(0.1) # float
Decimal('0.1000000000000000055511151231257827021181583404541015625')
>>> Decimal.from_number(Decimal('3.14')) # another decimal instance
Decimal('3.14')
"""
if isinstance(number, (int, Decimal, float)):
return cls(number)
raise TypeError("Cannot convert %r to Decimal" % number)
@classmethod
def from_float(cls, f):
"""Converts a float to a decimal number, exactly.
@@ -993,7 +902,7 @@ class Decimal(object):
if self.is_snan():
raise TypeError('Cannot hash a signaling NaN value.')
elif self.is_nan():
return _PyHASH_NAN
return object.__hash__(self)
else:
if self._sign:
return -_PyHASH_INF
@@ -1674,13 +1583,13 @@ class Decimal(object):
__trunc__ = __int__
@property
def real(self):
return self
real = property(real)
@property
def imag(self):
return Decimal(0)
imag = property(imag)
def conjugate(self):
return self
@@ -2260,10 +2169,16 @@ class Decimal(object):
else:
return None
if xc >= 10**p:
# An exact power of 10 is representable, but can convert to a
# string of any length. But an exact power of 10 shouldn't be
# possible at this point.
assert xc > 1, self
assert xc % 10 != 0, self
strxc = str(xc)
if len(strxc) > p:
return None
xe = -e-xe
return _dec_from_triple(0, str(xc), xe)
return _dec_from_triple(0, strxc, xe)
# now y is positive; find m and n such that y = m/n
if ye >= 0:
@@ -2272,7 +2187,7 @@ class Decimal(object):
if xe != 0 and len(str(abs(yc*xe))) <= -ye:
return None
xc_bits = _nbits(xc)
if xc != 1 and len(str(abs(yc)*xc_bits)) <= -ye:
if len(str(abs(yc)*xc_bits)) <= -ye:
return None
m, n = yc, 10**(-ye)
while m % 2 == n % 2 == 0:
@@ -2285,7 +2200,7 @@ class Decimal(object):
# compute nth root of xc*10**xe
if n > 1:
# if 1 < xc < 2**n then xc isn't an nth power
if xc != 1 and xc_bits <= n:
if xc_bits <= n:
return None
xe, rem = divmod(xe, n)
@@ -2313,13 +2228,18 @@ class Decimal(object):
return None
xc = xc**m
xe *= m
if xc > 10**p:
# An exact power of 10 is representable, but can convert to a string
# of any length. But an exact power of 10 shouldn't be possible at
# this point.
assert xc > 1, self
assert xc % 10 != 0, self
str_xc = str(xc)
if len(str_xc) > p:
return None
# by this point the result *is* exactly representable
# adjust the exponent to get as close as possible to the ideal
# exponent, if necessary
str_xc = str(xc)
if other._isinteger() and other._sign == 0:
ideal_exponent = self._exp*int(other)
zeros = min(xe-ideal_exponent, p-len(str_xc))
@@ -2543,12 +2463,12 @@ class Decimal(object):
return ans
def __rpow__(self, other, context=None):
def __rpow__(self, other, modulo=None, context=None):
"""Swaps self/other and returns __pow__."""
other = _convert_other(other)
if other is NotImplemented:
return other
return other.__pow__(self, context=context)
return other.__pow__(self, modulo, context=context)
def normalize(self, context=None):
"""Normalize- strip trailing 0s, change anything equal to 0 to 0e0"""
@@ -3420,7 +3340,10 @@ class Decimal(object):
return opa, opb
def logical_and(self, other, context=None):
"""Applies an 'and' operation between self and other's digits."""
"""Applies an 'and' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()
@@ -3437,14 +3360,20 @@ class Decimal(object):
return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_invert(self, context=None):
"""Invert all its digits."""
"""Invert all its digits.
The self must be logical number.
"""
if context is None:
context = getcontext()
return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0),
context)
def logical_or(self, other, context=None):
"""Applies an 'or' operation between self and other's digits."""
"""Applies an 'or' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()
@@ -3461,7 +3390,10 @@ class Decimal(object):
return _dec_from_triple(0, result.lstrip('0') or '0', 0)
def logical_xor(self, other, context=None):
"""Applies an 'xor' operation between self and other's digits."""
"""Applies an 'xor' operation between self and other's digits.
Both self and other must be logical numbers.
"""
if context is None:
context = getcontext()
@@ -3837,6 +3769,10 @@ class Decimal(object):
# represented in fixed point; rescale them to 0e0.
if not self and self._exp > 0 and spec['type'] in 'fF%':
self = self._rescale(0, rounding)
if not self and spec['no_neg_0'] and self._sign:
adjusted_sign = 0
else:
adjusted_sign = self._sign
# figure out placement of the decimal point
leftdigits = self._exp + len(self._int)
@@ -3867,7 +3803,7 @@ class Decimal(object):
# done with the decimal-specific stuff; hand over the rest
# of the formatting to the _format_number function
return _format_number(self._sign, intpart, fracpart, exp, spec)
return _format_number(adjusted_sign, intpart, fracpart, exp, spec)
def _dec_from_triple(sign, coefficient, exponent, special=False):
"""Create a decimal instance directly, without any validation,
@@ -5677,8 +5613,6 @@ class _WorkRep(object):
def __repr__(self):
return "(%r, %r, %r)" % (self.sign, self.int, self.exp)
__str__ = __repr__
def _normalize(op1, op2, prec = 0):
@@ -6174,7 +6108,7 @@ _parser = re.compile(r""" # A numeric string consists of:
(?P<diag>\d*) # with (possibly empty) diagnostic info.
)
# \s*
\Z
\z
""", re.VERBOSE | re.IGNORECASE).match
_all_zeros = re.compile('0*$').match
@@ -6187,7 +6121,7 @@ _exact_half = re.compile('50*$').match
#
# A format specifier for Decimal looks like:
#
# [[fill]align][sign][#][0][minimumwidth][,][.precision][type]
# [[fill]align][sign][z][#][0][minimumwidth][,][.precision][type]
_parse_format_specifier_regex = re.compile(r"""\A
(?:
@@ -6195,13 +6129,18 @@ _parse_format_specifier_regex = re.compile(r"""\A
(?P<align>[<>=^])
)?
(?P<sign>[-+ ])?
(?P<no_neg_0>z)?
(?P<alt>\#)?
(?P<zeropad>0)?
(?P<minimumwidth>(?!0)\d+)?
(?P<thousands_sep>,)?
(?:\.(?P<precision>0|(?!0)\d+))?
(?P<minimumwidth>\d+)?
(?P<thousands_sep>[,_])?
(?:\.
(?=[\d,_]) # lookahead for digit or separator
(?P<precision>\d+)?
(?P<frac_separators>[,_])?
)?
(?P<type>[eEfFgGn%])?
\Z
\z
""", re.VERBOSE|re.DOTALL)
del re
@@ -6292,6 +6231,9 @@ def _parse_format_specifier(format_spec, _localeconv=None):
format_dict['grouping'] = [3, 0]
format_dict['decimal_point'] = '.'
if format_dict['frac_separators'] is None:
format_dict['frac_separators'] = ''
return format_dict
def _format_align(sign, body, spec):
@@ -6411,6 +6353,11 @@ def _format_number(is_negative, intpart, fracpart, exp, spec):
sign = _format_sign(is_negative, spec)
frac_sep = spec['frac_separators']
if fracpart and frac_sep:
fracpart = frac_sep.join(fracpart[pos:pos + 3]
for pos in range(0, len(fracpart), 3))
if fracpart or spec['alt']:
fracpart = spec['decimal_point'] + fracpart

241
Lib/_pyio.py vendored
View File

@@ -16,15 +16,16 @@ else:
_setmode = None
import io
from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END)
from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END, Reader, Writer) # noqa: F401
valid_seek_flags = {0, 1, 2} # Hardwired values
if hasattr(os, 'SEEK_HOLE') :
valid_seek_flags.add(os.SEEK_HOLE)
valid_seek_flags.add(os.SEEK_DATA)
# open() uses st_blksize whenever we can
DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes
# open() uses max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE)
# when the device block size is available.
DEFAULT_BUFFER_SIZE = 128 * 1024 # bytes
# NOTE: Base classes defined here are registered with the "official" ABCs
# defined in io.py. We don't use real inheritance though, because we don't want
@@ -33,11 +34,8 @@ DEFAULT_BUFFER_SIZE = 8 * 1024 # bytes
# Rebind for compatibility
BlockingIOError = BlockingIOError
# Does io.IOBase finalizer log the exception if the close() method fails?
# The exception is ignored silently by default in release build.
_IOBASE_EMITS_UNRAISABLE = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode)
# Does open() check its 'errors' argument?
_CHECK_ERRORS = _IOBASE_EMITS_UNRAISABLE
_CHECK_ERRORS = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode)
def text_encoding(encoding, stacklevel=2):
@@ -126,10 +124,10 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
the size of a fixed-size chunk buffer. When no buffering argument is
given, the default buffering policy works as follows:
* Binary files are buffered in fixed-size chunks; the size of the buffer
is chosen using a heuristic trying to determine the underlying device's
"block size" and falling back on `io.DEFAULT_BUFFER_SIZE`.
On many systems, the buffer will typically be 4096 or 8192 bytes long.
* Binary files are buffered in fixed-size chunks; the size of the buffer
is max(min(blocksize, 8 MiB), DEFAULT_BUFFER_SIZE)
when the device block size is available.
On most systems, the buffer will typically be 128 kilobytes long.
* "Interactive" text files (files for which isatty() returns True)
use line buffering. Other text files use the policy described above
@@ -241,18 +239,11 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
result = raw
try:
line_buffering = False
if buffering == 1 or buffering < 0 and raw.isatty():
if buffering == 1 or buffering < 0 and raw._isatty_open_only():
buffering = -1
line_buffering = True
if buffering < 0:
buffering = DEFAULT_BUFFER_SIZE
try:
bs = os.fstat(raw.fileno()).st_blksize
except (OSError, AttributeError):
pass
else:
if bs > 1:
buffering = bs
buffering = max(min(raw._blksize, 8192 * 1024), DEFAULT_BUFFER_SIZE)
if buffering < 0:
raise ValueError("invalid buffering size")
if buffering == 0:
@@ -416,18 +407,12 @@ class IOBase(metaclass=abc.ABCMeta):
if closed:
return
if _IOBASE_EMITS_UNRAISABLE:
self.close()
else:
# The try/except block is in case this is called at program
# exit time, when it's possible that globals have already been
# deleted, and then the close() call might fail. Since
# there's nothing we can do about such failures and they annoy
# the end users, we suppress the traceback.
try:
self.close()
except:
pass
if dealloc_warn := getattr(self, "_dealloc_warn", None):
dealloc_warn(self)
# If close() fails, the caller logs the exception with
# sys.unraisablehook. close() must be called at the end at __del__().
self.close()
### Inquiries ###
@@ -632,16 +617,15 @@ class RawIOBase(IOBase):
n = self.readinto(b)
if n is None:
return None
if n < 0 or n > len(b):
raise ValueError(f"readinto returned {n} outside buffer size {len(b)}")
del b[n:]
return bytes(b)
def readall(self):
"""Read until EOF, using multiple read() call."""
res = bytearray()
while True:
data = self.read(DEFAULT_BUFFER_SIZE)
if not data:
break
while data := self.read(DEFAULT_BUFFER_SIZE):
res += data
if res:
return bytes(res)
@@ -666,8 +650,6 @@ class RawIOBase(IOBase):
self._unsupported("write")
io.RawIOBase.register(RawIOBase)
from _io import FileIO
RawIOBase.register(FileIO)
class BufferedIOBase(IOBase):
@@ -874,6 +856,10 @@ class _BufferedIOMixin(BufferedIOBase):
else:
return "<{}.{} name={!r}>".format(modname, clsname, name)
def _dealloc_warn(self, source):
if dealloc_warn := getattr(self.raw, "_dealloc_warn", None):
dealloc_warn(source)
### Lower-level APIs ###
def fileno(self):
@@ -949,22 +935,22 @@ class BytesIO(BufferedIOBase):
return self.read(size)
def write(self, b):
if self.closed:
raise ValueError("write to closed file")
if isinstance(b, str):
raise TypeError("can't write str to binary stream")
with memoryview(b) as view:
if self.closed:
raise ValueError("write to closed file")
n = view.nbytes # Size of any bytes-like object
if n == 0:
return 0
pos = self._pos
if pos > len(self._buffer):
# Inserts null bytes between the current end of the file
# and the new write position.
padding = b'\x00' * (pos - len(self._buffer))
self._buffer += padding
self._buffer[pos:pos + n] = b
self._pos += n
if n == 0:
return 0
pos = self._pos
if pos > len(self._buffer):
# Pad buffer to pos with null bytes.
self._buffer.resize(pos)
self._buffer[pos:pos + n] = view
self._pos += n
return n
def seek(self, pos, whence=0):
@@ -1478,6 +1464,17 @@ class BufferedRandom(BufferedWriter, BufferedReader):
return BufferedWriter.write(self, b)
def _new_buffersize(bytes_read):
# Parallels _io/fileio.c new_buffersize
if bytes_read > 65536:
addend = bytes_read >> 3
else:
addend = 256 + bytes_read
if addend < DEFAULT_BUFFER_SIZE:
addend = DEFAULT_BUFFER_SIZE
return bytes_read + addend
class FileIO(RawIOBase):
_fd = -1
_created = False
@@ -1502,6 +1499,7 @@ class FileIO(RawIOBase):
"""
if self._fd >= 0:
# Have to close the existing file first.
self._stat_atopen = None
try:
if self._closefd:
os.close(self._fd)
@@ -1511,6 +1509,11 @@ class FileIO(RawIOBase):
if isinstance(file, float):
raise TypeError('integer argument expected, got float')
if isinstance(file, int):
if isinstance(file, bool):
import warnings
warnings.warn("bool is used as a file descriptor",
RuntimeWarning, stacklevel=2)
file = int(file)
fd = file
if fd < 0:
raise ValueError('negative file descriptor')
@@ -1569,24 +1572,22 @@ class FileIO(RawIOBase):
if not isinstance(fd, int):
raise TypeError('expected integer from opener')
if fd < 0:
raise OSError('Negative file descriptor')
# bpo-27066: Raise a ValueError for bad value.
raise ValueError(f'opener returned {fd}')
owned_fd = fd
if not noinherit_flag:
os.set_inheritable(fd, False)
self._closefd = closefd
fdfstat = os.fstat(fd)
self._stat_atopen = os.fstat(fd)
try:
if stat.S_ISDIR(fdfstat.st_mode):
if stat.S_ISDIR(self._stat_atopen.st_mode):
raise IsADirectoryError(errno.EISDIR,
os.strerror(errno.EISDIR), file)
except AttributeError:
# Ignore the AttributeError if stat.S_ISDIR or errno.EISDIR
# don't exist.
pass
self._blksize = getattr(fdfstat, 'st_blksize', 0)
if self._blksize <= 1:
self._blksize = DEFAULT_BUFFER_SIZE
if _setmode:
# don't translate newlines (\r\n <=> \n)
@@ -1603,17 +1604,17 @@ class FileIO(RawIOBase):
if e.errno != errno.ESPIPE:
raise
except:
self._stat_atopen = None
if owned_fd is not None:
os.close(owned_fd)
raise
self._fd = fd
def __del__(self):
def _dealloc_warn(self, source):
if self._fd >= 0 and self._closefd and not self.closed:
import warnings
warnings.warn('unclosed file %r' % (self,), ResourceWarning,
warnings.warn(f'unclosed file {source!r}', ResourceWarning,
stacklevel=2, source=self)
self.close()
def __getstate__(self):
raise TypeError(f"cannot pickle {self.__class__.__name__!r} object")
@@ -1632,6 +1633,17 @@ class FileIO(RawIOBase):
return ('<%s name=%r mode=%r closefd=%r>' %
(class_name, name, self.mode, self._closefd))
@property
def _blksize(self):
if self._stat_atopen is None:
return DEFAULT_BUFFER_SIZE
blksize = getattr(self._stat_atopen, "st_blksize", 0)
# WASI sets blsize to 0
if not blksize:
return DEFAULT_BUFFER_SIZE
return blksize
def _checkReadable(self):
if not self._readable:
raise UnsupportedOperation('File not open for reading')
@@ -1643,7 +1655,13 @@ class FileIO(RawIOBase):
def read(self, size=None):
"""Read at most size bytes, returned as bytes.
Only makes one system call, so less data may be returned than requested
If size is less than 0, read all bytes in the file making
multiple read calls. See ``FileIO.readall``.
Attempts to make only one system call, retrying only per
PEP 475 (EINTR). This means less data may be returned than
requested.
In non-blocking mode, returns None if no data is available.
Return an empty bytes object at EOF.
"""
@@ -1659,45 +1677,57 @@ class FileIO(RawIOBase):
def readall(self):
"""Read all data from the file, returned as bytes.
In non-blocking mode, returns as much as is immediately available,
or None if no data is available. Return an empty bytes object at EOF.
Reads until either there is an error or read() returns size 0
(indicates EOF). If the file is already at EOF, returns an
empty bytes object.
In non-blocking mode, returns as much data as could be read
before EAGAIN. If no data is available (EAGAIN is returned
before bytes are read) returns None.
"""
self._checkClosed()
self._checkReadable()
bufsize = DEFAULT_BUFFER_SIZE
if self._stat_atopen is None or self._stat_atopen.st_size <= 0:
bufsize = DEFAULT_BUFFER_SIZE
else:
# In order to detect end of file, need a read() of at least 1
# byte which returns size 0. Oversize the buffer by 1 byte so the
# I/O can be completed with two read() calls (one for all data, one
# for EOF) without needing to resize the buffer.
bufsize = self._stat_atopen.st_size + 1
if self._stat_atopen.st_size > 65536:
try:
pos = os.lseek(self._fd, 0, SEEK_CUR)
if self._stat_atopen.st_size >= pos:
bufsize = self._stat_atopen.st_size - pos + 1
except OSError:
pass
result = bytearray(bufsize)
bytes_read = 0
try:
pos = os.lseek(self._fd, 0, SEEK_CUR)
end = os.fstat(self._fd).st_size
if end >= pos:
bufsize = end - pos + 1
except OSError:
pass
result = bytearray()
while True:
if len(result) >= bufsize:
bufsize = len(result)
bufsize += max(bufsize, DEFAULT_BUFFER_SIZE)
n = bufsize - len(result)
try:
chunk = os.read(self._fd, n)
except BlockingIOError:
if result:
break
while n := os.readinto(self._fd, memoryview(result)[bytes_read:]):
bytes_read += n
if bytes_read >= len(result):
result.resize(_new_buffersize(bytes_read))
except BlockingIOError:
if not bytes_read:
return None
if not chunk: # reached the end of the file
break
result += chunk
assert len(result) - bytes_read >= 1, \
"os.readinto buffer size 0 will result in erroneous EOF / returns 0"
result.resize(bytes_read)
return bytes(result)
def readinto(self, b):
def readinto(self, buffer):
"""Same as RawIOBase.readinto()."""
m = memoryview(b).cast('B')
data = self.read(len(m))
n = len(data)
m[:n] = data
return n
self._checkClosed()
self._checkReadable()
try:
return os.readinto(self._fd, buffer)
except BlockingIOError:
return None
def write(self, b):
"""Write bytes b to file, return number written.
@@ -1747,6 +1777,7 @@ class FileIO(RawIOBase):
if size is None:
size = self.tell()
os.ftruncate(self._fd, size)
self._stat_atopen = None
return size
def close(self):
@@ -1756,8 +1787,9 @@ class FileIO(RawIOBase):
called more than once without error.
"""
if not self.closed:
self._stat_atopen = None
try:
if self._closefd:
if self._closefd and self._fd >= 0:
os.close(self._fd)
finally:
super().close()
@@ -1794,6 +1826,21 @@ class FileIO(RawIOBase):
self._checkClosed()
return os.isatty(self._fd)
def _isatty_open_only(self):
"""Checks whether the file is a TTY using an open-only optimization.
TTYs are always character devices. If the interpreter knows a file is
not a character device when it would call ``isatty``, can skip that
call. Inside ``open()`` there is a fresh stat result that contains that
information. Use the stat result to skip a system call. Outside of that
context TOCTOU issues (the fd could be arbitrarily modified by
surrounding code).
"""
if (self._stat_atopen is not None
and not stat.S_ISCHR(self._stat_atopen.st_mode)):
return False
return os.isatty(self._fd)
@property
def closefd(self):
"""True if the file descriptor will be closed by close()."""
@@ -2018,8 +2065,7 @@ class TextIOWrapper(TextIOBase):
raise ValueError("invalid encoding: %r" % encoding)
if not codecs.lookup(encoding)._is_text_encoding:
msg = ("%r is not a text encoding; "
"use codecs.open() to handle arbitrary codecs")
msg = "%r is not a text encoding"
raise LookupError(msg % encoding)
if errors is None:
@@ -2527,9 +2573,12 @@ class TextIOWrapper(TextIOBase):
size = size_index()
decoder = self._decoder or self._get_decoder()
if size < 0:
chunk = self.buffer.read()
if chunk is None:
raise BlockingIOError("Read returned None.")
# Read everything.
result = (self._get_decoded_chars() +
decoder.decode(self.buffer.read(), final=True))
decoder.decode(chunk, final=True))
if self._snapshot is not None:
self._set_decoded_chars('')
self._snapshot = None
@@ -2649,6 +2698,10 @@ class TextIOWrapper(TextIOBase):
def newlines(self):
return self._decoder.newlines if self._decoder else None
def _dealloc_warn(self, source):
if dealloc_warn := getattr(self.buffer, "_dealloc_warn", None):
dealloc_warn(source)
class StringIO(TextIOWrapper):
"""Text I/O implementation using an in-memory buffer.

434
Lib/_pylong.py vendored
View File

@@ -45,10 +45,16 @@ except ImportError:
#
# and `mycache[lo]` replaces `base**lo` in the inner function.
#
# While this does give minor speedups (a few percent at best), the primary
# intent is to simplify the functions using this, by eliminating the need for
# them to craft their own ad-hoc caching schemes.
def compute_powers(w, base, more_than, show=False):
# If an algorithm wants the powers of ceiling(w/2) instead of the floor,
# pass keyword argument `need_hi=True`.
#
# While this does give minor speedups (a few percent at best), the
# primary intent is to simplify the functions using this, by eliminating
# the need for them to craft their own ad-hoc caching schemes.
#
# See code near end of file for a block of code that can be enabled to
# run millions of tests.
def compute_powers(w, base, more_than, *, need_hi=False, show=False):
seen = set()
need = set()
ws = {w}
@@ -58,40 +64,70 @@ def compute_powers(w, base, more_than, show=False):
continue
seen.add(w)
lo = w >> 1
# only _need_ lo here; some other path may, or may not, need hi
need.add(lo)
ws.add(lo)
if w & 1:
ws.add(lo + 1)
hi = w - lo
# only _need_ one here; the other may, or may not, be needed
which = hi if need_hi else lo
need.add(which)
ws.add(which)
if lo != hi:
ws.add(w - which)
# `need` is the set of exponents needed. To compute them all
# efficiently, possibly add other exponents to `extra`. The goal is
# to ensure that each exponent can be gotten from a smaller one via
# multiplying by the base, squaring it, or squaring and then
# multiplying by the base.
#
# If need_hi is False, this is already the case (w can always be
# gotten from w >> 1 via one of the squaring strategies). But we do
# the work anyway, just in case ;-)
#
# Note that speed is irrelevant. These loops are working on little
# ints (exponents) and go around O(log w) times. The total cost is
# insignificant compared to just one of the bigint multiplies.
cands = need.copy()
extra = set()
while cands:
w = max(cands)
cands.remove(w)
lo = w >> 1
if lo > more_than and w-1 not in cands and lo not in cands:
extra.add(lo)
cands.add(lo)
assert need_hi or not extra
d = {}
if not need:
return d
it = iter(sorted(need))
first = next(it)
if show:
print("pow at", first)
d[first] = base ** first
for this in it:
if this - 1 in d:
for n in sorted(need | extra):
lo = n >> 1
hi = n - lo
if n-1 in d:
if show:
print("* base at", this)
d[this] = d[this - 1] * base # cheap
else:
lo = this >> 1
hi = this - lo
assert lo in d
print("* base", end="")
result = d[n-1] * base # cheap!
elif lo in d:
# Multiplying a bigint by itself is about twice as fast
# in CPython provided it's the same object.
if show:
print("square at", this)
# Multiplying a bigint by itself (same object!) is about twice
# as fast in CPython.
sq = d[lo] * d[lo]
print("square", end="")
result = d[lo] * d[lo] # same object
if hi != lo:
assert hi == lo + 1
if show:
print(" and * base")
sq *= base
d[this] = sq
print(" * base", end="")
assert 2 * lo + 1 == n
result *= base
else: # rare
if show:
print("pow", end='')
result = base ** n
if show:
print(" at", n, "needed" if n in need else "extra")
d[n] = result
assert need <= d.keys()
if excess := d.keys() - need:
assert need_hi
for n in excess:
del d[n]
return d
_unbounded_dec_context = decimal.getcontext().copy()
@@ -211,6 +247,145 @@ def _str_to_int_inner(s):
return inner(0, len(s))
# Asymptotically faster version, using the C decimal module. See
# comments at the end of the file. This uses decimal arithmetic to
# convert from base 10 to base 256. The latter is just a string of
# bytes, which CPython can convert very efficiently to a Python int.
# log of 10 to base 256 with best-possible 53-bit precision. Obtained
# via:
# from mpmath import mp
# mp.prec = 1000
# print(float(mp.log(10, 256)).hex())
_LOG_10_BASE_256 = float.fromhex('0x1.a934f0979a371p-2') # about 0.415
# _spread is for internal testing. It maps a key to the number of times
# that condition obtained in _dec_str_to_int_inner:
# key 0 - quotient guess was right
# key 1 - quotient had to be boosted by 1, one time
# key 999 - one adjustment wasn't enough, so fell back to divmod
from collections import defaultdict
_spread = defaultdict(int)
del defaultdict
def _dec_str_to_int_inner(s, *, GUARD=8):
# Yes, BYTELIM is "large". Large enough that CPython will usually
# use the Karatsuba _str_to_int_inner to convert the string. This
# allowed reducing the cutoff for calling _this_ function from 3.5M
# to 2M digits. We could almost certainly do even better by
# fine-tuning this and/or using a larger output base than 256.
BYTELIM = 100_000
D = decimal.Decimal
result = bytearray()
# See notes at end of file for discussion of GUARD.
assert GUARD > 0 # if 0, `decimal` can blow up - .prec 0 not allowed
def inner(n, w):
#assert n < D256 ** w # required, but too expensive to check
if w <= BYTELIM:
# XXX Stefan Pochmann discovered that, for 1024-bit ints,
# `int(Decimal)` took 2.5x longer than `int(str(Decimal))`.
# Worse, `int(Decimal) is still quadratic-time for much
# larger ints. So unless/until all that is repaired, the
# seemingly redundant `str(Decimal)` is crucial to speed.
result.extend(int(str(n)).to_bytes(w)) # big-endian default
return
w1 = w >> 1
w2 = w - w1
if 0:
# This is maximally clear, but "too slow". `decimal`
# division is asymptotically fast, but we have no way to
# tell it to reuse the high-precision reciprocal it computes
# for pow256[w2], so it has to recompute it over & over &
# over again :-(
hi, lo = divmod(n, pow256[w2][0])
else:
p256, recip = pow256[w2]
# The integer part will have a number of digits about equal
# to the difference between the log10s of `n` and `pow256`
# (which, since these are integers, is roughly approximated
# by `.adjusted()`). That's the working precision we need,
ctx.prec = max(n.adjusted() - p256.adjusted(), 0) + GUARD
hi = +n * +recip # unary `+` chops back to ctx.prec digits
ctx.prec = decimal.MAX_PREC
hi = hi.to_integral_value() # lose the fractional digits
lo = n - hi * p256
# Because we've been uniformly rounding down, `hi` is a
# lower bound on the correct quotient.
assert lo >= 0
# Adjust quotient up if needed. It usually isn't. In random
# testing on inputs through 5 billion digit strings, the
# test triggered once in about 200 thousand tries.
count = 0
if lo >= p256:
count = 1
lo -= p256
hi += 1
if lo >= p256:
# Complete correction via an exact computation. I
# believe it's not possible to get here provided
# GUARD >= 3. It's tested by reducing GUARD below
# that.
count = 999
hi2, lo = divmod(lo, p256)
hi += hi2
_spread[count] += 1
# The assert should always succeed, but way too slow to keep
# enabled.
#assert hi, lo == divmod(n, pow256[w2][0])
inner(hi, w1)
del hi # at top levels, can free a lot of RAM "early"
inner(lo, w2)
# How many base 256 digits are needed?. Mathematically, exactly
# floor(log256(int(s))) + 1. There is no cheap way to compute this.
# But we can get an upper bound, and that's necessary for our error
# analysis to make sense. int(s) < 10**len(s), so the log needed is
# < log256(10**len(s)) = len(s) * log256(10). However, using
# finite-precision floating point for this, it's possible that the
# computed value is a little less than the true value. If the true
# value is at - or a little higher than - an integer, we can get an
# off-by-1 error too low. So we add 2 instead of 1 if chopping lost
# a fraction > 0.9.
# The "WASI" test platform can complain about `len(s)` if it's too
# large to fit in its idea of "an index-sized integer".
lenS = s.__len__()
log_ub = lenS * _LOG_10_BASE_256
log_ub_as_int = int(log_ub)
w = log_ub_as_int + 1 + (log_ub - log_ub_as_int > 0.9)
# And what if we've plain exhausted the limits of HW floats? We
# could compute the log to any desired precision using `decimal`,
# but it's not plausible that anyone will pass a string requiring
# trillions of bytes (unless they're just trying to "break things").
if w.bit_length() >= 46:
# "Only" had < 53 - 46 = 7 bits to spare in IEEE-754 double.
raise ValueError(f"cannot convert string of len {lenS} to int")
with decimal.localcontext(_unbounded_dec_context) as ctx:
D256 = D(256)
pow256 = compute_powers(w, D256, BYTELIM, need_hi=True)
rpow256 = compute_powers(w, 1 / D256, BYTELIM, need_hi=True)
# We're going to do inexact, chopped arithmetic, multiplying by
# an approximation to the reciprocal of 256**i. We chop to get a
# lower bound on the true integer quotient. Our approximation is
# a lower bound, the multiplication is chopped too, and
# to_integral_value() is also chopped.
ctx.traps[decimal.Inexact] = 0
ctx.rounding = decimal.ROUND_DOWN
for k, v in pow256.items():
# No need to save much more precision in the reciprocal than
# the power of 256 has, plus some guard digits to absorb
# most relevant rounding errors. This is highly significant:
# 1/2**i has the same number of significant decimal digits
# as 5**i, generally over twice the number in 2**i,
ctx.prec = v.adjusted() + GUARD + 1
# The unary "+" chops the reciprocal back to that precision.
pow256[k] = v, +rpow256[k]
del rpow256 # exact reciprocals no longer needed
ctx.prec = decimal.MAX_PREC
inner(D(s), w)
return int.from_bytes(result)
def int_from_string(s):
"""Asymptotically fast version of PyLong_FromString(), conversion
of a string of decimal digits into an 'int'."""
@@ -219,7 +394,10 @@ def int_from_string(s):
# and underscores, and stripped leading whitespace. The input can still
# contain underscores and have trailing whitespace.
s = s.rstrip().replace('_', '')
return _str_to_int_inner(s)
func = _str_to_int_inner
if len(s) >= 2_000_000 and _decimal is not None:
func = _dec_str_to_int_inner
return func(s)
def str_to_int(s):
"""Asymptotically fast version of decimal string to 'int' conversion."""
@@ -352,7 +530,7 @@ def int_divmod(a, b):
Its time complexity is O(n**1.58), where n = #bits(a) + #bits(b).
"""
if b == 0:
raise ZeroDivisionError
raise ZeroDivisionError('division by zero')
elif b < 0:
q, r = int_divmod(-a, -b)
return q, -r
@@ -361,3 +539,191 @@ def int_divmod(a, b):
return ~q, b + ~r
else:
return _divmod_pos(a, b)
# Notes on _dec_str_to_int_inner:
#
# Stefan Pochmann worked up a str->int function that used the decimal
# module to, in effect, convert from base 10 to base 256. This is
# "unnatural", in that it requires multiplying and dividing by large
# powers of 2, which `decimal` isn't naturally suited to. But
# `decimal`'s `*` and `/` are asymptotically superior to CPython's, so
# at _some_ point it could be expected to win.
#
# Alas, the crossover point was too high to be of much real interest. I
# (Tim) then worked on ways to replace its division with multiplication
# by a cached reciprocal approximation instead, fixing up errors
# afterwards. This reduced the crossover point significantly,
#
# I revisited the code, and found ways to improve and simplify it. The
# crossover point is at about 3.4 million digits now.
#
# About .adjusted()
# -----------------
# Restrict to Decimal values x > 0. We don't use negative numbers in the
# code, and I don't want to have to keep typing, e.g., "absolute value".
#
# For convenience, I'll use `x.a` to mean `x.adjusted()`. x.a doesn't
# look at the digits of x, but instead returns an integer giving x's
# order of magnitude. These are equivalent:
#
# - x.a is the power-of-10 exponent of x's most significant digit.
# - x.a = the infinitely precise floor(log10(x))
# - x can be written in this form, where f is a real with 1 <= f < 10:
# x = f * 10**x.a
#
# Observation; if x is an integer, len(str(x)) = x.a + 1.
#
# Lemma 1: (x * y).a = x.a + y.a, or one larger
#
# Proof: Write x = f * 10**x.a and y = g * 10**y.a, where f and g are in
# [1, 10). Then x*y = f*g * 10**(x.a + y.a), where 1 <= f*g < 100. If
# f*g < 10, (x*y).a is x.a+y.a. Else divide f*g by 10 to bring it back
# into [1, 10], and add 1 to the exponent to compensate. Then (x*y).a is
# x.a+y.a+1.
#
# Lemma 2: ceiling(log10(x/y)) <= x.a - y.a + 1
#
# Proof: Express x and y as in Lemma 1. Then x/y = f/g * 10**(x.a -
# y.a), where 1/10 < f/g < 10. If 1 <= f/g, (x/y).a is x.a-y.a. Else
# multiply f/g by 10 to bring it back into [1, 10], and subtract 1 from
# the exponent to compensate. Then (x/y).a is x.a-y.a-1. So the largest
# (x/y).a can be is x.a-y.a. Since that's the floor of log10(x/y). the
# ceiling is at most 1 larger (with equality iff f/g = 1 exactly).
#
# GUARD digits
# ------------
# We only want the integer part of divisions, so don't need to build
# the full multiplication tree. But using _just_ the number of
# digits expected in the integer part ignores too much. What's left
# out can have a very significant effect on the quotient. So we use
# GUARD additional digits.
#
# The default 8 is more than enough so no more than 1 correction step
# was ever needed for all inputs tried through 2.5 billion digits. In
# fact, I believe 3 guard digits are always enough - but the proof is
# very involved, so better safe than sorry.
#
# Short course:
#
# If prec is the decimal precision in effect, and we're rounding down,
# the result of an operation is exactly equal to the infinitely precise
# result times 1-e for some real e with 0 <= e < 10**(1-prec). In
#
# ctx.prec = max(n.adjusted() - p256.adjusted(), 0) + GUARD
# hi = +n * +recip # unary `+` chops to ctx.prec digits
#
# we have 3 visible chopped operations, but there's also a 4th:
# precomputing a truncated `recip` as part of setup.
#
# So the computed product is exactly equal to the true product times
# (1-e1)*(1-e2)*(1-e3)*(1-e4); since the e's are all very small, an
# excellent approximation to the second factor is 1-(e1+e2+e3+e4) (the
# 2nd and higher order terms in the expanded product are too tiny to
# matter). If they're all as large as possible, that's
#
# 1 - 4*10**(1-prec). This, BTW, is all bog-standard FP error analysis.
#
# That implies the computed product is within 1 of the true product
# provided prec >= log10(true_product) + 1.602.
#
# Here are telegraphic details, rephrasing the initial condition in
# equivalent ways, step by step:
#
# prod - prod * (1 - 4*10**(1-prec)) <= 1
# prod - prod + prod * 4*10**(1-prec)) <= 1
# prod * 4*10**(1-prec)) <= 1
# 10**(log10(prod)) * 4*10**(1-prec)) <= 1
# 4*10**(1-prec+log10(prod))) <= 1
# 10**(1-prec+log10(prod))) <= 1/4
# 1-prec+log10(prod) <= log10(1/4) = -0.602
# -prec <= -1.602 - log10(prod)
# prec >= log10(prod) + 1.602
#
# The true product is the same as the true ratio n/p256. By Lemma 2
# above, n.a - p256.a + 1 is an upper bound on the ceiling of
# log10(prod). Then 2 is the ceiling of 1.602. so n.a - p256.a + 3 is an
# upper bound on the right hand side of the inequality. Any prec >= that
# will work.
#
# But since this is just a sketch of a proof ;-), the code uses the
# empirically tested 8 instead of 3. 5 digits more or less makes no
# practical difference to speed - these ints are huge. And while
# increasing GUARD above 3 may not be necessary, every increase cuts the
# percentage of cases that need a correction at all.
#
# On Computing Reciprocals
# ------------------------
# In general, the exact reciprocals we compute have over twice as many
# significant digits as needed. 1/256**i has the same number of
# significant decimal digits as 5**i. It's a significant waste of RAM
# to store all those unneeded digits.
#
# So we cut exact reciprocals back to the least precision that can
# be needed so that the error analysis above is valid,
#
# [Note: turns out it's very significantly faster to do it this way than
# to compute 1 / 256**i directly to the desired precision, because the
# power method doesn't require division. It's also faster than computing
# (1/256)**i directly to the desired precision - no material division
# there, but `compute_powers()` is much smarter about _how_ to compute
# all the powers needed than repeated applications of `**` - that
# function invokes `**` for at most the few smallest powers needed.]
#
# The hard part is that chopping back to a shorter width occurs
# _outside_ of `inner`. We can't know then what `prec` `inner()` will
# need. We have to pick, for each value of `w2`, the largest possible
# value `prec` can become when `inner()` is working on `w2`.
#
# This is the `prec` inner() uses:
# max(n.a - p256.a, 0) + GUARD
# and what setup uses (renaming its `v` to `p256` - same thing):
# p256.a + GUARD + 1
#
# We need that the second is always at least as large as the first,
# which is the same as requiring
#
# n.a - 2 * p256.a <= 1
#
# What's the largest n can be? n < 255**w = 256**(w2 + (w - w2)). The
# worst case in this context is when w ix even. and then w = 2*w2, so
# n < 256**(2*w2) = (256**w2)**2 = p256**2. By Lemma 1, then, n.a
# is at most p256.a + p256.a + 1.
#
# So the most n.a - 2 * p256.a can be is
# p256.a + p256.a + 1 - 2 * p256.a = 1. QED
#
# Note: an earlier version of the code split on floor(e/2) instead of on
# the ceiling. The worst case then is odd `w`, and a more involved proof
# was needed to show that adding 4 (instead of 1) may be necessary.
# Basically because, in that case, n may be up to 256 times larger than
# p256**2. Curiously enough, by splitting on the ceiling instead,
# nothing in any proof here actually depends on the output base (256).
# Enable for brute-force testing of compute_powers(). This takes about a
# minute, because it tries millions of cases.
if 0:
def consumer(w, limit, need_hi):
seen = set()
need = set()
def inner(w):
if w <= limit:
return
if w in seen:
return
seen.add(w)
lo = w >> 1
hi = w - lo
need.add(hi if need_hi else lo)
inner(lo)
inner(hi)
inner(w)
exp = compute_powers(w, 1, limit, need_hi=need_hi)
assert exp.keys() == need
from itertools import chain
for need_hi in (False, True):
for limit in (0, 1, 10, 100, 1_000, 10_000, 100_000):
for w in chain(range(1, 100_000),
(10**i for i in range(5, 30))):
consumer(w, limit, need_hi)

419
Lib/_strptime.py vendored
View File

@@ -10,10 +10,13 @@ FUNCTIONS:
strptime -- Calculates the time struct represented by the passed-in string
"""
import os
import time
import locale
import calendar
import re
from re import compile as re_compile
from re import sub as re_sub
from re import IGNORECASE
from re import escape as re_escape
from datetime import (date as datetime_date,
@@ -27,6 +30,41 @@ def _getlang():
# Figure out what the current language is set to.
return locale.getlocale(locale.LC_TIME)
def _findall(haystack, needle):
# Find all positions of needle in haystack.
if not needle:
return
i = 0
while True:
i = haystack.find(needle, i)
if i < 0:
break
yield i
i += len(needle)
def _fixmonths(months):
yield from months
# The lower case of 'İ' ('\u0130') is 'i\u0307'.
# The re module only supports 1-to-1 character matching in
# case-insensitive mode.
for s in months:
if 'i\u0307' in s:
yield s.replace('i\u0307', '\u0130')
lzh_TW_alt_digits = (
# :一:二:三:四:五:六:七:八:九
'\u3007', '\u4e00', '\u4e8c', '\u4e09', '\u56db',
'\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d',
# 十:十一:十二:十三:十四:十五:十六:十七:十八:十九
'\u5341', '\u5341\u4e00', '\u5341\u4e8c', '\u5341\u4e09', '\u5341\u56db',
'\u5341\u4e94', '\u5341\u516d', '\u5341\u4e03', '\u5341\u516b', '\u5341\u4e5d',
# 廿:廿一:廿二:廿三:廿四:廿五:廿六:廿七:廿八:廿九
'\u5eff', '\u5eff\u4e00', '\u5eff\u4e8c', '\u5eff\u4e09', '\u5eff\u56db',
'\u5eff\u4e94', '\u5eff\u516d', '\u5eff\u4e03', '\u5eff\u516b', '\u5eff\u4e5d',
# 卅:卅一
'\u5345', '\u5345\u4e00')
class LocaleTime(object):
"""Stores and handles locale-specific information related to time.
@@ -70,6 +108,7 @@ class LocaleTime(object):
self.__calc_weekday()
self.__calc_month()
self.__calc_am_pm()
self.__calc_alt_digits()
self.__calc_timezone()
self.__calc_date_time()
if _getlang() != self.lang:
@@ -101,53 +140,184 @@ class LocaleTime(object):
am_pm = []
for hour in (1, 22):
time_tuple = time.struct_time((1999,3,17,hour,44,55,2,76,0))
am_pm.append(time.strftime("%p", time_tuple).lower())
# br_FR has AM/PM info (' ',' ').
am_pm.append(time.strftime("%p", time_tuple).lower().strip())
self.am_pm = am_pm
def __calc_alt_digits(self):
# Set self.LC_alt_digits by using time.strftime().
# The magic data should contain all decimal digits.
time_tuple = time.struct_time((1998, 1, 27, 10, 43, 56, 1, 27, 0))
s = time.strftime("%x%X", time_tuple)
if s.isascii():
# Fast path -- all digits are ASCII.
self.LC_alt_digits = ()
return
digits = ''.join(sorted(set(re.findall(r'\d', s))))
if len(digits) == 10 and ord(digits[-1]) == ord(digits[0]) + 9:
# All 10 decimal digits from the same set.
if digits.isascii():
# All digits are ASCII.
self.LC_alt_digits = ()
return
self.LC_alt_digits = [a + b for a in digits for b in digits]
# Test whether the numbers contain leading zero.
time_tuple2 = time.struct_time((2000, 1, 1, 1, 1, 1, 5, 1, 0))
if self.LC_alt_digits[1] not in time.strftime("%x %X", time_tuple2):
self.LC_alt_digits[:10] = digits
return
# Either non-Gregorian calendar or non-decimal numbers.
if {'\u4e00', '\u4e03', '\u4e5d', '\u5341', '\u5eff'}.issubset(s):
# lzh_TW
self.LC_alt_digits = lzh_TW_alt_digits
return
self.LC_alt_digits = None
def __calc_date_time(self):
# Set self.date_time, self.date, & self.time by using
# time.strftime().
# Set self.LC_date_time, self.LC_date, self.LC_time and
# self.LC_time_ampm by using time.strftime().
# Use (1999,3,17,22,44,55,2,76,0) for magic date because the amount of
# overloaded numbers is minimized. The order in which searches for
# values within the format string is very important; it eliminates
# possible ambiguity for what something represents.
time_tuple = time.struct_time((1999,3,17,22,44,55,2,76,0))
date_time = [None, None, None]
date_time[0] = time.strftime("%c", time_tuple).lower()
date_time[1] = time.strftime("%x", time_tuple).lower()
date_time[2] = time.strftime("%X", time_tuple).lower()
replacement_pairs = [('%', '%%'), (self.f_weekday[2], '%A'),
(self.f_month[3], '%B'), (self.a_weekday[2], '%a'),
(self.a_month[3], '%b'), (self.am_pm[1], '%p'),
('1999', '%Y'), ('99', '%y'), ('22', '%H'),
('44', '%M'), ('55', '%S'), ('76', '%j'),
('17', '%d'), ('03', '%m'), ('3', '%m'),
# '3' needed for when no leading zero.
('2', '%w'), ('10', '%I')]
replacement_pairs.extend([(tz, "%Z") for tz_values in self.timezone
for tz in tz_values])
for offset,directive in ((0,'%c'), (1,'%x'), (2,'%X')):
current_format = date_time[offset]
for old, new in replacement_pairs:
time_tuple2 = time.struct_time((1999,1,3,1,1,1,6,3,0))
replacement_pairs = []
# Non-ASCII digits
if self.LC_alt_digits or self.LC_alt_digits is None:
for n, d in [(19, '%OC'), (99, '%Oy'), (22, '%OH'),
(44, '%OM'), (55, '%OS'), (17, '%Od'),
(3, '%Om'), (2, '%Ow'), (10, '%OI')]:
if self.LC_alt_digits is None:
s = chr(0x660 + n // 10) + chr(0x660 + n % 10)
replacement_pairs.append((s, d))
if n < 10:
replacement_pairs.append((s[1], d))
elif len(self.LC_alt_digits) > n:
replacement_pairs.append((self.LC_alt_digits[n], d))
else:
replacement_pairs.append((time.strftime(d, time_tuple), d))
replacement_pairs += [
('1999', '%Y'), ('99', '%y'), ('22', '%H'),
('44', '%M'), ('55', '%S'), ('76', '%j'),
('17', '%d'), ('03', '%m'), ('3', '%m'),
# '3' needed for when no leading zero.
('2', '%w'), ('10', '%I'),
]
date_time = []
for directive in ('%c', '%x', '%X', '%r'):
current_format = time.strftime(directive, time_tuple).lower()
current_format = current_format.replace('%', '%%')
# The month and the day of the week formats are treated specially
# because of a possible ambiguity in some locales where the full
# and abbreviated names are equal or names of different types
# are equal. See doc of __find_month_format for more details.
lst, fmt = self.__find_weekday_format(directive)
if lst:
current_format = current_format.replace(lst[2], fmt, 1)
lst, fmt = self.__find_month_format(directive)
if lst:
current_format = current_format.replace(lst[3], fmt, 1)
if self.am_pm[1]:
# Must deal with possible lack of locale info
# manifesting itself as the empty string (e.g., Swedish's
# lack of AM/PM info) or a platform returning a tuple of empty
# strings (e.g., MacOS 9 having timezone as ('','')).
if old:
current_format = current_format.replace(old, new)
current_format = current_format.replace(self.am_pm[1], '%p')
for tz_values in self.timezone:
for tz in tz_values:
if tz:
current_format = current_format.replace(tz, "%Z")
# Transform all non-ASCII digits to digits in range U+0660 to U+0669.
if not current_format.isascii() and self.LC_alt_digits is None:
current_format = re_sub(r'\d(?<![0-9])',
lambda m: chr(0x0660 + int(m[0])),
current_format)
for old, new in replacement_pairs:
current_format = current_format.replace(old, new)
# If %W is used, then Sunday, 2005-01-03 will fall on week 0 since
# 2005-01-03 occurs before the first Monday of the year. Otherwise
# %U is used.
time_tuple = time.struct_time((1999,1,3,1,1,1,6,3,0))
if '00' in time.strftime(directive, time_tuple):
if '00' in time.strftime(directive, time_tuple2):
U_W = '%W'
else:
U_W = '%U'
date_time[offset] = current_format.replace('11', U_W)
current_format = current_format.replace('11', U_W)
date_time.append(current_format)
self.LC_date_time = date_time[0]
self.LC_date = date_time[1]
self.LC_time = date_time[2]
self.LC_time_ampm = date_time[3]
def __find_month_format(self, directive):
"""Find the month format appropriate for the current locale.
In some locales (for example French and Hebrew), the default month
used in __calc_date_time has the same name in full and abbreviated
form. Also, the month name can by accident match other part of the
representation: the day of the week name (for example in Morisyen)
or the month number (for example in Japanese). Thus, cycle months
of the year and find all positions that match the month name for
each month, If no common positions are found, the representation
does not use the month name.
"""
full_indices = abbr_indices = None
for m in range(1, 13):
time_tuple = time.struct_time((1999, m, 17, 22, 44, 55, 2, 76, 0))
datetime = time.strftime(directive, time_tuple).lower()
indices = set(_findall(datetime, self.f_month[m]))
if full_indices is None:
full_indices = indices
else:
full_indices &= indices
indices = set(_findall(datetime, self.a_month[m]))
if abbr_indices is None:
abbr_indices = set(indices)
else:
abbr_indices &= indices
if not full_indices and not abbr_indices:
return None, None
if full_indices:
return self.f_month, '%B'
if abbr_indices:
return self.a_month, '%b'
return None, None
def __find_weekday_format(self, directive):
"""Find the day of the week format appropriate for the current locale.
Similar to __find_month_format().
"""
full_indices = abbr_indices = None
for wd in range(7):
time_tuple = time.struct_time((1999, 3, 17, 22, 44, 55, wd, 76, 0))
datetime = time.strftime(directive, time_tuple).lower()
indices = set(_findall(datetime, self.f_weekday[wd]))
if full_indices is None:
full_indices = indices
else:
full_indices &= indices
if self.f_weekday[wd] != self.a_weekday[wd]:
indices = set(_findall(datetime, self.a_weekday[wd]))
if abbr_indices is None:
abbr_indices = set(indices)
else:
abbr_indices &= indices
if not full_indices and not abbr_indices:
return None, None
if full_indices:
return self.f_weekday, '%A'
if abbr_indices:
return self.a_weekday, '%a'
return None, None
def __calc_timezone(self):
# Set self.timezone by using time.tzname.
@@ -181,12 +351,14 @@ class TimeRE(dict):
else:
self.locale_time = LocaleTime()
base = super()
base.__init__({
mapping = {
# The " [1-9]" part of the regex is to make %c from ANSI C work
'd': r"(?P<d>3[0-1]|[1-2]\d|0[1-9]|[1-9]| [1-9])",
'f': r"(?P<f>[0-9]{1,6})",
'H': r"(?P<H>2[0-3]|[0-1]\d|\d)",
'I': r"(?P<I>1[0-2]|0[1-9]|[1-9])",
'H': r"(?P<H>2[0-3]|[0-1]\d|\d| \d)",
'k': r"(?P<H>2[0-3]|[0-1]\d|\d| \d)",
'I': r"(?P<I>1[0-2]|0[1-9]|[1-9]| [1-9])",
'l': r"(?P<I>1[0-2]|0[1-9]|[1-9]| [1-9])",
'G': r"(?P<G>\d\d\d\d)",
'j': r"(?P<j>36[0-6]|3[0-5]\d|[1-2]\d\d|0[1-9]\d|00[1-9]|[1-9]\d|0[1-9]|[1-9])",
'm': r"(?P<m>1[0-2]|0[1-9]|[1-9])",
@@ -198,25 +370,60 @@ class TimeRE(dict):
'V': r"(?P<V>5[0-3]|0[1-9]|[1-4]\d|\d)",
# W is set below by using 'U'
'y': r"(?P<y>\d\d)",
#XXX: Does 'Y' need to worry about having less or more than
# 4 digits?
'Y': r"(?P<Y>\d\d\d\d)",
'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
'b': self.__seqToRE(self.locale_time.a_month[1:], 'b'),
'B': self.__seqToRE(_fixmonths(self.locale_time.f_month[1:]), 'B'),
'b': self.__seqToRE(_fixmonths(self.locale_time.a_month[1:]), 'b'),
'p': self.__seqToRE(self.locale_time.am_pm, 'p'),
'Z': self.__seqToRE((tz for tz_names in self.locale_time.timezone
for tz in tz_names),
'Z'),
'%': '%'})
base.__setitem__('W', base.__getitem__('U').replace('U', 'W'))
base.__setitem__('c', self.pattern(self.locale_time.LC_date_time))
base.__setitem__('x', self.pattern(self.locale_time.LC_date))
base.__setitem__('X', self.pattern(self.locale_time.LC_time))
'%': '%'}
if self.locale_time.LC_alt_digits is None:
for d in 'dmyCHIMS':
mapping['O' + d] = r'(?P<%s>\d\d|\d| \d)' % d
mapping['Ow'] = r'(?P<w>\d)'
else:
mapping.update({
'Od': self.__seqToRE(self.locale_time.LC_alt_digits[1:32], 'd',
'3[0-1]|[1-2][0-9]|0[1-9]|[1-9]'),
'Om': self.__seqToRE(self.locale_time.LC_alt_digits[1:13], 'm',
'1[0-2]|0[1-9]|[1-9]'),
'Ow': self.__seqToRE(self.locale_time.LC_alt_digits[:7], 'w',
'[0-6]'),
'Oy': self.__seqToRE(self.locale_time.LC_alt_digits, 'y',
'[0-9][0-9]'),
'OC': self.__seqToRE(self.locale_time.LC_alt_digits, 'C',
'[0-9][0-9]'),
'OH': self.__seqToRE(self.locale_time.LC_alt_digits[:24], 'H',
'2[0-3]|[0-1][0-9]|[0-9]'),
'OI': self.__seqToRE(self.locale_time.LC_alt_digits[1:13], 'I',
'1[0-2]|0[1-9]|[1-9]'),
'OM': self.__seqToRE(self.locale_time.LC_alt_digits[:60], 'M',
'[0-5][0-9]|[0-9]'),
'OS': self.__seqToRE(self.locale_time.LC_alt_digits[:62], 'S',
'6[0-1]|[0-5][0-9]|[0-9]'),
})
mapping.update({
'e': mapping['d'],
'Oe': mapping['Od'],
'P': mapping['p'],
'Op': mapping['p'],
'W': mapping['U'].replace('U', 'W'),
})
mapping['W'] = mapping['U'].replace('U', 'W')
def __seqToRE(self, to_convert, directive):
base.__init__(mapping)
base.__setitem__('T', self.pattern('%H:%M:%S'))
base.__setitem__('R', self.pattern('%H:%M'))
base.__setitem__('r', self.pattern(self.locale_time.LC_time_ampm))
base.__setitem__('X', self.pattern(self.locale_time.LC_time))
base.__setitem__('x', self.pattern(self.locale_time.LC_date))
base.__setitem__('c', self.pattern(self.locale_time.LC_date_time))
def __seqToRE(self, to_convert, directive, altregex=None):
"""Convert a list to a regex string for matching a directive.
Want possible matching values to be from longest to shortest. This
@@ -232,8 +439,9 @@ class TimeRE(dict):
else:
return ''
regex = '|'.join(re_escape(stuff) for stuff in to_convert)
regex = '(?P<%s>%s' % (directive, regex)
return '%s)' % regex
if altregex is not None:
regex += '|' + altregex
return '(?P<%s>%s)' % (directive, regex)
def pattern(self, format):
"""Return regex pattern for the format string.
@@ -242,21 +450,36 @@ class TimeRE(dict):
regex syntax are escaped.
"""
processed_format = ''
# The sub() call escapes all characters that might be misconstrued
# as regex syntax. Cannot use re.escape since we have to deal with
# format directives (%m, etc.).
regex_chars = re_compile(r"([\\.^$*+?\(\){}\[\]|])")
format = regex_chars.sub(r"\\\1", format)
whitespace_replacement = re_compile(r'\s+')
format = whitespace_replacement.sub(r'\\s+', format)
while '%' in format:
directive_index = format.index('%')+1
processed_format = "%s%s%s" % (processed_format,
format[:directive_index-1],
self[format[directive_index]])
format = format[directive_index+1:]
return "%s%s" % (processed_format, format)
format = re_sub(r"([\\.^$*+?\(\){}\[\]|])", r"\\\1", format)
format = re_sub(r'\s+', r'\\s+', format)
format = re_sub(r"'", "['\u02bc]", format) # needed for br_FR
year_in_format = False
day_of_month_in_format = False
def repl(m):
format_char = m[1]
match format_char:
case 'Y' | 'y' | 'G':
nonlocal year_in_format
year_in_format = True
case 'd':
nonlocal day_of_month_in_format
day_of_month_in_format = True
return self[format_char]
format = re_sub(r'%[-_0^#]*[0-9]*([OE]?\\?.?)', repl, format)
if day_of_month_in_format and not year_in_format:
import warnings
warnings.warn("""\
Parsing dates involving a day of month without a year specified is ambiguous
and fails to parse leap day. The default behavior will change in Python 3.15
to either always raise an exception or to use a different default year (TBD).
To avoid trouble, add a specific year to the input & format.
See https://github.com/python/cpython/issues/70647.""",
DeprecationWarning,
skip_file_prefixes=(os.path.dirname(__file__),))
return format
def compile(self, format):
"""Return a compiled re object for the format string."""
@@ -319,14 +542,13 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
# \\, in which case it was a stray % but with a space after it
except KeyError as err:
bad_directive = err.args[0]
if bad_directive == "\\":
bad_directive = "%"
del err
bad_directive = bad_directive.replace('\\s', '')
if not bad_directive:
raise ValueError("stray %% in format '%s'" % format) from None
bad_directive = bad_directive.replace('\\', '', 1)
raise ValueError("'%s' is a bad directive in format '%s'" %
(bad_directive, format)) from None
# IndexError only occurs when the format string is "%"
except IndexError:
raise ValueError("stray %% in format '%s'" % format) from None
_regex_cache[format] = format_regex
found = format_regex.match(data_string)
if not found:
@@ -348,6 +570,15 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
# values
weekday = julian = None
found_dict = found.groupdict()
if locale_time.LC_alt_digits:
def parse_int(s):
try:
return locale_time.LC_alt_digits.index(s)
except ValueError:
return int(s)
else:
parse_int = int
for group_key in found_dict.keys():
# Directives not explicitly handled below:
# c, x, X
@@ -355,30 +586,34 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
# U, W
# worthless without day of the week
if group_key == 'y':
year = int(found_dict['y'])
# Open Group specification for strptime() states that a %y
#value in the range of [00, 68] is in the century 2000, while
#[69,99] is in the century 1900
if year <= 68:
year += 2000
year = parse_int(found_dict['y'])
if 'C' in found_dict:
century = parse_int(found_dict['C'])
year += century * 100
else:
year += 1900
# Open Group specification for strptime() states that a %y
#value in the range of [00, 68] is in the century 2000, while
#[69,99] is in the century 1900
if year <= 68:
year += 2000
else:
year += 1900
elif group_key == 'Y':
year = int(found_dict['Y'])
elif group_key == 'G':
iso_year = int(found_dict['G'])
elif group_key == 'm':
month = int(found_dict['m'])
month = parse_int(found_dict['m'])
elif group_key == 'B':
month = locale_time.f_month.index(found_dict['B'].lower())
elif group_key == 'b':
month = locale_time.a_month.index(found_dict['b'].lower())
elif group_key == 'd':
day = int(found_dict['d'])
day = parse_int(found_dict['d'])
elif group_key == 'H':
hour = int(found_dict['H'])
hour = parse_int(found_dict['H'])
elif group_key == 'I':
hour = int(found_dict['I'])
hour = parse_int(found_dict['I'])
ampm = found_dict.get('p', '').lower()
# If there was no AM/PM indicator, we'll treat this like AM
if ampm in ('', locale_time.am_pm[0]):
@@ -394,9 +629,9 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
if hour != 12:
hour += 12
elif group_key == 'M':
minute = int(found_dict['M'])
minute = parse_int(found_dict['M'])
elif group_key == 'S':
second = int(found_dict['S'])
second = parse_int(found_dict['S'])
elif group_key == 'f':
s = found_dict['f']
# Pad to always return microseconds.
@@ -548,18 +783,40 @@ def _strptime_time(data_string, format="%a %b %d %H:%M:%S %Y"):
tt = _strptime(data_string, format)[0]
return time.struct_time(tt[:time._STRUCT_TM_ITEMS])
def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a class cls instance based on the input string and the
def _strptime_datetime_date(cls, data_string, format="%a %b %d %Y"):
"""Return a date instance based on the input string and the
format string."""
tt, _, _ = _strptime(data_string, format)
args = tt[:3]
return cls(*args)
def _parse_tz(tzname, gmtoff, gmtoff_fraction):
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
if tzname:
return datetime_timezone(tzdelta, tzname)
else:
return datetime_timezone(tzdelta)
def _strptime_datetime_time(cls, data_string, format="%H:%M:%S"):
"""Return a time instance based on the input string and the
format string."""
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
tzname, gmtoff = tt[-2:]
args = tt[3:6] + (fraction,)
if gmtoff is None:
return cls(*args)
else:
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
return cls(*args, tz)
def _strptime_datetime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"):
"""Return a datetime instance based on the input string and the
format string."""
tt, fraction, gmtoff_fraction = _strptime(data_string, format)
tzname, gmtoff = tt[-2:]
args = tt[:6] + (fraction,)
if gmtoff is not None:
tzdelta = datetime_timedelta(seconds=gmtoff, microseconds=gmtoff_fraction)
if tzname:
tz = datetime_timezone(tzdelta, tzname)
else:
tz = datetime_timezone(tzdelta)
args += (tz,)
return cls(*args)
if gmtoff is None:
return cls(*args)
else:
tz = _parse_tz(tzname, gmtoff, gmtoff_fraction)
return cls(*args, tz)

78
Lib/_weakrefset.py vendored
View File

@@ -8,69 +8,29 @@ from types import GenericAlias
__all__ = ['WeakSet']
class _IterationGuard:
# This context manager registers itself in the current iterators of the
# weak container, such as to delay all removals until the context manager
# exits.
# This technique should be relatively thread-safe (since sets are).
def __init__(self, weakcontainer):
# Don't create cycles
self.weakcontainer = ref(weakcontainer)
def __enter__(self):
w = self.weakcontainer()
if w is not None:
w._iterating.add(self)
return self
def __exit__(self, e, t, b):
w = self.weakcontainer()
if w is not None:
s = w._iterating
s.remove(self)
if not s:
w._commit_removals()
class WeakSet:
def __init__(self, data=None):
self.data = set()
def _remove(item, selfref=ref(self)):
self = selfref()
if self is not None:
if self._iterating:
self._pending_removals.append(item)
else:
self.data.discard(item)
self.data.discard(item)
self._remove = _remove
# A list of keys to be removed
self._pending_removals = []
self._iterating = set()
if data is not None:
self.update(data)
def _commit_removals(self):
pop = self._pending_removals.pop
discard = self.data.discard
while True:
try:
item = pop()
except IndexError:
return
discard(item)
def __iter__(self):
with _IterationGuard(self):
for itemref in self.data:
item = itemref()
if item is not None:
# Caveat: the iterator will keep a strong reference to
# `item` until it is resumed or closed.
yield item
for itemref in self.data.copy():
item = itemref()
if item is not None:
# Caveat: the iterator will keep a strong reference to
# `item` until it is resumed or closed.
yield item
def __len__(self):
return len(self.data) - len(self._pending_removals)
return len(self.data)
def __contains__(self, item):
try:
@@ -83,21 +43,15 @@ class WeakSet:
return self.__class__, (list(self),), self.__getstate__()
def add(self, item):
if self._pending_removals:
self._commit_removals()
self.data.add(ref(item, self._remove))
def clear(self):
if self._pending_removals:
self._commit_removals()
self.data.clear()
def copy(self):
return self.__class__(self)
def pop(self):
if self._pending_removals:
self._commit_removals()
while True:
try:
itemref = self.data.pop()
@@ -108,18 +62,12 @@ class WeakSet:
return item
def remove(self, item):
if self._pending_removals:
self._commit_removals()
self.data.remove(ref(item))
def discard(self, item):
if self._pending_removals:
self._commit_removals()
self.data.discard(ref(item))
def update(self, other):
if self._pending_removals:
self._commit_removals()
for element in other:
self.add(element)
@@ -136,8 +84,6 @@ class WeakSet:
def difference_update(self, other):
self.__isub__(other)
def __isub__(self, other):
if self._pending_removals:
self._commit_removals()
if self is other:
self.data.clear()
else:
@@ -151,8 +97,6 @@ class WeakSet:
def intersection_update(self, other):
self.__iand__(other)
def __iand__(self, other):
if self._pending_removals:
self._commit_removals()
self.data.intersection_update(ref(item) for item in other)
return self
@@ -184,8 +128,6 @@ class WeakSet:
def symmetric_difference_update(self, other):
self.__ixor__(other)
def __ixor__(self, other):
if self._pending_removals:
self._commit_removals()
if self is other:
self.data.clear()
else:

1152
Lib/annotationlib.py vendored Normal file

File diff suppressed because it is too large Load Diff

572
Lib/argparse.py vendored
View File

@@ -18,11 +18,12 @@ command-line and writes the result to a file::
'integers', metavar='int', nargs='+', type=int,
help='an integer to be summed')
parser.add_argument(
'--log', default=sys.stdout, type=argparse.FileType('w'),
'--log',
help='the file where the sum should be written')
args = parser.parse_args()
args.log.write('%s' % sum(args.integers))
args.log.close()
with (open(args.log, 'w') if args.log is not None
else contextlib.nullcontext(sys.stdout)) as log:
log.write('%s' % sum(args.integers))
The module contains the following public classes:
@@ -39,7 +40,8 @@ The module contains the following public classes:
- FileType -- A factory for defining types of files to be created. As the
example above shows, instances of FileType are typically passed as
the type= argument of add_argument() calls.
the type= argument of add_argument() calls. Deprecated since
Python 3.14.
- Action -- The base class for parser actions. Typically actions are
selected by passing strings like 'store_true' or 'append_const' to
@@ -159,18 +161,21 @@ class HelpFormatter(object):
provided by the class are considered an implementation detail.
"""
def __init__(self,
prog,
indent_increment=2,
max_help_position=24,
width=None):
def __init__(
self,
prog,
indent_increment=2,
max_help_position=24,
width=None,
color=True,
):
# default setting for width
if width is None:
import shutil
width = shutil.get_terminal_size().columns
width -= 2
self._set_color(color)
self._prog = prog
self._indent_increment = indent_increment
self._max_help_position = min(max_help_position,
@@ -187,6 +192,16 @@ class HelpFormatter(object):
self._whitespace_matcher = _re.compile(r'\s+', _re.ASCII)
self._long_break_matcher = _re.compile(r'\n\n\n+')
def _set_color(self, color):
from _colorize import can_colorize, decolor, get_theme
if color and can_colorize():
self._theme = get_theme(force_color=True).argparse
self._decolor = decolor
else:
self._theme = get_theme(force_no_color=True).argparse
self._decolor = lambda text: text
# ===============================
# Section and indentation methods
# ===============================
@@ -225,7 +240,11 @@ class HelpFormatter(object):
if self.heading is not SUPPRESS and self.heading is not None:
current_indent = self.formatter._current_indent
heading_text = _('%(heading)s:') % dict(heading=self.heading)
heading = '%*s%s\n' % (current_indent, '', heading_text)
t = self.formatter._theme
heading = (
f'{" " * current_indent}'
f'{t.heading}{heading_text}{t.reset}\n'
)
else:
heading = ''
@@ -262,7 +281,7 @@ class HelpFormatter(object):
if action.help is not SUPPRESS:
# find all invocations
get_invocation = self._format_action_invocation
get_invocation = lambda x: self._decolor(self._format_action_invocation(x))
invocation_lengths = [len(get_invocation(action)) + self._current_indent]
for subaction in self._iter_indented_subactions(action):
invocation_lengths.append(len(get_invocation(subaction)) + self._current_indent)
@@ -296,42 +315,39 @@ class HelpFormatter(object):
if part and part is not SUPPRESS])
def _format_usage(self, usage, actions, groups, prefix):
t = self._theme
if prefix is None:
prefix = _('usage: ')
# if usage is specified, use that
if usage is not None:
usage = usage % dict(prog=self._prog)
usage = (
t.prog_extra
+ usage
% {"prog": f"{t.prog}{self._prog}{t.reset}{t.prog_extra}"}
+ t.reset
)
# if no optionals or positionals are available, usage is just prog
elif usage is None and not actions:
usage = '%(prog)s' % dict(prog=self._prog)
usage = f"{t.prog}{self._prog}{t.reset}"
# if optionals and positionals are available, calculate usage
elif usage is None:
prog = '%(prog)s' % dict(prog=self._prog)
# split optionals from positionals
optionals = []
positionals = []
for action in actions:
if action.option_strings:
optionals.append(action)
else:
positionals.append(action)
parts, pos_start = self._get_actions_usage_parts(actions, groups)
# build full usage string
format = self._format_actions_usage
action_usage = format(optionals + positionals, groups)
usage = ' '.join([s for s in [prog, action_usage] if s])
usage = ' '.join(filter(None, [prog, *parts]))
# wrap the usage parts if it's too long
text_width = self._width - self._current_indent
if len(prefix) + len(usage) > text_width:
if len(prefix) + len(self._decolor(usage)) > text_width:
# break usage into wrappable parts
opt_parts = self._get_actions_usage_parts(optionals, groups)
pos_parts = self._get_actions_usage_parts(positionals, groups)
opt_parts = parts[:pos_start]
pos_parts = parts[pos_start:]
# helper for wrapping lines
def get_lines(parts, indent, prefix=None):
@@ -343,12 +359,13 @@ class HelpFormatter(object):
else:
line_len = indent_length - 1
for part in parts:
if line_len + 1 + len(part) > text_width and line:
part_len = len(self._decolor(part))
if line_len + 1 + part_len > text_width and line:
lines.append(indent + ' '.join(line))
line = []
line_len = indent_length - 1
line.append(part)
line_len += len(part) + 1
line_len += part_len + 1
if line:
lines.append(indent + ' '.join(line))
if prefix is not None:
@@ -356,8 +373,9 @@ class HelpFormatter(object):
return lines
# if prog is short, follow it with optionals or positionals
if len(prefix) + len(prog) <= 0.75 * text_width:
indent = ' ' * (len(prefix) + len(prog) + 1)
prog_len = len(self._decolor(prog))
if len(prefix) + prog_len <= 0.75 * text_width:
indent = ' ' * (len(prefix) + prog_len + 1)
if opt_parts:
lines = get_lines([prog] + opt_parts, indent, prefix)
lines.extend(get_lines(pos_parts, indent))
@@ -380,97 +398,120 @@ class HelpFormatter(object):
# join lines into usage
usage = '\n'.join(lines)
# prefix with 'usage:'
return '%s%s\n\n' % (prefix, usage)
usage = usage.removeprefix(prog)
usage = f"{t.prog}{prog}{t.reset}{usage}"
def _format_actions_usage(self, actions, groups):
return ' '.join(self._get_actions_usage_parts(actions, groups))
# prefix with 'usage:'
return f'{t.usage}{prefix}{t.reset}{usage}\n\n'
def _is_long_option(self, string):
return len(string) > 2
def _get_actions_usage_parts(self, actions, groups):
# find group indices and identify actions in groups
group_actions = set()
inserts = {}
"""Get usage parts with split index for optionals/positionals.
Returns (parts, pos_start) where pos_start is the index in parts
where positionals begin.
This preserves mutually exclusive group formatting across the
optionals/positionals boundary (gh-75949).
"""
actions = [action for action in actions if action.help is not SUPPRESS]
# group actions by mutually exclusive groups
action_groups = dict.fromkeys(actions)
for group in groups:
if not group._group_actions:
raise ValueError(f'empty group {group}')
if all(action.help is SUPPRESS for action in group._group_actions):
continue
try:
start = actions.index(group._group_actions[0])
except ValueError:
continue
else:
end = start + len(group._group_actions)
if actions[start:end] == group._group_actions:
group_actions.update(group._group_actions)
inserts[start, end] = group
for action in group._group_actions:
if action in action_groups:
action_groups[action] = group
# positional arguments keep their position
positionals = []
for action in actions:
if not action.option_strings:
group = action_groups.pop(action)
if group:
group_actions = [
action2 for action2 in group._group_actions
if action2.option_strings and
action_groups.pop(action2, None)
] + [action]
positionals.append((group.required, group_actions))
else:
positionals.append((None, [action]))
# the remaining optional arguments are sorted by the position of
# the first option in the group
optionals = []
for action in actions:
if action.option_strings and action in action_groups:
group = action_groups.pop(action)
if group:
group_actions = [action] + [
action2 for action2 in group._group_actions
if action2.option_strings and
action_groups.pop(action2, None)
]
optionals.append((group.required, group_actions))
else:
optionals.append((None, [action]))
# collect all actions format strings
parts = []
for action in actions:
t = self._theme
pos_start = None
for i, (required, group) in enumerate(optionals + positionals):
start = len(parts)
if i == len(optionals):
pos_start = start
in_group = len(group) > 1
for action in group:
# produce all arg strings
if not action.option_strings:
default = self._get_default_metavar_for_positional(action)
part = self._format_args(action, default)
# if it's in a group, strip the outer []
if in_group:
if part[0] == '[' and part[-1] == ']':
part = part[1:-1]
part = t.summary_action + part + t.reset
# suppressed arguments are marked with None
if action.help is SUPPRESS:
part = None
# produce all arg strings
elif not action.option_strings:
default = self._get_default_metavar_for_positional(action)
part = self._format_args(action, default)
# if it's in a group, strip the outer []
if action in group_actions:
if part[0] == '[' and part[-1] == ']':
part = part[1:-1]
# produce the first way to invoke the option in brackets
else:
option_string = action.option_strings[0]
# if the Optional doesn't take a value, format is:
# -s or --long
if action.nargs == 0:
part = action.format_usage()
# if the Optional takes a value, format is:
# -s ARGS or --long ARGS
# produce the first way to invoke the option in brackets
else:
default = self._get_default_metavar_for_optional(action)
args_string = self._format_args(action, default)
part = '%s %s' % (option_string, args_string)
option_string = action.option_strings[0]
if self._is_long_option(option_string):
option_color = t.summary_long_option
else:
option_color = t.summary_short_option
# make it look optional if it's not required or in a group
if not action.required and action not in group_actions:
part = '[%s]' % part
# if the Optional doesn't take a value, format is:
# -s or --long
if action.nargs == 0:
part = action.format_usage()
part = f"{option_color}{part}{t.reset}"
# add the action string to the list
parts.append(part)
# if the Optional takes a value, format is:
# -s ARGS or --long ARGS
else:
default = self._get_default_metavar_for_optional(action)
args_string = self._format_args(action, default)
part = (
f"{option_color}{option_string} "
f"{t.summary_label}{args_string}{t.reset}"
)
# group mutually exclusive actions
inserted_separators_indices = set()
for start, end in sorted(inserts, reverse=True):
group = inserts[start, end]
group_parts = [item for item in parts[start:end] if item is not None]
group_size = len(group_parts)
if group.required:
open, close = "()" if group_size > 1 else ("", "")
else:
open, close = "[]"
group_parts[0] = open + group_parts[0]
group_parts[-1] = group_parts[-1] + close
for i, part in enumerate(group_parts[:-1], start=start):
# insert a separator if not already done in a nested group
if i not in inserted_separators_indices:
parts[i] = part + ' |'
inserted_separators_indices.add(i)
parts[start + group_size - 1] = group_parts[-1]
for i in range(start + group_size, end):
parts[i] = None
# make it look optional if it's not required or in a group
if not (action.required or required or in_group):
part = '[%s]' % part
# return the usage parts
return [item for item in parts if item is not None]
# add the action string to the list
parts.append(part)
if in_group:
parts[start] = ('(' if required else '[') + parts[start]
for i in range(start, len(parts) - 1):
parts[i] += ' |'
parts[-1] += ')' if required else ']'
if pos_start is None:
pos_start = len(parts)
return parts, pos_start
def _format_text(self, text):
if '%(prog)' in text:
@@ -486,6 +527,7 @@ class HelpFormatter(object):
help_width = max(self._width - help_position, 11)
action_width = help_position - self._current_indent - 2
action_header = self._format_action_invocation(action)
action_header_no_color = self._decolor(action_header)
# no help; start on same line and add a final newline
if not action.help:
@@ -493,9 +535,15 @@ class HelpFormatter(object):
action_header = '%*s%s\n' % tup
# short action name; start on the same line and pad two spaces
elif len(action_header) <= action_width:
tup = self._current_indent, '', action_width, action_header
elif len(action_header_no_color) <= action_width:
# calculate widths without color codes
action_header_color = action_header
tup = self._current_indent, '', action_width, action_header_no_color
action_header = '%*s%-*s ' % tup
# swap in the colored header
action_header = action_header.replace(
action_header_no_color, action_header_color
)
indent_first = 0
# long action name; start on the next line
@@ -528,23 +576,42 @@ class HelpFormatter(object):
return self._join_parts(parts)
def _format_action_invocation(self, action):
t = self._theme
if not action.option_strings:
default = self._get_default_metavar_for_positional(action)
return ' '.join(self._metavar_formatter(action, default)(1))
return (
t.action
+ ' '.join(self._metavar_formatter(action, default)(1))
+ t.reset
)
else:
def color_option_strings(strings):
parts = []
for s in strings:
if self._is_long_option(s):
parts.append(f"{t.long_option}{s}{t.reset}")
else:
parts.append(f"{t.short_option}{s}{t.reset}")
return parts
# if the Optional doesn't take a value, format is:
# -s, --long
if action.nargs == 0:
return ', '.join(action.option_strings)
option_strings = color_option_strings(action.option_strings)
return ', '.join(option_strings)
# if the Optional takes a value, format is:
# -s, --long ARGS
else:
default = self._get_default_metavar_for_optional(action)
args_string = self._format_args(action, default)
return ', '.join(action.option_strings) + ' ' + args_string
option_strings = color_option_strings(action.option_strings)
args_string = (
f"{t.label}{self._format_args(action, default)}{t.reset}"
)
return ', '.join(option_strings) + ' ' + args_string
def _metavar_formatter(self, action, default_metavar):
if action.metavar is not None:
@@ -590,16 +657,19 @@ class HelpFormatter(object):
return result
def _expand_help(self, action):
help_string = self._get_help_string(action)
if '%' not in help_string:
return help_string
params = dict(vars(action), prog=self._prog)
for name in list(params):
if params[name] is SUPPRESS:
value = params[name]
if value is SUPPRESS:
del params[name]
for name in list(params):
if hasattr(params[name], '__name__'):
params[name] = params[name].__name__
elif hasattr(value, '__name__'):
params[name] = value.__name__
if params.get('choices') is not None:
params['choices'] = ', '.join(map(str, params['choices']))
return self._get_help_string(action) % params
return help_string % params
def _iter_indented_subactions(self, action):
try:
@@ -669,11 +739,14 @@ class ArgumentDefaultsHelpFormatter(HelpFormatter):
if help is None:
help = ''
if '%(default)' not in help:
if action.default is not SUPPRESS:
defaulting_nargs = [OPTIONAL, ZERO_OR_MORE]
if action.option_strings or action.nargs in defaulting_nargs:
help += _(' (default: %(default)s)')
if (
'%(default)' not in help
and action.default is not SUPPRESS
and not action.required
):
defaulting_nargs = (OPTIONAL, ZERO_OR_MORE)
if action.option_strings or action.nargs in defaulting_nargs:
help += _(' (default: %(default)s)')
return help
@@ -844,22 +917,16 @@ class Action(_AttributeHolder):
return self.option_strings[0]
def __call__(self, parser, namespace, values, option_string=None):
raise NotImplementedError(_('.__call__() not defined'))
raise NotImplementedError('.__call__() not defined')
# FIXME: remove together with `BooleanOptionalAction` deprecated arguments.
_deprecated_default = object()
class BooleanOptionalAction(Action):
def __init__(self,
option_strings,
dest,
default=None,
type=_deprecated_default,
choices=_deprecated_default,
required=False,
help=None,
metavar=_deprecated_default,
deprecated=False):
_option_strings = []
@@ -867,38 +934,19 @@ class BooleanOptionalAction(Action):
_option_strings.append(option_string)
if option_string.startswith('--'):
if option_string.startswith('--no-'):
raise ValueError(f'invalid option name {option_string!r} '
f'for BooleanOptionalAction')
option_string = '--no-' + option_string[2:]
_option_strings.append(option_string)
# We need `_deprecated` special value to ban explicit arguments that
# match default value. Like:
# parser.add_argument('-f', action=BooleanOptionalAction, type=int)
for field_name in ('type', 'choices', 'metavar'):
if locals()[field_name] is not _deprecated_default:
import warnings
warnings._deprecated(
field_name,
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.",
remove=(3, 14))
if type is _deprecated_default:
type = None
if choices is _deprecated_default:
choices = None
if metavar is _deprecated_default:
metavar = None
super().__init__(
option_strings=_option_strings,
dest=dest,
nargs=0,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar,
deprecated=deprecated)
@@ -1180,6 +1228,7 @@ class _SubParsersAction(Action):
self._name_parser_map = {}
self._choices_actions = []
self._deprecated = set()
self._color = True
super(_SubParsersAction, self).__init__(
option_strings=option_strings,
@@ -1195,23 +1244,30 @@ class _SubParsersAction(Action):
if kwargs.get('prog') is None:
kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
# set color
if kwargs.get('color') is None:
kwargs['color'] = self._color
aliases = kwargs.pop('aliases', ())
if name in self._name_parser_map:
raise ArgumentError(self, _('conflicting subparser: %s') % name)
raise ValueError(f'conflicting subparser: {name}')
for alias in aliases:
if alias in self._name_parser_map:
raise ArgumentError(
self, _('conflicting subparser alias: %s') % alias)
raise ValueError(f'conflicting subparser alias: {alias}')
# create a pseudo-action to hold the choice help
if 'help' in kwargs:
help = kwargs.pop('help')
choice_action = self._ChoicesPseudoAction(name, aliases, help)
self._choices_actions.append(choice_action)
else:
choice_action = None
# create the parser and add it to the map
parser = self._parser_class(**kwargs)
if choice_action is not None:
parser._check_help(choice_action)
self._name_parser_map[name] = parser
# make parser available under aliases also
@@ -1276,7 +1332,7 @@ class _ExtendAction(_AppendAction):
# ==============
class FileType(object):
"""Factory for creating file object types
"""Deprecated factory for creating file object types
Instances of FileType are typically passed as type= arguments to the
ArgumentParser add_argument() method.
@@ -1293,6 +1349,12 @@ class FileType(object):
"""
def __init__(self, mode='r', bufsize=-1, encoding=None, errors=None):
import warnings
warnings.warn(
"FileType is deprecated. Simply open files after parsing arguments.",
category=PendingDeprecationWarning,
stacklevel=2
)
self._mode = mode
self._bufsize = bufsize
self._encoding = encoding
@@ -1396,7 +1458,7 @@ class _ActionsContainer(object):
self._defaults = {}
# determines whether an "option" looks like a negative number
self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$')
self._negative_number_matcher = _re.compile(r'-\.?\d')
# whether or not there are any optionals that look like negative
# numbers -- uses a list so it can be shared and edited
@@ -1449,7 +1511,8 @@ class _ActionsContainer(object):
chars = self.prefix_chars
if not args or len(args) == 1 and args[0][0] not in chars:
if args and 'dest' in kwargs:
raise ValueError('dest supplied twice for positional argument')
raise TypeError('dest supplied twice for positional argument,'
' did you mean metavar?')
kwargs = self._get_positional_kwargs(*args, **kwargs)
# otherwise, we're adding an optional argument
@@ -1465,27 +1528,34 @@ class _ActionsContainer(object):
kwargs['default'] = self.argument_default
# create the action object, and add it to the parser
action_name = kwargs.get('action')
action_class = self._pop_action_class(kwargs)
if not callable(action_class):
raise ValueError('unknown action "%s"' % (action_class,))
raise ValueError(f'unknown action {action_class!r}')
action = action_class(**kwargs)
# raise an error if action for positional argument does not
# consume arguments
if not action.option_strings and action.nargs == 0:
raise ValueError(f'action {action_name!r} is not valid for positional arguments')
# raise an error if the action type is not callable
type_func = self._registry_get('type', action.type, action.type)
if not callable(type_func):
raise ValueError('%r is not callable' % (type_func,))
raise TypeError(f'{type_func!r} is not callable')
if type_func is FileType:
raise ValueError('%r is a FileType class object, instance of it'
' must be passed' % (type_func,))
raise TypeError(f'{type_func!r} is a FileType class object, '
f'instance of it must be passed')
# raise an error if the metavar does not match the type
if hasattr(self, "_get_formatter"):
if hasattr(self, "_get_validation_formatter"):
formatter = self._get_validation_formatter()
try:
self._get_formatter()._format_args(action, None)
formatter._format_args(action, None)
except TypeError:
raise ValueError("length of metavar tuple does not match nargs")
self._check_help(action)
return self._add_action(action)
def add_argument_group(self, *args, **kwargs):
@@ -1529,8 +1599,8 @@ class _ActionsContainer(object):
if group.title in title_group_map:
# This branch could happen if a derived class added
# groups with duplicated titles in __init__
msg = _('cannot merge actions - two groups are named %r')
raise ValueError(msg % (group.title))
msg = f'cannot merge actions - two groups are named {group.title!r}'
raise ValueError(msg)
title_group_map[group.title] = group
# map each action to its group
@@ -1571,13 +1641,15 @@ class _ActionsContainer(object):
def _get_positional_kwargs(self, dest, **kwargs):
# make sure required is not specified
if 'required' in kwargs:
msg = _("'required' is an invalid argument for positionals")
msg = "'required' is an invalid argument for positionals"
raise TypeError(msg)
# mark positional arguments as required if at least one is
# always required
nargs = kwargs.get('nargs')
if nargs not in [OPTIONAL, ZERO_OR_MORE, REMAINDER, SUPPRESS, 0]:
if nargs == 0:
raise ValueError('nargs for positionals must be != 0')
if nargs not in [OPTIONAL, ZERO_OR_MORE, REMAINDER, SUPPRESS]:
kwargs['required'] = True
# return the keyword arguments with no option strings
@@ -1590,11 +1662,9 @@ class _ActionsContainer(object):
for option_string in args:
# error on strings that don't start with an appropriate prefix
if not option_string[0] in self.prefix_chars:
args = {'option': option_string,
'prefix_chars': self.prefix_chars}
msg = _('invalid option string %(option)r: '
'must start with a character %(prefix_chars)r')
raise ValueError(msg % args)
raise ValueError(
f'invalid option string {option_string!r}: '
f'must start with a character {self.prefix_chars!r}')
# strings starting with two prefix characters are long options
option_strings.append(option_string)
@@ -1610,8 +1680,8 @@ class _ActionsContainer(object):
dest_option_string = option_strings[0]
dest = dest_option_string.lstrip(self.prefix_chars)
if not dest:
msg = _('dest= is required for options like %r')
raise ValueError(msg % option_string)
msg = f'dest= is required for options like {option_string!r}'
raise TypeError(msg)
dest = dest.replace('-', '_')
# return the updated keyword arguments
@@ -1627,8 +1697,8 @@ class _ActionsContainer(object):
try:
return getattr(self, handler_func_name)
except AttributeError:
msg = _('invalid conflict_resolution value: %r')
raise ValueError(msg % self.conflict_handler)
msg = f'invalid conflict_resolution value: {self.conflict_handler!r}'
raise ValueError(msg)
def _check_conflict(self, action):
@@ -1667,10 +1737,26 @@ class _ActionsContainer(object):
if not action.option_strings:
action.container._remove_action(action)
def _check_help(self, action):
if action.help and hasattr(self, "_get_validation_formatter"):
formatter = self._get_validation_formatter()
try:
formatter._expand_help(action)
except (ValueError, TypeError, KeyError) as exc:
raise ValueError('badly formed help string') from exc
class _ArgumentGroup(_ActionsContainer):
def __init__(self, container, title=None, description=None, **kwargs):
if 'prefix_chars' in kwargs:
import warnings
depr_msg = (
"The use of the undocumented 'prefix_chars' parameter in "
"ArgumentParser.add_argument_group() is deprecated."
)
warnings.warn(depr_msg, DeprecationWarning, stacklevel=3)
# add any missing keyword arguments by checking the container
update = kwargs.setdefault
update('conflict_handler', container.conflict_handler)
@@ -1702,14 +1788,7 @@ class _ArgumentGroup(_ActionsContainer):
self._group_actions.remove(action)
def add_argument_group(self, *args, **kwargs):
import warnings
warnings.warn(
"Nesting argument groups is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
return super().add_argument_group(*args, **kwargs)
raise ValueError('argument groups cannot be nested')
class _MutuallyExclusiveGroup(_ArgumentGroup):
@@ -1720,7 +1799,7 @@ class _MutuallyExclusiveGroup(_ArgumentGroup):
def _add_action(self, action):
if action.required:
msg = _('mutually exclusive arguments must be optional')
msg = 'mutually exclusive arguments must be optional'
raise ValueError(msg)
action = self._container._add_action(action)
self._group_actions.append(action)
@@ -1730,14 +1809,29 @@ class _MutuallyExclusiveGroup(_ArgumentGroup):
self._container._remove_action(action)
self._group_actions.remove(action)
def add_mutually_exclusive_group(self, *args, **kwargs):
import warnings
warnings.warn(
"Nesting mutually exclusive groups is deprecated.",
category=DeprecationWarning,
stacklevel=2
)
return super().add_mutually_exclusive_group(*args, **kwargs)
def add_mutually_exclusive_group(self, **kwargs):
raise ValueError('mutually exclusive groups cannot be nested')
def _prog_name(prog=None):
if prog is not None:
return prog
arg0 = _sys.argv[0]
try:
modspec = _sys.modules['__main__'].__spec__
except (KeyError, AttributeError):
# possibly PYTHONSTARTUP or -X presite or other weird edge case
# no good answer here, so fall back to the default
modspec = None
if modspec is None:
# simple script
return _os.path.basename(arg0)
py = _os.path.basename(_sys.executable)
if modspec.name != '__main__':
# imported module or package
modname = modspec.name.removesuffix('.__main__')
return f'{py} -m {modname}'
# directory or ZIP file
return f'{py} {arg0}'
class ArgumentParser(_AttributeHolder, _ActionsContainer):
@@ -1760,6 +1854,9 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
- allow_abbrev -- Allow long options to be abbreviated unambiguously
- exit_on_error -- Determines whether or not ArgumentParser exits with
error info when an error occurs
- suggest_on_error - Enables suggestions for mistyped argument choices
and subparser names (default: ``False``)
- color - Allow color output in help messages (default: ``False``)
"""
def __init__(self,
@@ -1775,19 +1872,18 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
conflict_handler='error',
add_help=True,
allow_abbrev=True,
exit_on_error=True):
exit_on_error=True,
*,
suggest_on_error=False,
color=True,
):
superinit = super(ArgumentParser, self).__init__
superinit(description=description,
prefix_chars=prefix_chars,
argument_default=argument_default,
conflict_handler=conflict_handler)
# default setting for prog
if prog is None:
prog = _os.path.basename(_sys.argv[0])
self.prog = prog
self.prog = _prog_name(prog)
self.usage = usage
self.epilog = epilog
self.formatter_class = formatter_class
@@ -1795,6 +1891,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
self.add_help = add_help
self.allow_abbrev = allow_abbrev
self.exit_on_error = exit_on_error
self.suggest_on_error = suggest_on_error
self.color = color
# Cached formatter for validation (avoids repeated _set_color calls)
self._cached_formatter = None
add_group = self.add_argument_group
self._positionals = add_group(_('positional arguments'))
@@ -1844,7 +1945,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def add_subparsers(self, **kwargs):
if self._subparsers is not None:
raise ArgumentError(None, _('cannot have multiple subparser arguments'))
raise ValueError('cannot have multiple subparser arguments')
# add the parser class to the arguments if it's not present
kwargs.setdefault('parser_class', type(self))
@@ -1859,15 +1960,19 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
# prog defaults to the usage message of this parser, skipping
# optional arguments and with no "usage:" prefix
if kwargs.get('prog') is None:
formatter = self._get_formatter()
# Create formatter without color to avoid storing ANSI codes in prog
formatter = self.formatter_class(prog=self.prog)
formatter._set_color(False)
positionals = self._get_positional_actions()
groups = self._mutually_exclusive_groups
formatter.add_usage(self.usage, positionals, groups, '')
formatter.add_usage(None, positionals, groups, '')
kwargs['prog'] = formatter.format_help().strip()
# create the parsers action and add it to the positionals list
parsers_class = self._pop_action_class(kwargs, 'parsers')
action = parsers_class(option_strings=[], **kwargs)
action._color = self.color
self._check_help(action)
self._subparsers._add_action(action)
# return the created parsers action
@@ -2498,7 +2603,6 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
value = action.default
if isinstance(value, str) and value is not SUPPRESS:
value = self._get_value(action, value)
self._check_value(action, value)
# when nargs='*' on a positional, if there were no command-line
# args, use the default if it is anything other than None
@@ -2506,11 +2610,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
not action.option_strings):
if action.default is not None:
value = action.default
self._check_value(action, value)
else:
# since arg_strings is always [] at this point
# there is no need to use self._check_value(action, value)
value = arg_strings
value = []
# single argument or optional argument produces a single value
elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
@@ -2543,8 +2644,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def _get_value(self, action, arg_string):
type_func = self._registry_get('type', action.type, action.type)
if not callable(type_func):
msg = _('%r is not callable')
raise ArgumentError(action, msg % type_func)
raise TypeError(f'{type_func!r} is not callable')
# convert the value to the appropriate type
try:
@@ -2568,14 +2668,27 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
def _check_value(self, action, value):
# converted value must be one of the choices (if specified)
choices = action.choices
if choices is not None:
if isinstance(choices, str):
choices = iter(choices)
if value not in choices:
args = {'value': str(value),
'choices': ', '.join(map(str, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
raise ArgumentError(action, msg % args)
if choices is None:
return
if isinstance(choices, str):
choices = iter(choices)
if value not in choices:
args = {'value': str(value),
'choices': ', '.join(map(str, action.choices))}
msg = _('invalid choice: %(value)r (choose from %(choices)s)')
if self.suggest_on_error and isinstance(value, str):
if all(isinstance(choice, str) for choice in action.choices):
import difflib
suggestions = difflib.get_close_matches(value, action.choices, 1)
if suggestions:
args['closest'] = suggestions[0]
msg = _('invalid choice: %(value)r, maybe you meant %(closest)r? '
'(choose from %(choices)s)')
raise ArgumentError(action, msg % args)
# =======================
# Help-formatting methods
@@ -2611,7 +2724,16 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
return formatter.format_help()
def _get_formatter(self):
return self.formatter_class(prog=self.prog)
formatter = self.formatter_class(prog=self.prog)
formatter._set_color(self.color)
return formatter
def _get_validation_formatter(self):
# Return cached formatter for read-only validation operations
# (_expand_help and _format_args). Avoids repeated slow _set_color calls.
if self._cached_formatter is None:
self._cached_formatter = self._get_formatter()
return self._cached_formatter
# =====================
# Help-printing methods

1433
Lib/ast.py vendored

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@ from .coroutines import *
from .events import *
from .exceptions import *
from .futures import *
from .graph import *
from .locks import *
from .protocols import *
from .runners import *
@@ -27,6 +28,7 @@ __all__ = (base_events.__all__ +
events.__all__ +
exceptions.__all__ +
futures.__all__ +
graph.__all__ +
locks.__all__ +
protocols.__all__ +
runners.__all__ +
@@ -45,3 +47,28 @@ if sys.platform == 'win32': # pragma: no cover
else:
from .unix_events import * # pragma: no cover
__all__ += unix_events.__all__
def __getattr__(name: str):
import warnings
match name:
case "AbstractEventLoopPolicy":
warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
return events._AbstractEventLoopPolicy
case "DefaultEventLoopPolicy":
warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
if sys.platform == 'win32':
return windows_events._DefaultEventLoopPolicy
return unix_events._DefaultEventLoopPolicy
case "WindowsSelectorEventLoopPolicy":
if sys.platform == 'win32':
warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
return windows_events._WindowsSelectorEventLoopPolicy
# Else fall through to the AttributeError below.
case "WindowsProactorEventLoopPolicy":
if sys.platform == 'win32':
warnings._deprecated(f"asyncio.{name}", remove=(3, 16))
return windows_events._WindowsProactorEventLoopPolicy
# Else fall through to the AttributeError below.
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -1,41 +1,53 @@
import argparse
import ast
import asyncio
import code
import asyncio.tools
import concurrent.futures
import contextvars
import inspect
import os
import site
import sys
import threading
import types
import warnings
from _colorize import get_theme
from _pyrepl.console import InteractiveColoredConsole
from . import futures
class AsyncIOInteractiveConsole(code.InteractiveConsole):
class AsyncIOInteractiveConsole(InteractiveColoredConsole):
def __init__(self, locals, loop):
super().__init__(locals)
super().__init__(locals, filename="<stdin>")
self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
self.loop = loop
self.context = contextvars.copy_context()
def runcode(self, code):
global return_code
future = concurrent.futures.Future()
def callback():
global return_code
global repl_future
global repl_future_interrupted
global keyboard_interrupted
repl_future = None
repl_future_interrupted = False
keyboard_interrupted = False
func = types.FunctionType(code, self.locals)
try:
coro = func()
except SystemExit:
raise
except SystemExit as se:
return_code = se.code
self.loop.stop()
return
except KeyboardInterrupt as ex:
repl_future_interrupted = True
keyboard_interrupted = True
future.set_exception(ex)
return
except BaseException as ex:
@@ -47,39 +59,72 @@ class AsyncIOInteractiveConsole(code.InteractiveConsole):
return
try:
repl_future = self.loop.create_task(coro)
repl_future = self.loop.create_task(coro, context=self.context)
futures._chain_future(repl_future, future)
except BaseException as exc:
future.set_exception(exc)
loop.call_soon_threadsafe(callback)
self.loop.call_soon_threadsafe(callback, context=self.context)
try:
return future.result()
except SystemExit:
raise
except SystemExit as se:
return_code = se.code
self.loop.stop()
return
except BaseException:
if repl_future_interrupted:
self.write("\nKeyboardInterrupt\n")
if keyboard_interrupted:
if not CAN_USE_PYREPL:
self.write("\nKeyboardInterrupt\n")
else:
self.showtraceback()
return self.STATEMENT_FAILED
class REPLThread(threading.Thread):
def run(self):
global return_code
try:
banner = (
f'asyncio REPL {sys.version} on {sys.platform}\n'
f'Use "await" directly instead of "asyncio.run()".\n'
f'Type "help", "copyright", "credits" or "license" '
f'for more information.\n'
f'{getattr(sys, "ps1", ">>> ")}import asyncio'
)
console.interact(
banner=banner,
exitmsg='exiting asyncio REPL...')
console.write(banner)
if startup_path := os.getenv("PYTHONSTARTUP"):
sys.audit("cpython.run_startup", startup_path)
import tokenize
with tokenize.open(startup_path) as f:
startup_code = compile(f.read(), startup_path, "exec")
exec(startup_code, console.locals)
ps1 = getattr(sys, "ps1", ">>> ")
if CAN_USE_PYREPL:
theme = get_theme().syntax
ps1 = f"{theme.prompt}{ps1}{theme.reset}"
console.write(f"{ps1}import asyncio\n")
if CAN_USE_PYREPL:
from _pyrepl.simple_interact import (
run_multiline_interactive_console,
)
try:
run_multiline_interactive_console(console)
except SystemExit:
# expected via the `exit` and `quit` commands
pass
except BaseException:
# unexpected issue
console.showtraceback()
console.write("Internal error, ")
return_code = 1
else:
console.interact(banner="", exitmsg="")
finally:
warnings.filterwarnings(
'ignore',
@@ -88,8 +133,56 @@ class REPLThread(threading.Thread):
loop.call_soon_threadsafe(loop.stop)
def interrupt(self) -> None:
if not CAN_USE_PYREPL:
return
from _pyrepl.simple_interact import _get_reader
r = _get_reader()
if r.threading_hook is not None:
r.threading_hook.add("") # type: ignore
if __name__ == '__main__':
parser = argparse.ArgumentParser(
prog="python3 -m asyncio",
description="Interactive asyncio shell and CLI tools",
color=True,
)
subparsers = parser.add_subparsers(help="sub-commands", dest="command")
ps = subparsers.add_parser(
"ps", help="Display a table of all pending tasks in a process"
)
ps.add_argument("pid", type=int, help="Process ID to inspect")
pstree = subparsers.add_parser(
"pstree", help="Display a tree of all pending tasks in a process"
)
pstree.add_argument("pid", type=int, help="Process ID to inspect")
args = parser.parse_args()
match args.command:
case "ps":
asyncio.tools.display_awaited_by_tasks_table(args.pid)
sys.exit(0)
case "pstree":
asyncio.tools.display_awaited_by_tasks_tree(args.pid)
sys.exit(0)
case None:
pass # continue to the interactive shell
case _:
# shouldn't happen as an invalid command-line wouldn't parse
# but let's keep it for the next person adding a command
print(f"error: unhandled command {args.command}", file=sys.stderr)
parser.print_usage(file=sys.stderr)
sys.exit(1)
sys.audit("cpython.run_stdin")
if os.getenv('PYTHON_BASIC_REPL'):
CAN_USE_PYREPL = False
else:
from _pyrepl.main import CAN_USE_PYREPL
return_code = 0
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
@@ -102,14 +195,31 @@ if __name__ == '__main__':
console = AsyncIOInteractiveConsole(repl_locals, loop)
repl_future = None
repl_future_interrupted = False
keyboard_interrupted = False
try:
import readline # NoQA
except ImportError:
pass
readline = None
repl_thread = REPLThread()
interactive_hook = getattr(sys, "__interactivehook__", None)
if interactive_hook is not None:
sys.audit("cpython.run_interactivehook", interactive_hook)
interactive_hook()
if interactive_hook is site.register_readline:
# Fix the completer function to use the interactive console locals
try:
import rlcompleter
except:
pass
else:
if readline is not None:
completer = rlcompleter.Completer(console.locals)
readline.set_completer(completer.complete)
repl_thread = REPLThread(name="Interactive thread")
repl_thread.daemon = True
repl_thread.start()
@@ -117,9 +227,13 @@ if __name__ == '__main__':
try:
loop.run_forever()
except KeyboardInterrupt:
keyboard_interrupted = True
if repl_future and not repl_future.done():
repl_future.cancel()
repl_future_interrupted = True
repl_thread.interrupt()
continue
else:
break
console.write('exiting asyncio REPL...\n')
sys.exit(return_code)

View File

@@ -17,7 +17,6 @@ import collections
import collections.abc
import concurrent.futures
import errno
import functools
import heapq
import itertools
import os
@@ -279,7 +278,9 @@ class Server(events.AbstractServer):
ssl_handshake_timeout, ssl_shutdown_timeout=None):
self._loop = loop
self._sockets = sockets
self._active_count = 0
# Weak references so we don't break Transport's ability to
# detect abandoned transports
self._clients = weakref.WeakSet()
self._waiters = []
self._protocol_factory = protocol_factory
self._backlog = backlog
@@ -292,14 +293,13 @@ class Server(events.AbstractServer):
def __repr__(self):
return f'<{self.__class__.__name__} sockets={self.sockets!r}>'
def _attach(self):
def _attach(self, transport):
assert self._sockets is not None
self._active_count += 1
self._clients.add(transport)
def _detach(self):
assert self._active_count > 0
self._active_count -= 1
if self._active_count == 0 and self._sockets is None:
def _detach(self, transport):
self._clients.discard(transport)
if len(self._clients) == 0 and self._sockets is None:
self._wakeup()
def _wakeup(self):
@@ -348,9 +348,17 @@ class Server(events.AbstractServer):
self._serving_forever_fut.cancel()
self._serving_forever_fut = None
if self._active_count == 0:
if len(self._clients) == 0:
self._wakeup()
def close_clients(self):
for transport in self._clients.copy():
transport.close()
def abort_clients(self):
for transport in self._clients.copy():
transport.abort()
async def start_serving(self):
self._start_serving()
# Skip one loop iteration so that all 'loop.add_reader'
@@ -422,6 +430,8 @@ class BaseEventLoop(events.AbstractEventLoop):
self._clock_resolution = time.get_clock_info('monotonic').resolution
self._exception_handler = None
self.set_debug(coroutines._is_debug_mode())
# The preserved state of async generator hooks.
self._old_agen_hooks = None
# In debug mode, if the execution of a callback or a step of a task
# exceed this duration in seconds, the slow callback/task is logged.
self.slow_callback_duration = 0.1
@@ -448,26 +458,24 @@ class BaseEventLoop(events.AbstractEventLoop):
"""Create a Future object attached to the loop."""
return futures.Future(loop=self)
def create_task(self, coro, *, name=None, context=None):
"""Schedule a coroutine object.
def create_task(self, coro, **kwargs):
"""Schedule or begin executing a coroutine object.
Return a task object.
"""
self._check_closed()
if self._task_factory is None:
task = tasks.Task(coro, loop=self, name=name, context=context)
if task._source_traceback:
del task._source_traceback[-1]
else:
if context is None:
# Use legacy API if context is not needed
task = self._task_factory(self, coro)
else:
task = self._task_factory(self, coro, context=context)
if self._task_factory is not None:
return self._task_factory(self, coro, **kwargs)
tasks._set_task_name(task, name)
return task
task = tasks.Task(coro, loop=self, **kwargs)
if task._source_traceback:
del task._source_traceback[-1]
try:
return task
finally:
# gh-128552: prevent a refcycle of
# task.exception().__traceback__->BaseEventLoop.create_task->task
del task
def set_task_factory(self, factory):
"""Set a task factory that will be used by loop.create_task().
@@ -475,9 +483,10 @@ class BaseEventLoop(events.AbstractEventLoop):
If factory is None the default task factory will be set.
If factory is a callable, it should have a signature matching
'(loop, coro)', where 'loop' will be a reference to the active
event loop, 'coro' will be a coroutine object. The callable
must return a Future.
'(loop, coro, **kwargs)', where 'loop' will be a reference to the active
event loop, 'coro' will be a coroutine object, and **kwargs will be
arbitrary keyword arguments that should be passed on to Task.
The callable must return a Task.
"""
if factory is not None and not callable(factory):
raise TypeError('task factory must be a callable or None')
@@ -624,29 +633,52 @@ class BaseEventLoop(events.AbstractEventLoop):
raise RuntimeError(
'Cannot run the event loop while another loop is running')
def run_forever(self):
"""Run until stop() is called."""
def _run_forever_setup(self):
"""Prepare the run loop to process events.
This method exists so that custom event loop subclasses (e.g., event loops
that integrate a GUI event loop with Python's event loop) have access to all the
loop setup logic.
"""
self._check_closed()
self._check_running()
self._set_coroutine_origin_tracking(self._debug)
old_agen_hooks = sys.get_asyncgen_hooks()
try:
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook)
self._old_agen_hooks = sys.get_asyncgen_hooks()
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(
firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook
)
events._set_running_loop(self)
events._set_running_loop(self)
def _run_forever_cleanup(self):
"""Clean up after an event loop finishes the looping over events.
This method exists so that custom event loop subclasses (e.g., event loops
that integrate a GUI event loop with Python's event loop) have access to all the
loop cleanup logic.
"""
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
# Restore any pre-existing async generator hooks.
if self._old_agen_hooks is not None:
sys.set_asyncgen_hooks(*self._old_agen_hooks)
self._old_agen_hooks = None
def run_forever(self):
"""Run until stop() is called."""
self._run_forever_setup()
try:
while True:
self._run_once()
if self._stopping:
break
finally:
self._stopping = False
self._thread_id = None
events._set_running_loop(None)
self._set_coroutine_origin_tracking(False)
sys.set_asyncgen_hooks(*old_agen_hooks)
self._run_forever_cleanup()
def run_until_complete(self, future):
"""Run until the Future is done.
@@ -803,7 +835,7 @@ class BaseEventLoop(events.AbstractEventLoop):
def _check_callback(self, callback, method):
if (coroutines.iscoroutine(callback) or
coroutines.iscoroutinefunction(callback)):
coroutines._iscoroutinefunction(callback)):
raise TypeError(
f"coroutines cannot be used with {method}()")
if not callable(callback):
@@ -840,7 +872,10 @@ class BaseEventLoop(events.AbstractEventLoop):
self._check_closed()
if self._debug:
self._check_callback(callback, 'call_soon_threadsafe')
handle = self._call_soon(callback, args, context)
handle = events._ThreadSafeHandle(callback, args, self, context)
self._ready.append(handle)
if handle._source_traceback:
del handle._source_traceback[-1]
if handle._source_traceback:
del handle._source_traceback[-1]
self._write_to_self()
@@ -981,39 +1016,43 @@ class BaseEventLoop(events.AbstractEventLoop):
family, type_, proto, _, address = addr_info
sock = None
try:
sock = socket.socket(family=family, type=type_, proto=proto)
sock.setblocking(False)
if local_addr_infos is not None:
for lfamily, _, _, _, laddr in local_addr_infos:
# skip local addresses of different family
if lfamily != family:
continue
try:
sock.bind(laddr)
break
except OSError as exc:
msg = (
f'error while attempting to bind on '
f'address {laddr!r}: '
f'{exc.strerror.lower()}'
)
exc = OSError(exc.errno, msg)
my_exceptions.append(exc)
else: # all bind attempts failed
if my_exceptions:
raise my_exceptions.pop()
else:
raise OSError(f"no matching local address with {family=} found")
await self.sock_connect(sock, address)
return sock
except OSError as exc:
my_exceptions.append(exc)
if sock is not None:
sock.close()
raise
try:
sock = socket.socket(family=family, type=type_, proto=proto)
sock.setblocking(False)
if local_addr_infos is not None:
for lfamily, _, _, _, laddr in local_addr_infos:
# skip local addresses of different family
if lfamily != family:
continue
try:
sock.bind(laddr)
break
except OSError as exc:
msg = (
f'error while attempting to bind on '
f'address {laddr!r}: {str(exc).lower()}'
)
exc = OSError(exc.errno, msg)
my_exceptions.append(exc)
else: # all bind attempts failed
if my_exceptions:
raise my_exceptions.pop()
else:
raise OSError(f"no matching local address with {family=} found")
await self.sock_connect(sock, address)
return sock
except OSError as exc:
my_exceptions.append(exc)
raise
except:
if sock is not None:
sock.close()
try:
sock.close()
except OSError:
# An error when closing a newly created socket is
# not important, but it can overwrite more important
# non-OSError error. So ignore it.
pass
raise
finally:
exceptions = my_exceptions = None
@@ -1107,11 +1146,18 @@ class BaseEventLoop(events.AbstractEventLoop):
except OSError:
continue
else: # using happy eyeballs
sock, _, _ = await staggered.staggered_race(
(functools.partial(self._connect_sock,
exceptions, addrinfo, laddr_infos)
for addrinfo in infos),
happy_eyeballs_delay, loop=self)
sock = (await staggered.staggered_race(
(
# can't use functools.partial as it keeps a reference
# to exceptions
lambda addrinfo=addrinfo: self._connect_sock(
exceptions, addrinfo, laddr_infos
)
for addrinfo in infos
),
happy_eyeballs_delay,
loop=self,
))[0] # can't use sock, _, _ as it keeks a reference to exceptions
if sock is None:
exceptions = [exc for sub in exceptions for exc in sub]
@@ -1120,7 +1166,7 @@ class BaseEventLoop(events.AbstractEventLoop):
raise ExceptionGroup("create_connection failed", exceptions)
if len(exceptions) == 1:
raise exceptions[0]
else:
elif exceptions:
# If they all have the same str(), raise one.
model = str(exceptions[0])
if all(str(exc) == model for exc in exceptions):
@@ -1129,6 +1175,9 @@ class BaseEventLoop(events.AbstractEventLoop):
# the various error messages.
raise OSError('Multiple exceptions: {}'.format(
', '.join(str(exc) for exc in exceptions)))
else:
# No exceptions were collected, raise a timeout error
raise TimeoutError('create_connection failed')
finally:
exceptions = None
@@ -1254,8 +1303,8 @@ class BaseEventLoop(events.AbstractEventLoop):
read = await self.run_in_executor(None, file.readinto, view)
if not read:
return total_sent # EOF
await proto.drain()
transp.write(view[:read])
await proto.drain()
total_sent += read
finally:
if total_sent > 0 and hasattr(file, 'seek'):
@@ -1474,6 +1523,7 @@ class BaseEventLoop(events.AbstractEventLoop):
ssl=None,
reuse_address=None,
reuse_port=None,
keep_alive=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
start_serving=True):
@@ -1545,8 +1595,13 @@ class BaseEventLoop(events.AbstractEventLoop):
if reuse_address:
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
if reuse_port:
# Since Linux 6.12.9, SO_REUSEPORT is not allowed
# on other address families than AF_INET/AF_INET6.
if reuse_port and af in (socket.AF_INET, socket.AF_INET6):
_set_reuseport(sock)
if keep_alive:
sock.setsockopt(
socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
# Disable IPv4/IPv6 dual stack support (enabled by
# default on Linux) which makes a single socket
# listen on both address families.
@@ -1561,7 +1616,7 @@ class BaseEventLoop(events.AbstractEventLoop):
except OSError as err:
msg = ('error while attempting '
'to bind on address %r: %s'
% (sa, err.strerror.lower()))
% (sa, str(err).lower()))
if err.errno == errno.EADDRNOTAVAIL:
# Assume the family is not enabled (bpo-30945)
sockets.pop()
@@ -1619,8 +1674,7 @@ class BaseEventLoop(events.AbstractEventLoop):
raise ValueError(
'ssl_shutdown_timeout is only meaningful with ssl')
if sock is not None:
_check_ssl_socket(sock)
_check_ssl_socket(sock)
transport, protocol = await self._create_connection_transport(
sock, protocol_factory, ssl, '', server_side=True,
@@ -1833,6 +1887,8 @@ class BaseEventLoop(events.AbstractEventLoop):
- 'protocol' (optional): Protocol instance;
- 'transport' (optional): Transport instance;
- 'socket' (optional): Socket instance;
- 'source_traceback' (optional): Traceback of the source;
- 'handle_traceback' (optional): Traceback of the handle;
- 'asyncgen' (optional): Asynchronous generator that caused
the exception.
@@ -1943,8 +1999,11 @@ class BaseEventLoop(events.AbstractEventLoop):
timeout = 0
elif self._scheduled:
# Compute the desired timeout.
when = self._scheduled[0]._when
timeout = min(max(0, when - self.time()), MAXIMUM_SELECT_TIMEOUT)
timeout = self._scheduled[0]._when - self.time()
if timeout > MAXIMUM_SELECT_TIMEOUT:
timeout = MAXIMUM_SELECT_TIMEOUT
elif timeout < 0:
timeout = 0
event_list = self._selector.select(timeout)
self._process_events(event_list)

View File

@@ -1,6 +1,9 @@
import collections
import subprocess
import warnings
import os
import signal
import sys
from . import protocols
from . import transports
@@ -23,6 +26,7 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
self._pending_calls = collections.deque()
self._pipes = {}
self._finished = False
self._pipes_connected = False
if stdin == subprocess.PIPE:
self._pipes[0] = None
@@ -101,7 +105,12 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
for proto in self._pipes.values():
if proto is None:
continue
proto.pipe.close()
# See gh-114177
# skip closing the pipe if loop is already closed
# this can happen e.g. when loop is closed immediately after
# process is killed
if self._loop and not self._loop.is_closed():
proto.pipe.close()
if (self._proc is not None and
# has the child process finished?
@@ -115,7 +124,8 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
try:
self._proc.kill()
except ProcessLookupError:
except (ProcessLookupError, PermissionError):
# the process may have already exited or may be running setuid
pass
# Don't clear the _proc reference yet: _post_init() may still run
@@ -141,17 +151,31 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
if self._proc is None:
raise ProcessLookupError()
def send_signal(self, signal):
self._check_proc()
self._proc.send_signal(signal)
if sys.platform == 'win32':
def send_signal(self, signal):
self._check_proc()
self._proc.send_signal(signal)
def terminate(self):
self._check_proc()
self._proc.terminate()
def terminate(self):
self._check_proc()
self._proc.terminate()
def kill(self):
self._check_proc()
self._proc.kill()
def kill(self):
self._check_proc()
self._proc.kill()
else:
def send_signal(self, signal):
self._check_proc()
try:
os.kill(self._proc.pid, signal)
except ProcessLookupError:
pass
def terminate(self):
self.send_signal(signal.SIGTERM)
def kill(self):
self.send_signal(signal.SIGKILL)
async def _connect_pipes(self, waiter):
try:
@@ -190,6 +214,7 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
else:
if waiter is not None and not waiter.cancelled():
waiter.set_result(None)
self._pipes_connected = True
def _call(self, cb, *data):
if self._pending_calls is not None:
@@ -233,6 +258,15 @@ class BaseSubprocessTransport(transports.SubprocessTransport):
assert not self._finished
if self._returncode is None:
return
if not self._pipes_connected:
# self._pipes_connected can be False if not all pipes were connected
# because either the process failed to start or the self._connect_pipes task
# got cancelled. In this broken state we consider all pipes disconnected and
# to avoid hanging forever in self._wait as otherwise _exit_waiters
# would never be woken up, we wake them up here.
for waiter in self._exit_waiters:
if not waiter.cancelled():
waiter.set_result(self._returncode)
if all(p is not None and p.disconnected
for p in self._pipes.values()):
self._finished = True

View File

@@ -18,7 +18,16 @@ _is_coroutine = object()
def iscoroutinefunction(func):
import warnings
"""Return True if func is a decorated coroutine function."""
warnings._deprecated("asyncio.iscoroutinefunction",
f"{warnings._DEPRECATED_MSG}; "
"use inspect.iscoroutinefunction() instead",
remove=(3,16))
return _iscoroutinefunction(func)
def _iscoroutinefunction(func):
return (inspect.iscoroutinefunction(func) or
getattr(func, '_is_coroutine', None) is _is_coroutine)

146
Lib/asyncio/events.py vendored
View File

@@ -5,14 +5,18 @@
# SPDX-FileCopyrightText: Copyright (c) 2015-2021 MagicStack Inc. http://magic.io
__all__ = (
'AbstractEventLoopPolicy',
'AbstractEventLoop', 'AbstractServer',
'Handle', 'TimerHandle',
'get_event_loop_policy', 'set_event_loop_policy',
'get_event_loop', 'set_event_loop', 'new_event_loop',
'get_child_watcher', 'set_child_watcher',
'_set_running_loop', 'get_running_loop',
'_get_running_loop',
"AbstractEventLoop",
"AbstractServer",
"Handle",
"TimerHandle",
"get_event_loop_policy",
"set_event_loop_policy",
"get_event_loop",
"set_event_loop",
"new_event_loop",
"_set_running_loop",
"get_running_loop",
"_get_running_loop",
)
import contextvars
@@ -22,6 +26,7 @@ import socket
import subprocess
import sys
import threading
import warnings
from . import format_helpers
@@ -54,7 +59,8 @@ class Handle:
info.append('cancelled')
if self._callback is not None:
info.append(format_helpers._format_callback_source(
self._callback, self._args))
self._callback, self._args,
debug=self._loop.get_debug()))
if self._source_traceback:
frame = self._source_traceback[-1]
info.append(f'created at {frame[0]}:{frame[1]}')
@@ -90,7 +96,8 @@ class Handle:
raise
except BaseException as exc:
cb = format_helpers._format_callback_source(
self._callback, self._args)
self._callback, self._args,
debug=self._loop.get_debug())
msg = f'Exception in callback {cb}'
context = {
'message': msg,
@@ -102,6 +109,34 @@ class Handle:
self._loop.call_exception_handler(context)
self = None # Needed to break cycles when an exception occurs.
# _ThreadSafeHandle is used for callbacks scheduled with call_soon_threadsafe
# and is thread safe unlike Handle which is not thread safe.
class _ThreadSafeHandle(Handle):
__slots__ = ('_lock',)
def __init__(self, callback, args, loop, context=None):
super().__init__(callback, args, loop, context)
self._lock = threading.RLock()
def cancel(self):
with self._lock:
return super().cancel()
def cancelled(self):
with self._lock:
return super().cancelled()
def _run(self):
# The event loop checks for cancellation without holding the lock
# It is possible that the handle is cancelled after the check
# but before the callback is called so check it again after acquiring
# the lock and return without calling the callback if it is cancelled.
with self._lock:
if self._cancelled:
return
return super()._run()
class TimerHandle(Handle):
"""Object returned by timed callback registration methods."""
@@ -173,6 +208,14 @@ class AbstractServer:
"""Stop serving. This leaves existing connections open."""
raise NotImplementedError
def close_clients(self):
"""Close all active connections."""
raise NotImplementedError
def abort_clients(self):
"""Close all active connections immediately."""
raise NotImplementedError
def get_loop(self):
"""Get the event loop the Server object is attached to."""
raise NotImplementedError
@@ -282,7 +325,7 @@ class AbstractEventLoop:
# Method scheduling a coroutine object: create a task.
def create_task(self, coro, *, name=None, context=None):
def create_task(self, coro, **kwargs):
raise NotImplementedError
# Methods for interacting with threads.
@@ -320,6 +363,7 @@ class AbstractEventLoop:
*, family=socket.AF_UNSPEC,
flags=socket.AI_PASSIVE, sock=None, backlog=100,
ssl=None, reuse_address=None, reuse_port=None,
keep_alive=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
start_serving=True):
@@ -358,6 +402,9 @@ class AbstractEventLoop:
they all set this flag when being created. This option is not
supported on Windows.
keep_alive set to True keeps connections active by enabling the
periodic transmission of messages.
ssl_handshake_timeout is the time in seconds that an SSL server
will wait for completion of the SSL handshake before aborting the
connection. Default is 60s.
@@ -615,7 +662,7 @@ class AbstractEventLoop:
raise NotImplementedError
class AbstractEventLoopPolicy:
class _AbstractEventLoopPolicy:
"""Abstract policy for accessing the event loop."""
def get_event_loop(self):
@@ -638,18 +685,7 @@ class AbstractEventLoopPolicy:
the current context, set_event_loop must be called explicitly."""
raise NotImplementedError
# Child processes handling (Unix only).
def get_child_watcher(self):
"Get the watcher for child processes."
raise NotImplementedError
def set_child_watcher(self, watcher):
"""Set the watcher for child processes."""
raise NotImplementedError
class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
class _BaseDefaultEventLoopPolicy(_AbstractEventLoopPolicy):
"""Default policy implementation for accessing the event loop.
In this policy, each thread has its own event loop. However, we
@@ -666,7 +702,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
class _Local(threading.local):
_loop = None
_set_called = False
def __init__(self):
self._local = self._Local()
@@ -676,28 +711,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
Returns an instance of EventLoop or raises an exception.
"""
if (self._local._loop is None and
not self._local._set_called and
threading.current_thread() is threading.main_thread()):
stacklevel = 2
try:
f = sys._getframe(1)
except AttributeError:
pass
else:
# Move up the call stack so that the warning is attached
# to the line outside asyncio itself.
while f:
module = f.f_globals.get('__name__')
if not (module == 'asyncio' or module.startswith('asyncio.')):
break
f = f.f_back
stacklevel += 1
import warnings
warnings.warn('There is no current event loop',
DeprecationWarning, stacklevel=stacklevel)
self.set_event_loop(self.new_event_loop())
if self._local._loop is None:
raise RuntimeError('There is no current event loop in thread %r.'
% threading.current_thread().name)
@@ -706,7 +719,6 @@ class BaseDefaultEventLoopPolicy(AbstractEventLoopPolicy):
def set_event_loop(self, loop):
"""Set the event loop."""
self._local._set_called = True
if loop is not None and not isinstance(loop, AbstractEventLoop):
raise TypeError(f"loop must be an instance of AbstractEventLoop or None, not '{type(loop).__name__}'")
self._local._loop = loop
@@ -776,26 +788,35 @@ def _init_event_loop_policy():
global _event_loop_policy
with _lock:
if _event_loop_policy is None: # pragma: no branch
from . import DefaultEventLoopPolicy
_event_loop_policy = DefaultEventLoopPolicy()
if sys.platform == 'win32':
from .windows_events import _DefaultEventLoopPolicy
else:
from .unix_events import _DefaultEventLoopPolicy
_event_loop_policy = _DefaultEventLoopPolicy()
def get_event_loop_policy():
def _get_event_loop_policy():
"""Get the current event loop policy."""
if _event_loop_policy is None:
_init_event_loop_policy()
return _event_loop_policy
def get_event_loop_policy():
warnings._deprecated('asyncio.get_event_loop_policy', remove=(3, 16))
return _get_event_loop_policy()
def set_event_loop_policy(policy):
def _set_event_loop_policy(policy):
"""Set the current event loop policy.
If policy is None, the default policy is restored."""
global _event_loop_policy
if policy is not None and not isinstance(policy, AbstractEventLoopPolicy):
if policy is not None and not isinstance(policy, _AbstractEventLoopPolicy):
raise TypeError(f"policy must be an instance of AbstractEventLoopPolicy or None, not '{type(policy).__name__}'")
_event_loop_policy = policy
def set_event_loop_policy(policy):
warnings._deprecated('asyncio.set_event_loop_policy', remove=(3,16))
_set_event_loop_policy(policy)
def get_event_loop():
"""Return an asyncio event loop.
@@ -810,28 +831,17 @@ def get_event_loop():
current_loop = _get_running_loop()
if current_loop is not None:
return current_loop
return get_event_loop_policy().get_event_loop()
return _get_event_loop_policy().get_event_loop()
def set_event_loop(loop):
"""Equivalent to calling get_event_loop_policy().set_event_loop(loop)."""
get_event_loop_policy().set_event_loop(loop)
_get_event_loop_policy().set_event_loop(loop)
def new_event_loop():
"""Equivalent to calling get_event_loop_policy().new_event_loop()."""
return get_event_loop_policy().new_event_loop()
def get_child_watcher():
"""Equivalent to calling get_event_loop_policy().get_child_watcher()."""
return get_event_loop_policy().get_child_watcher()
def set_child_watcher(watcher):
"""Equivalent to calling
get_event_loop_policy().set_child_watcher(watcher)."""
return get_event_loop_policy().set_child_watcher(watcher)
return _get_event_loop_policy().new_event_loop()
# Alias pure-Python implementations for testing purposes.
@@ -861,7 +871,7 @@ if hasattr(os, 'fork'):
def on_fork():
# Reset the loop and wakeupfd in the forked child process.
if _event_loop_policy is not None:
_event_loop_policy._local = BaseDefaultEventLoopPolicy._Local()
_event_loop_policy._local = _BaseDefaultEventLoopPolicy._Local()
_set_running_loop(None)
signal.set_wakeup_fd(-1)

View File

@@ -19,19 +19,26 @@ def _get_function_source(func):
return None
def _format_callback_source(func, args):
func_repr = _format_callback(func, args, None)
def _format_callback_source(func, args, *, debug=False):
func_repr = _format_callback(func, args, None, debug=debug)
source = _get_function_source(func)
if source:
func_repr += f' at {source[0]}:{source[1]}'
return func_repr
def _format_args_and_kwargs(args, kwargs):
def _format_args_and_kwargs(args, kwargs, *, debug=False):
"""Format function arguments and keyword arguments.
Special case for a single parameter: ('hello',) is formatted as ('hello').
Note that this function only returns argument details when
debug=True is specified, as arguments may contain sensitive
information.
"""
if not debug:
return '()'
# use reprlib to limit the length of the output
items = []
if args:
@@ -41,10 +48,11 @@ def _format_args_and_kwargs(args, kwargs):
return '({})'.format(', '.join(items))
def _format_callback(func, args, kwargs, suffix=''):
def _format_callback(func, args, kwargs, *, debug=False, suffix=''):
if isinstance(func, functools.partial):
suffix = _format_args_and_kwargs(args, kwargs) + suffix
return _format_callback(func.func, func.args, func.keywords, suffix)
suffix = _format_args_and_kwargs(args, kwargs, debug=debug) + suffix
return _format_callback(func.func, func.args, func.keywords,
debug=debug, suffix=suffix)
if hasattr(func, '__qualname__') and func.__qualname__:
func_repr = func.__qualname__
@@ -53,7 +61,7 @@ def _format_callback(func, args, kwargs, suffix=''):
else:
func_repr = repr(func)
func_repr += _format_args_and_kwargs(args, kwargs)
func_repr += _format_args_and_kwargs(args, kwargs, debug=debug)
if suffix:
func_repr += suffix
return func_repr

View File

@@ -2,6 +2,7 @@
__all__ = (
'Future', 'wrap_future', 'isfuture',
'future_add_to_awaited_by', 'future_discard_from_awaited_by',
)
import concurrent.futures
@@ -43,7 +44,6 @@ class Future:
- This class is not compatible with the wait() and as_completed()
methods in the concurrent.futures package.
(In Python 3.4 or later we may be able to unify the implementations.)
"""
# Class variables serving as defaults for instance variables.
@@ -61,12 +61,15 @@ class Future:
# the Future protocol (i.e. is intended to be duck-type compatible).
# The value must also be not-None, to enable a subclass to declare
# that it is not compatible by setting this to None.
# - It is set by __iter__() below so that Task._step() can tell
# - It is set by __iter__() below so that Task.__step() can tell
# the difference between
# `await Future()` or`yield from Future()` (correct) vs.
# `await Future()` or `yield from Future()` (correct) vs.
# `yield Future()` (incorrect).
_asyncio_future_blocking = False
# Used by the capture_call_stack() API.
__asyncio_awaited_by = None
__log_traceback = False
def __init__(self, *, loop=None):
@@ -116,6 +119,12 @@ class Future:
raise ValueError('_log_traceback can only be set to False')
self.__log_traceback = False
@property
def _asyncio_awaited_by(self):
if self.__asyncio_awaited_by is None:
return None
return frozenset(self.__asyncio_awaited_by)
def get_loop(self):
"""Return the event loop the Future is bound to."""
loop = self._loop
@@ -138,9 +147,6 @@ class Future:
exc = exceptions.CancelledError()
else:
exc = exceptions.CancelledError(self._cancel_message)
exc.__context__ = self._cancelled_exc
# Remove the reference since we don't need this anymore.
self._cancelled_exc = None
return exc
def cancel(self, msg=None):
@@ -194,8 +200,7 @@ class Future:
the future is done and has an exception set, this exception is raised.
"""
if self._state == _CANCELLED:
exc = self._make_cancelled_error()
raise exc
raise self._make_cancelled_error()
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Result is not ready.')
self.__log_traceback = False
@@ -212,8 +217,7 @@ class Future:
InvalidStateError.
"""
if self._state == _CANCELLED:
exc = self._make_cancelled_error()
raise exc
raise self._make_cancelled_error()
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Exception is not set.')
self.__log_traceback = False
@@ -272,9 +276,13 @@ class Future:
raise exceptions.InvalidStateError(f'{self._state}: {self!r}')
if isinstance(exception, type):
exception = exception()
if type(exception) is StopIteration:
raise TypeError("StopIteration interacts badly with generators "
"and cannot be raised into a Future")
if isinstance(exception, StopIteration):
new_exc = RuntimeError("StopIteration interacts badly with "
"generators and cannot be raised into a "
"Future")
new_exc.__cause__ = exception
new_exc.__context__ = exception
exception = new_exc
self._exception = exception
self._exception_tb = exception.__traceback__
self._state = _FINISHED
@@ -318,11 +326,9 @@ def _set_result_unless_cancelled(fut, result):
def _convert_future_exc(exc):
exc_class = type(exc)
if exc_class is concurrent.futures.CancelledError:
return exceptions.CancelledError(*exc.args)
elif exc_class is concurrent.futures.TimeoutError:
return exceptions.TimeoutError(*exc.args)
return exceptions.CancelledError(*exc.args).with_traceback(exc.__traceback__)
elif exc_class is concurrent.futures.InvalidStateError:
return exceptions.InvalidStateError(*exc.args)
return exceptions.InvalidStateError(*exc.args).with_traceback(exc.__traceback__)
else:
return exc
@@ -419,6 +425,49 @@ def wrap_future(future, *, loop=None):
return new_future
def future_add_to_awaited_by(fut, waiter, /):
"""Record that `fut` is awaited on by `waiter`."""
# For the sake of keeping the implementation minimal and assuming
# that most of asyncio users use the built-in Futures and Tasks
# (or their subclasses), we only support native Future objects
# and their subclasses.
#
# Longer version: tracking requires storing the caller-callee
# dependency somewhere. One obvious choice is to store that
# information right in the future itself in a dedicated attribute.
# This means that we'd have to require all duck-type compatible
# futures to implement a specific attribute used by asyncio for
# the book keeping. Another solution would be to store that in
# a global dictionary. The downside here is that that would create
# strong references and any scenario where the "add" call isn't
# followed by a "discard" call would lead to a memory leak.
# Using WeakDict would resolve that issue, but would complicate
# the C code (_asynciomodule.c). The bottom line here is that
# it's not clear that all this work would be worth the effort.
#
# Note that there's an accelerated version of this function
# shadowing this implementation later in this file.
if isinstance(fut, _PyFuture) and isinstance(waiter, _PyFuture):
if fut._Future__asyncio_awaited_by is None:
fut._Future__asyncio_awaited_by = set()
fut._Future__asyncio_awaited_by.add(waiter)
def future_discard_from_awaited_by(fut, waiter, /):
"""Record that `fut` is no longer awaited on by `waiter`."""
# See the comment in "future_add_to_awaited_by()" body for
# details on implementation.
#
# Note that there's an accelerated version of this function
# shadowing this implementation later in this file.
if isinstance(fut, _PyFuture) and isinstance(waiter, _PyFuture):
if fut._Future__asyncio_awaited_by is not None:
fut._Future__asyncio_awaited_by.discard(waiter)
_py_future_add_to_awaited_by = future_add_to_awaited_by
_py_future_discard_from_awaited_by = future_discard_from_awaited_by
try:
import _asyncio
except ImportError:
@@ -426,3 +475,7 @@ except ImportError:
else:
# _CFuture is needed for tests.
Future = _CFuture = _asyncio.Future
future_add_to_awaited_by = _asyncio.future_add_to_awaited_by
future_discard_from_awaited_by = _asyncio.future_discard_from_awaited_by
_c_future_add_to_awaited_by = future_add_to_awaited_by
_c_future_discard_from_awaited_by = future_discard_from_awaited_by

276
Lib/asyncio/graph.py vendored Normal file
View File

@@ -0,0 +1,276 @@
"""Introspection utils for tasks call graphs."""
import dataclasses
import io
import sys
import types
from . import events
from . import futures
from . import tasks
__all__ = (
'capture_call_graph',
'format_call_graph',
'print_call_graph',
'FrameCallGraphEntry',
'FutureCallGraph',
)
# Sadly, we can't re-use the traceback module's datastructures as those
# are tailored for error reporting, whereas we need to represent an
# async call graph.
#
# Going with pretty verbose names as we'd like to export them to the
# top level asyncio namespace, and want to avoid future name clashes.
@dataclasses.dataclass(frozen=True, slots=True)
class FrameCallGraphEntry:
frame: types.FrameType
@dataclasses.dataclass(frozen=True, slots=True)
class FutureCallGraph:
future: futures.Future
call_stack: tuple["FrameCallGraphEntry", ...]
awaited_by: tuple["FutureCallGraph", ...]
def _build_graph_for_future(
future: futures.Future,
*,
limit: int | None = None,
) -> FutureCallGraph:
if not isinstance(future, futures.Future):
raise TypeError(
f"{future!r} object does not appear to be compatible "
f"with asyncio.Future"
)
coro = None
if get_coro := getattr(future, 'get_coro', None):
coro = get_coro() if limit != 0 else None
st: list[FrameCallGraphEntry] = []
awaited_by: list[FutureCallGraph] = []
while coro is not None:
if hasattr(coro, 'cr_await'):
# A native coroutine or duck-type compatible iterator
st.append(FrameCallGraphEntry(coro.cr_frame))
coro = coro.cr_await
elif hasattr(coro, 'ag_await'):
# A native async generator or duck-type compatible iterator
st.append(FrameCallGraphEntry(coro.cr_frame))
coro = coro.ag_await
else:
break
if future._asyncio_awaited_by:
for parent in future._asyncio_awaited_by:
awaited_by.append(_build_graph_for_future(parent, limit=limit))
if limit is not None:
if limit > 0:
st = st[:limit]
elif limit < 0:
st = st[limit:]
st.reverse()
return FutureCallGraph(future, tuple(st), tuple(awaited_by))
def capture_call_graph(
future: futures.Future | None = None,
/,
*,
depth: int = 1,
limit: int | None = None,
) -> FutureCallGraph | None:
"""Capture the async call graph for the current task or the provided Future.
The graph is represented with three data structures:
* FutureCallGraph(future, call_stack, awaited_by)
Where 'future' is an instance of asyncio.Future or asyncio.Task.
'call_stack' is a tuple of FrameGraphEntry objects.
'awaited_by' is a tuple of FutureCallGraph objects.
* FrameCallGraphEntry(frame)
Where 'frame' is a frame object of a regular Python function
in the call stack.
Receives an optional 'future' argument. If not passed,
the current task will be used. If there's no current task, the function
returns None.
If "capture_call_graph()" is introspecting *the current task*, the
optional keyword-only 'depth' argument can be used to skip the specified
number of frames from top of the stack.
If the optional keyword-only 'limit' argument is provided, each call stack
in the resulting graph is truncated to include at most ``abs(limit)``
entries. If 'limit' is positive, the entries left are the closest to
the invocation point. If 'limit' is negative, the topmost entries are
left. If 'limit' is omitted or None, all entries are present.
If 'limit' is 0, the call stack is not captured at all, only
"awaited by" information is present.
"""
loop = events._get_running_loop()
if future is not None:
# Check if we're in a context of a running event loop;
# if yes - check if the passed future is the currently
# running task or not.
if loop is None or future is not tasks.current_task(loop=loop):
return _build_graph_for_future(future, limit=limit)
# else: future is the current task, move on.
else:
if loop is None:
raise RuntimeError(
'capture_call_graph() is called outside of a running '
'event loop and no *future* to introspect was provided')
future = tasks.current_task(loop=loop)
if future is None:
# This isn't a generic call stack introspection utility. If we
# can't determine the current task and none was provided, we
# just return.
return None
if not isinstance(future, futures.Future):
raise TypeError(
f"{future!r} object does not appear to be compatible "
f"with asyncio.Future"
)
call_stack: list[FrameCallGraphEntry] = []
f = sys._getframe(depth) if limit != 0 else None
try:
while f is not None:
is_async = f.f_generator is not None
call_stack.append(FrameCallGraphEntry(f))
if is_async:
if f.f_back is not None and f.f_back.f_generator is None:
# We've reached the bottom of the coroutine stack, which
# must be the Task that runs it.
break
f = f.f_back
finally:
del f
awaited_by = []
if future._asyncio_awaited_by:
for parent in future._asyncio_awaited_by:
awaited_by.append(_build_graph_for_future(parent, limit=limit))
if limit is not None:
limit *= -1
if limit > 0:
call_stack = call_stack[:limit]
elif limit < 0:
call_stack = call_stack[limit:]
return FutureCallGraph(future, tuple(call_stack), tuple(awaited_by))
def format_call_graph(
future: futures.Future | None = None,
/,
*,
depth: int = 1,
limit: int | None = None,
) -> str:
"""Return the async call graph as a string for `future`.
If `future` is not provided, format the call graph for the current task.
"""
def render_level(st: FutureCallGraph, buf: list[str], level: int) -> None:
def add_line(line: str) -> None:
buf.append(level * ' ' + line)
if isinstance(st.future, tasks.Task):
add_line(
f'* Task(name={st.future.get_name()!r}, id={id(st.future):#x})'
)
else:
add_line(
f'* Future(id={id(st.future):#x})'
)
if st.call_stack:
add_line(
f' + Call stack:'
)
for ste in st.call_stack:
f = ste.frame
if f.f_generator is None:
f = ste.frame
add_line(
f' | File {f.f_code.co_filename!r},'
f' line {f.f_lineno}, in'
f' {f.f_code.co_qualname}()'
)
else:
c = f.f_generator
try:
f = c.cr_frame
code = c.cr_code
tag = 'async'
except AttributeError:
try:
f = c.ag_frame
code = c.ag_code
tag = 'async generator'
except AttributeError:
f = c.gi_frame
code = c.gi_code
tag = 'generator'
add_line(
f' | File {f.f_code.co_filename!r},'
f' line {f.f_lineno}, in'
f' {tag} {code.co_qualname}()'
)
if st.awaited_by:
add_line(
f' + Awaited by:'
)
for fut in st.awaited_by:
render_level(fut, buf, level + 1)
graph = capture_call_graph(future, depth=depth + 1, limit=limit)
if graph is None:
return ""
buf: list[str] = []
try:
render_level(graph, buf, 0)
finally:
# 'graph' has references to frames so we should
# make sure it's GC'ed as soon as we don't need it.
del graph
return '\n'.join(buf)
def print_call_graph(
future: futures.Future | None = None,
/,
*,
file: io.Writer[str] | None = None,
depth: int = 1,
limit: int | None = None,
) -> None:
"""Print the async call graph for the current task or the provided Future."""
print(format_call_graph(future, depth=depth, limit=limit), file=file)

159
Lib/asyncio/locks.py vendored
View File

@@ -24,25 +24,23 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
"""Primitive lock objects.
A primitive lock is a synchronization primitive that is not owned
by a particular coroutine when locked. A primitive lock is in one
by a particular task when locked. A primitive lock is in one
of two states, 'locked' or 'unlocked'.
It is created in the unlocked state. It has two basic methods,
acquire() and release(). When the state is unlocked, acquire()
changes the state to locked and returns immediately. When the
state is locked, acquire() blocks until a call to release() in
another coroutine changes it to unlocked, then the acquire() call
another task changes it to unlocked, then the acquire() call
resets it to locked and returns. The release() method should only
be called in the locked state; it changes the state to unlocked
and returns immediately. If an attempt is made to release an
unlocked lock, a RuntimeError will be raised.
When more than one coroutine is blocked in acquire() waiting for
the state to turn to unlocked, only one coroutine proceeds when a
release() call resets the state to unlocked; first coroutine which
is blocked in acquire() is being processed.
acquire() is a coroutine and should be called with 'await'.
When more than one task is blocked in acquire() waiting for
the state to turn to unlocked, only one task proceeds when a
release() call resets the state to unlocked; successive release()
calls will unblock tasks in FIFO order.
Locks also support the asynchronous context management protocol.
'async with lock' statement should be used.
@@ -95,6 +93,8 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
This method blocks until the lock is unlocked, then sets it to
locked and returns True.
"""
# Implement fair scheduling, where thread always waits
# its turn. Jumping the queue if all are cancelled is an optimization.
if (not self._locked and (self._waiters is None or
all(w.cancelled() for w in self._waiters))):
self._locked = True
@@ -105,19 +105,22 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
fut = self._get_loop().create_future()
self._waiters.append(fut)
# Finally block should be called before the CancelledError
# handling as we don't want CancelledError to call
# _wake_up_first() and attempt to wake up itself.
try:
try:
await fut
finally:
self._waiters.remove(fut)
except exceptions.CancelledError:
# Currently the only exception designed be able to occur here.
# Ensure the lock invariant: If lock is not claimed (or about
# to be claimed by us) and there is a Task in waiters,
# ensure that the Task at the head will run.
if not self._locked:
self._wake_up_first()
raise
# assert self._locked is False
self._locked = True
return True
@@ -125,7 +128,7 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
"""Release a lock.
When the lock is locked, reset it to unlocked, and return.
If any other coroutines are blocked waiting for the lock to become
If any other tasks are blocked waiting for the lock to become
unlocked, allow exactly one of them to proceed.
When invoked on an unlocked lock, a RuntimeError is raised.
@@ -139,7 +142,7 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
raise RuntimeError('Lock is not acquired.')
def _wake_up_first(self):
"""Wake up the first waiter if it isn't done."""
"""Ensure that the first waiter will wake up."""
if not self._waiters:
return
try:
@@ -147,9 +150,7 @@ class Lock(_ContextManagerMixin, mixins._LoopBoundMixin):
except StopIteration:
return
# .done() necessarily means that a waiter will wake up later on and
# either take the lock, or, if it was cancelled and lock wasn't
# taken already, will hit this again and wake up a new waiter.
# .done() means that the waiter is already set to wake up.
if not fut.done():
fut.set_result(True)
@@ -179,8 +180,8 @@ class Event(mixins._LoopBoundMixin):
return self._value
def set(self):
"""Set the internal flag to true. All coroutines waiting for it to
become true are awakened. Coroutine that call wait() once the flag is
"""Set the internal flag to true. All tasks waiting for it to
become true are awakened. Tasks that call wait() once the flag is
true will not block at all.
"""
if not self._value:
@@ -191,7 +192,7 @@ class Event(mixins._LoopBoundMixin):
fut.set_result(True)
def clear(self):
"""Reset the internal flag to false. Subsequently, coroutines calling
"""Reset the internal flag to false. Subsequently, tasks calling
wait() will block until set() is called to set the internal flag
to true again."""
self._value = False
@@ -200,7 +201,7 @@ class Event(mixins._LoopBoundMixin):
"""Block until the internal flag is true.
If the internal flag is true on entry, return True
immediately. Otherwise, block until another coroutine calls
immediately. Otherwise, block until another task calls
set() to set the flag to true, then return True.
"""
if self._value:
@@ -219,8 +220,8 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundMixin):
"""Asynchronous equivalent to threading.Condition.
This class implements condition variable objects. A condition variable
allows one or more coroutines to wait until they are notified by another
coroutine.
allows one or more tasks to wait until they are notified by another
task.
A new Lock object is created and used as the underlying lock.
"""
@@ -247,45 +248,64 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundMixin):
async def wait(self):
"""Wait until notified.
If the calling coroutine has not acquired the lock when this
If the calling task has not acquired the lock when this
method is called, a RuntimeError is raised.
This method releases the underlying lock, and then blocks
until it is awakened by a notify() or notify_all() call for
the same condition variable in another coroutine. Once
the same condition variable in another task. Once
awakened, it re-acquires the lock and returns True.
This method may return spuriously,
which is why the caller should always
re-check the state and be prepared to wait() again.
"""
if not self.locked():
raise RuntimeError('cannot wait on un-acquired lock')
fut = self._get_loop().create_future()
self.release()
try:
fut = self._get_loop().create_future()
self._waiters.append(fut)
try:
await fut
return True
finally:
self._waiters.remove(fut)
finally:
# Must reacquire lock even if wait is cancelled
cancelled = False
while True:
self._waiters.append(fut)
try:
await self.acquire()
break
except exceptions.CancelledError:
cancelled = True
await fut
return True
finally:
self._waiters.remove(fut)
if cancelled:
raise exceptions.CancelledError
finally:
# Must re-acquire lock even if wait is cancelled.
# We only catch CancelledError here, since we don't want any
# other (fatal) errors with the future to cause us to spin.
err = None
while True:
try:
await self.acquire()
break
except exceptions.CancelledError as e:
err = e
if err is not None:
try:
raise err # Re-raise most recent exception instance.
finally:
err = None # Break reference cycles.
except BaseException:
# Any error raised out of here _may_ have occurred after this Task
# believed to have been successfully notified.
# Make sure to notify another Task instead. This may result
# in a "spurious wakeup", which is allowed as part of the
# Condition Variable protocol.
self._notify(1)
raise
async def wait_for(self, predicate):
"""Wait until a predicate becomes true.
The predicate should be a callable which result will be
interpreted as a boolean value. The final predicate value is
The predicate should be a callable whose result will be
interpreted as a boolean value. The method will repeatedly
wait() until it evaluates to true. The final predicate value is
the return value.
"""
result = predicate()
@@ -295,20 +315,22 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundMixin):
return result
def notify(self, n=1):
"""By default, wake up one coroutine waiting on this condition, if any.
If the calling coroutine has not acquired the lock when this method
"""By default, wake up one task waiting on this condition, if any.
If the calling task has not acquired the lock when this method
is called, a RuntimeError is raised.
This method wakes up at most n of the coroutines waiting for the
condition variable; it is a no-op if no coroutines are waiting.
This method wakes up n of the tasks waiting for the condition
variable; if fewer than n are waiting, they are all awoken.
Note: an awakened coroutine does not actually return from its
Note: an awakened task does not actually return from its
wait() call until it can reacquire the lock. Since notify() does
not release the lock, its caller should.
"""
if not self.locked():
raise RuntimeError('cannot notify on un-acquired lock')
self._notify(n)
def _notify(self, n):
idx = 0
for fut in self._waiters:
if idx >= n:
@@ -319,9 +341,9 @@ class Condition(_ContextManagerMixin, mixins._LoopBoundMixin):
fut.set_result(False)
def notify_all(self):
"""Wake up all threads waiting on this condition. This method acts
like notify(), but wakes up all waiting threads instead of one. If the
calling thread has not acquired the lock when this method is called,
"""Wake up all tasks waiting on this condition. This method acts
like notify(), but wakes up all waiting tasks instead of one. If the
calling task has not acquired the lock when this method is called,
a RuntimeError is raised.
"""
self.notify(len(self._waiters))
@@ -357,6 +379,7 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
def locked(self):
"""Returns True if semaphore cannot be acquired immediately."""
# Due to state, or FIFO rules (must allow others to run first).
return self._value == 0 or (
any(not w.cancelled() for w in (self._waiters or ())))
@@ -365,11 +388,12 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
If the internal counter is larger than zero on entry,
decrement it by one and return True immediately. If it is
zero on entry, block, waiting until some other coroutine has
zero on entry, block, waiting until some other task has
called release() to make it larger than 0, and then return
True.
"""
if not self.locked():
# Maintain FIFO, wait for others to start even if _value > 0.
self._value -= 1
return True
@@ -378,29 +402,34 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
fut = self._get_loop().create_future()
self._waiters.append(fut)
# Finally block should be called before the CancelledError
# handling as we don't want CancelledError to call
# _wake_up_first() and attempt to wake up itself.
try:
try:
await fut
finally:
self._waiters.remove(fut)
except exceptions.CancelledError:
if not fut.cancelled():
# Currently the only exception designed be able to occur here.
if fut.done() and not fut.cancelled():
# Our Future was successfully set to True via _wake_up_next(),
# but we are not about to successfully acquire(). Therefore we
# must undo the bookkeeping already done and attempt to wake
# up someone else.
self._value += 1
self._wake_up_next()
raise
if self._value > 0:
self._wake_up_next()
finally:
# New waiters may have arrived but had to wait due to FIFO.
# Wake up as many as are allowed.
while self._value > 0:
if not self._wake_up_next():
break # There was no-one to wake up.
return True
def release(self):
"""Release a semaphore, incrementing the internal counter by one.
When it was zero on entry and another coroutine is waiting for it to
become larger than zero again, wake up that coroutine.
When it was zero on entry and another task is waiting for it to
become larger than zero again, wake up that task.
"""
self._value += 1
self._wake_up_next()
@@ -408,13 +437,15 @@ class Semaphore(_ContextManagerMixin, mixins._LoopBoundMixin):
def _wake_up_next(self):
"""Wake up the first waiter that isn't done."""
if not self._waiters:
return
return False
for fut in self._waiters:
if not fut.done():
self._value -= 1
fut.set_result(True)
return
# `fut` is now `done()` and not `cancelled()`.
return True
return False
class BoundedSemaphore(Semaphore):
@@ -454,7 +485,7 @@ class Barrier(mixins._LoopBoundMixin):
def __init__(self, parties):
"""Create a barrier, initialised to 'parties' tasks."""
if parties < 1:
raise ValueError('parties must be > 0')
raise ValueError('parties must be >= 1')
self._cond = Condition() # notify all tasks when state changes

View File

@@ -63,7 +63,7 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin,
self._called_connection_lost = False
self._eof_written = False
if self._server is not None:
self._server._attach()
self._server._attach(self)
self._loop.call_soon(self._protocol.connection_made, self)
if waiter is not None:
# only wake up the waiter when connection_made() has been called
@@ -167,7 +167,7 @@ class _ProactorBasePipeTransport(transports._FlowControlMixin,
self._sock = None
server = self._server
if server is not None:
server._detach()
server._detach(self)
self._server = None
self._called_connection_lost = True
@@ -460,6 +460,8 @@ class _ProactorWritePipeTransport(_ProactorBaseWritePipeTransport):
class _ProactorDatagramTransport(_ProactorBasePipeTransport,
transports.DatagramTransport):
max_size = 256 * 1024
_header_size = 8
def __init__(self, loop, sock, protocol, address=None,
waiter=None, extra=None):
self._address = address
@@ -487,9 +489,6 @@ class _ProactorDatagramTransport(_ProactorBasePipeTransport,
raise TypeError('data argument must be bytes-like object (%r)',
type(data))
if not data:
return
if self._address is not None and addr not in (None, self._address):
raise ValueError(
f'Invalid address: must be None or {self._address}')
@@ -502,7 +501,7 @@ class _ProactorDatagramTransport(_ProactorBasePipeTransport,
# Ensure that what we buffer is immutable.
self._buffer.append((bytes(data), addr))
self._buffer_size += len(data)
self._buffer_size += len(data) + self._header_size
if self._write_fut is None:
# No current write operations are active, kick one off
@@ -529,7 +528,7 @@ class _ProactorDatagramTransport(_ProactorBasePipeTransport,
return
data, addr = self._buffer.popleft()
self._buffer_size -= len(data)
self._buffer_size -= len(data) + self._header_size
if self._address is not None:
self._write_fut = self._loop._proactor.send(self._sock,
data)
@@ -724,6 +723,8 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
return await self._proactor.sendto(sock, data, 0, address)
async def sock_connect(self, sock, address):
if self._debug and sock.gettimeout() != 0:
raise ValueError("the socket must be non-blocking")
return await self._proactor.connect(sock, address)
async def sock_accept(self, sock):

69
Lib/asyncio/queues.py vendored
View File

@@ -1,4 +1,11 @@
__all__ = ('Queue', 'PriorityQueue', 'LifoQueue', 'QueueFull', 'QueueEmpty')
__all__ = (
'Queue',
'PriorityQueue',
'LifoQueue',
'QueueFull',
'QueueEmpty',
'QueueShutDown',
)
import collections
import heapq
@@ -18,6 +25,11 @@ class QueueFull(Exception):
pass
class QueueShutDown(Exception):
"""Raised when putting on to or getting from a shut-down Queue."""
pass
class Queue(mixins._LoopBoundMixin):
"""A queue, useful for coordinating producer and consumer coroutines.
@@ -41,6 +53,7 @@ class Queue(mixins._LoopBoundMixin):
self._finished = locks.Event()
self._finished.set()
self._init(maxsize)
self._is_shutdown = False
# These three are overridable in subclasses.
@@ -81,6 +94,8 @@ class Queue(mixins._LoopBoundMixin):
result += f' _putters[{len(self._putters)}]'
if self._unfinished_tasks:
result += f' tasks={self._unfinished_tasks}'
if self._is_shutdown:
result += ' shutdown'
return result
def qsize(self):
@@ -112,8 +127,12 @@ class Queue(mixins._LoopBoundMixin):
Put an item into the queue. If the queue is full, wait until a free
slot is available before adding item.
Raises QueueShutDown if the queue has been shut down.
"""
while self.full():
if self._is_shutdown:
raise QueueShutDown
putter = self._get_loop().create_future()
self._putters.append(putter)
try:
@@ -125,7 +144,7 @@ class Queue(mixins._LoopBoundMixin):
self._putters.remove(putter)
except ValueError:
# The putter could be removed from self._putters by a
# previous get_nowait call.
# previous get_nowait call or a shutdown call.
pass
if not self.full() and not putter.cancelled():
# We were woken up by get_nowait(), but can't take
@@ -138,7 +157,11 @@ class Queue(mixins._LoopBoundMixin):
"""Put an item into the queue without blocking.
If no free slot is immediately available, raise QueueFull.
Raises QueueShutDown if the queue has been shut down.
"""
if self._is_shutdown:
raise QueueShutDown
if self.full():
raise QueueFull
self._put(item)
@@ -150,8 +173,13 @@ class Queue(mixins._LoopBoundMixin):
"""Remove and return an item from the queue.
If queue is empty, wait until an item is available.
Raises QueueShutDown if the queue has been shut down and is empty, or
if the queue has been shut down immediately.
"""
while self.empty():
if self._is_shutdown and self.empty():
raise QueueShutDown
getter = self._get_loop().create_future()
self._getters.append(getter)
try:
@@ -163,7 +191,7 @@ class Queue(mixins._LoopBoundMixin):
self._getters.remove(getter)
except ValueError:
# The getter could be removed from self._getters by a
# previous put_nowait call.
# previous put_nowait call, or a shutdown call.
pass
if not self.empty() and not getter.cancelled():
# We were woken up by put_nowait(), but can't take
@@ -176,8 +204,13 @@ class Queue(mixins._LoopBoundMixin):
"""Remove and return an item from the queue.
Return an item if one is immediately available, else raise QueueEmpty.
Raises QueueShutDown if the queue has been shut down and is empty, or
if the queue has been shut down immediately.
"""
if self.empty():
if self._is_shutdown:
raise QueueShutDown
raise QueueEmpty
item = self._get()
self._wakeup_next(self._putters)
@@ -214,6 +247,36 @@ class Queue(mixins._LoopBoundMixin):
if self._unfinished_tasks > 0:
await self._finished.wait()
def shutdown(self, immediate=False):
"""Shut-down the queue, making queue gets and puts raise QueueShutDown.
By default, gets will only raise once the queue is empty. Set
'immediate' to True to make gets raise immediately instead.
All blocked callers of put() and get() will be unblocked.
If 'immediate', the queue is drained and unfinished tasks
is reduced by the number of drained tasks. If unfinished tasks
is reduced to zero, callers of Queue.join are unblocked.
"""
self._is_shutdown = True
if immediate:
while not self.empty():
self._get()
if self._unfinished_tasks > 0:
self._unfinished_tasks -= 1
if self._unfinished_tasks == 0:
self._finished.set()
# All getters need to re-check queue-empty to raise ShutDown
while self._getters:
getter = self._getters.popleft()
if not getter.done():
getter.set_result(None)
while self._putters:
putter = self._putters.popleft()
if not putter.done():
putter.set_result(None)
class PriorityQueue(Queue):
"""A subclass of Queue; retrieves entries in priority order (lowest first).

View File

@@ -3,6 +3,7 @@ __all__ = ('Runner', 'run')
import contextvars
import enum
import functools
import inspect
import threading
import signal
from . import coroutines
@@ -84,10 +85,7 @@ class Runner:
return self._loop
def run(self, coro, *, context=None):
"""Run a coroutine inside the embedded event loop."""
if not coroutines.iscoroutine(coro):
raise ValueError("a coroutine was expected, got {!r}".format(coro))
"""Run code in the embedded event loop."""
if events._get_running_loop() is not None:
# fail fast with short traceback
raise RuntimeError(
@@ -95,8 +93,19 @@ class Runner:
self._lazy_init()
if not coroutines.iscoroutine(coro):
if inspect.isawaitable(coro):
async def _wrap_awaitable(awaitable):
return await awaitable
coro = _wrap_awaitable(coro)
else:
raise TypeError('An asyncio.Future, a coroutine or an '
'awaitable is required')
if context is None:
context = self._context
task = self._loop.create_task(coro, context=context)
if (threading.current_thread() is threading.main_thread()
@@ -168,6 +177,7 @@ def run(main, *, debug=None, loop_factory=None):
running in the same thread.
If debug is True, the event loop will be run in debug mode.
If loop_factory is passed, it is used for new event loop creation.
This function always creates a new event loop and closes it at the end.
It should be used as a main entry point for asyncio programs, and should

View File

@@ -173,16 +173,20 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
# listening socket has triggered an EVENT_READ. There may be multiple
# connections waiting for an .accept() so it is called in a loop.
# See https://bugs.python.org/issue27906 for more details.
for _ in range(backlog):
for _ in range(backlog + 1):
try:
conn, addr = sock.accept()
if self._debug:
logger.debug("%r got a new connection from %r: %r",
server, addr, conn)
conn.setblocking(False)
except (BlockingIOError, InterruptedError, ConnectionAbortedError):
# Early exit because the socket accept buffer is empty.
return None
except ConnectionAbortedError:
# Discard connections that were aborted before accept().
continue
except (BlockingIOError, InterruptedError):
# Early exit because of a signal or
# the socket accept buffer is empty.
return
except OSError as exc:
# There's nowhere to send the error, so just log it.
if exc.errno in (errno.EMFILE, errno.ENFILE,
@@ -265,22 +269,17 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
except (AttributeError, TypeError, ValueError):
# This code matches selectors._fileobj_to_fd function.
raise ValueError(f"Invalid file object: {fd!r}") from None
try:
transport = self._transports[fileno]
except KeyError:
pass
else:
if not transport.is_closing():
raise RuntimeError(
f'File descriptor {fd!r} is used by transport '
f'{transport!r}')
transport = self._transports.get(fileno)
if transport and not transport.is_closing():
raise RuntimeError(
f'File descriptor {fd!r} is used by transport '
f'{transport!r}')
def _add_reader(self, fd, callback, *args):
self._check_closed()
handle = events.Handle(callback, args, self, None)
try:
key = self._selector.get_key(fd)
except KeyError:
key = self._selector.get_map().get(fd)
if key is None:
self._selector.register(fd, selectors.EVENT_READ,
(handle, None))
else:
@@ -294,30 +293,27 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
def _remove_reader(self, fd):
if self.is_closed():
return False
try:
key = self._selector.get_key(fd)
except KeyError:
key = self._selector.get_map().get(fd)
if key is None:
return False
mask, (reader, writer) = key.events, key.data
mask &= ~selectors.EVENT_READ
if not mask:
self._selector.unregister(fd)
else:
mask, (reader, writer) = key.events, key.data
mask &= ~selectors.EVENT_READ
if not mask:
self._selector.unregister(fd)
else:
self._selector.modify(fd, mask, (None, writer))
self._selector.modify(fd, mask, (None, writer))
if reader is not None:
reader.cancel()
return True
else:
return False
if reader is not None:
reader.cancel()
return True
else:
return False
def _add_writer(self, fd, callback, *args):
self._check_closed()
handle = events.Handle(callback, args, self, None)
try:
key = self._selector.get_key(fd)
except KeyError:
key = self._selector.get_map().get(fd)
if key is None:
self._selector.register(fd, selectors.EVENT_WRITE,
(None, handle))
else:
@@ -332,24 +328,22 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
"""Remove a writer callback."""
if self.is_closed():
return False
try:
key = self._selector.get_key(fd)
except KeyError:
key = self._selector.get_map().get(fd)
if key is None:
return False
mask, (reader, writer) = key.events, key.data
# Remove both writer and connector.
mask &= ~selectors.EVENT_WRITE
if not mask:
self._selector.unregister(fd)
else:
mask, (reader, writer) = key.events, key.data
# Remove both writer and connector.
mask &= ~selectors.EVENT_WRITE
if not mask:
self._selector.unregister(fd)
else:
self._selector.modify(fd, mask, (reader, None))
self._selector.modify(fd, mask, (reader, None))
if writer is not None:
writer.cancel()
return True
else:
return False
if writer is not None:
writer.cancel()
return True
else:
return False
def add_reader(self, fd, callback, *args):
"""Add a reader callback."""
@@ -801,7 +795,7 @@ class _SelectorTransport(transports._FlowControlMixin,
self._paused = False # Set when pause_reading() called
if self._server is not None:
self._server._attach()
self._server._attach(self)
loop._transports[self._sock_fd] = self
def __repr__(self):
@@ -878,6 +872,8 @@ class _SelectorTransport(transports._FlowControlMixin,
if self._sock is not None:
_warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
self._sock.close()
if self._server is not None:
self._server._detach(self)
def _fatal_error(self, exc, message='Fatal error on transport'):
# Should be called from exception handler only.
@@ -916,7 +912,7 @@ class _SelectorTransport(transports._FlowControlMixin,
self._loop = None
server = self._server
if server is not None:
server._detach()
server._detach(self)
self._server = None
def get_write_buffer_size(self):
@@ -1054,8 +1050,8 @@ class _SelectorSocketTransport(_SelectorTransport):
def write(self, data):
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError(f'data argument must be a bytes-like object, '
f'not {type(data).__name__!r}')
raise TypeError(f'data argument must be a bytes, bytearray, or memoryview '
f'object, not {type(data).__name__!r}')
if self._eof:
raise RuntimeError('Cannot call write() after write_eof()')
if self._empty_waiter is not None:
@@ -1178,20 +1174,31 @@ class _SelectorSocketTransport(_SelectorTransport):
raise RuntimeError('unable to writelines; sendfile is in progress')
if not list_of_data:
return
if self._conn_lost:
if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
logger.warning('socket.send() raised exception.')
self._conn_lost += 1
return
self._buffer.extend([memoryview(data) for data in list_of_data])
self._write_ready()
# If the entire buffer couldn't be written, register a write handler
if self._buffer:
self._loop._add_writer(self._sock_fd, self._write_ready)
self._maybe_pause_protocol()
def can_write_eof(self):
return True
def _call_connection_lost(self, exc):
super()._call_connection_lost(exc)
if self._empty_waiter is not None:
self._empty_waiter.set_exception(
ConnectionError("Connection is closed by peer"))
try:
super()._call_connection_lost(exc)
finally:
self._write_ready = None
if self._empty_waiter is not None:
self._empty_waiter.set_exception(
ConnectionError("Connection is closed by peer"))
def _make_empty_waiter(self):
if self._empty_waiter is not None:
@@ -1206,13 +1213,13 @@ class _SelectorSocketTransport(_SelectorTransport):
def close(self):
self._read_ready_cb = None
self._write_ready = None
super().close()
class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTransport):
_buffer_factory = collections.deque
_header_size = 8
def __init__(self, loop, sock, protocol, address=None,
waiter=None, extra=None):
@@ -1251,8 +1258,6 @@ class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTranspor
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError(f'data argument must be a bytes-like object, '
f'not {type(data).__name__!r}')
if not data:
return
if self._address:
if addr not in (None, self._address):
@@ -1288,13 +1293,13 @@ class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTranspor
# Ensure that what we buffer is immutable.
self._buffer.append((bytes(data), addr))
self._buffer_size += len(data)
self._buffer_size += len(data) + self._header_size
self._maybe_pause_protocol()
def _sendto_ready(self):
while self._buffer:
data, addr = self._buffer.popleft()
self._buffer_size -= len(data)
self._buffer_size -= len(data) + self._header_size
try:
if self._extra['peername']:
self._sock.send(data)
@@ -1302,7 +1307,7 @@ class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTranspor
self._sock.sendto(data, addr)
except (BlockingIOError, InterruptedError):
self._buffer.appendleft((data, addr)) # Try again later.
self._buffer_size += len(data)
self._buffer_size += len(data) + self._header_size
break
except OSError as exc:
self._protocol.error_received(exc)

View File

@@ -101,7 +101,7 @@ class _SSLProtocolTransport(transports._FlowControlMixin,
return self._ssl_protocol._app_protocol
def is_closing(self):
return self._closed
return self._closed or self._ssl_protocol._is_transport_closing()
def close(self):
"""Close the transport.
@@ -379,6 +379,9 @@ class SSLProtocol(protocols.BufferedProtocol):
self._app_transport_created = True
return self._app_transport
def _is_transport_closing(self):
return self._transport is not None and self._transport.is_closing()
def connection_made(self, transport):
"""Called when the low-level connection is made.
@@ -542,7 +545,7 @@ class SSLProtocol(protocols.BufferedProtocol):
# start handshake timeout count down
self._handshake_timeout_handle = \
self._loop.call_later(self._ssl_handshake_timeout,
lambda: self._check_handshake_timeout())
self._check_handshake_timeout)
self._do_handshake()
@@ -623,7 +626,7 @@ class SSLProtocol(protocols.BufferedProtocol):
self._set_state(SSLProtocolState.FLUSHING)
self._shutdown_timeout_handle = self._loop.call_later(
self._ssl_shutdown_timeout,
lambda: self._check_shutdown_timeout()
self._check_shutdown_timeout
)
self._do_flush()
@@ -762,7 +765,7 @@ class SSLProtocol(protocols.BufferedProtocol):
else:
break
else:
self._loop.call_soon(lambda: self._do_read())
self._loop.call_soon(self._do_read)
except SSLAgainErrors:
pass
if offset > 0:

View File

@@ -3,24 +3,15 @@
__all__ = 'staggered_race',
import contextlib
import typing
from . import events
from . import exceptions as exceptions_mod
from . import locks
from . import tasks
from . import futures
async def staggered_race(
coro_fns: typing.Iterable[typing.Callable[[], typing.Awaitable]],
delay: typing.Optional[float],
*,
loop: events.AbstractEventLoop = None,
) -> typing.Tuple[
typing.Any,
typing.Optional[int],
typing.List[typing.Optional[Exception]]
]:
async def staggered_race(coro_fns, delay, *, loop=None):
"""Run coroutines with staggered start times and take the first to finish.
This method takes an iterable of coroutine functions. The first one is
@@ -73,14 +64,38 @@ async def staggered_race(
"""
# TODO: when we have aiter() and anext(), allow async iterables in coro_fns.
loop = loop or events.get_running_loop()
parent_task = tasks.current_task(loop)
enum_coro_fns = enumerate(coro_fns)
winner_result = None
winner_index = None
unhandled_exceptions = []
exceptions = []
running_tasks = []
running_tasks = set()
on_completed_fut = None
async def run_one_coro(
previous_failed: typing.Optional[locks.Event]) -> None:
def task_done(task):
running_tasks.discard(task)
futures.future_discard_from_awaited_by(task, parent_task)
if (
on_completed_fut is not None
and not on_completed_fut.done()
and not running_tasks
):
on_completed_fut.set_result(None)
if task.cancelled():
return
exc = task.exception()
if exc is None:
return
unhandled_exceptions.append(exc)
async def run_one_coro(ok_to_start, previous_failed) -> None:
# in eager tasks this waits for the calling task to append this task
# to running_tasks, in regular tasks this wait is a no-op that does
# not yield a future. See gh-124309.
await ok_to_start.wait()
# Wait for the previous task to finish, or for delay seconds
if previous_failed is not None:
with contextlib.suppress(exceptions_mod.TimeoutError):
@@ -96,9 +111,14 @@ async def staggered_race(
return
# Start task that will run the next coroutine
this_failed = locks.Event()
next_task = loop.create_task(run_one_coro(this_failed))
running_tasks.append(next_task)
assert len(running_tasks) == this_index + 2
next_ok_to_start = locks.Event()
next_task = loop.create_task(run_one_coro(next_ok_to_start, this_failed))
futures.future_add_to_awaited_by(next_task, parent_task)
running_tasks.add(next_task)
next_task.add_done_callback(task_done)
# next_task has been appended to running_tasks so next_task is ok to
# start.
next_ok_to_start.set()
# Prepare place to put this coroutine's exceptions if not won
exceptions.append(None)
assert len(exceptions) == this_index + 1
@@ -123,27 +143,37 @@ async def staggered_race(
# up as done() == True, cancelled() == False, exception() ==
# asyncio.CancelledError. This behavior is specified in
# https://bugs.python.org/issue30048
for i, t in enumerate(running_tasks):
if i != this_index:
current_task = tasks.current_task(loop)
for t in running_tasks:
if t is not current_task:
t.cancel()
first_task = loop.create_task(run_one_coro(None))
running_tasks.append(first_task)
propagate_cancellation_error = None
try:
# Wait for a growing list of tasks to all finish: poor man's version of
# curio's TaskGroup or trio's nursery
done_count = 0
while done_count != len(running_tasks):
done, _ = await tasks.wait(running_tasks)
done_count = len(done)
ok_to_start = locks.Event()
first_task = loop.create_task(run_one_coro(ok_to_start, None))
futures.future_add_to_awaited_by(first_task, parent_task)
running_tasks.add(first_task)
first_task.add_done_callback(task_done)
# first_task has been appended to running_tasks so first_task is ok to start.
ok_to_start.set()
propagate_cancellation_error = None
# Make sure no tasks are left running if we leave this function
while running_tasks:
on_completed_fut = loop.create_future()
try:
await on_completed_fut
except exceptions_mod.CancelledError as ex:
propagate_cancellation_error = ex
for task in running_tasks:
task.cancel(*ex.args)
on_completed_fut = None
if __debug__ and unhandled_exceptions:
# If run_one_coro raises an unhandled exception, it's probably a
# programming error, and I want to see it.
if __debug__:
for d in done:
if d.done() and not d.cancelled() and d.exception():
raise d.exception()
raise ExceptionGroup("staggered race failed", unhandled_exceptions)
if propagate_cancellation_error is not None:
raise propagate_cancellation_error
return winner_result, winner_index, exceptions
finally:
# Make sure no tasks are left running if we leave this function
for t in running_tasks:
t.cancel()
del exceptions, propagate_cancellation_error, unhandled_exceptions, parent_task

View File

@@ -201,7 +201,6 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol):
# is established.
self._strong_reader = stream_reader
self._reject_connection = False
self._stream_writer = None
self._task = None
self._transport = None
self._client_connected_cb = client_connected_cb
@@ -214,10 +213,8 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol):
return None
return self._stream_reader_wr()
def _replace_writer(self, writer):
def _replace_transport(self, transport):
loop = self._loop
transport = writer.transport
self._stream_writer = writer
self._transport = transport
self._over_ssl = transport.get_extra_info('sslcontext') is not None
@@ -239,11 +236,8 @@ class StreamReaderProtocol(FlowControlMixin, protocols.Protocol):
reader.set_transport(transport)
self._over_ssl = transport.get_extra_info('sslcontext') is not None
if self._client_connected_cb is not None:
self._stream_writer = StreamWriter(transport, self,
reader,
self._loop)
res = self._client_connected_cb(reader,
self._stream_writer)
writer = StreamWriter(transport, self, reader, self._loop)
res = self._client_connected_cb(reader, writer)
if coroutines.iscoroutine(res):
def callback(task):
if task.cancelled():
@@ -405,9 +399,9 @@ class StreamWriter:
ssl_handshake_timeout=ssl_handshake_timeout,
ssl_shutdown_timeout=ssl_shutdown_timeout)
self._transport = new_transport
protocol._replace_writer(self)
protocol._replace_transport(new_transport)
def __del__(self):
def __del__(self, warnings=warnings):
if not self._transport.is_closing():
if self._loop.is_closed():
warnings.warn("loop is closed", ResourceWarning)
@@ -596,20 +590,34 @@ class StreamReader:
If the data cannot be read because of over limit, a
LimitOverrunError exception will be raised, and the data
will be left in the internal buffer, so it can be read again.
The ``separator`` may also be a tuple of separators. In this
case the return value will be the shortest possible that has any
separator as the suffix. For the purposes of LimitOverrunError,
the shortest possible separator is considered to be the one that
matched.
"""
seplen = len(separator)
if seplen == 0:
if isinstance(separator, tuple):
# Makes sure shortest matches wins
separator = sorted(separator, key=len)
else:
separator = [separator]
if not separator:
raise ValueError('Separator should contain at least one element')
min_seplen = len(separator[0])
max_seplen = len(separator[-1])
if min_seplen == 0:
raise ValueError('Separator should be at least one-byte string')
if self._exception is not None:
raise self._exception
# Consume whole buffer except last bytes, which length is
# one less than seplen. Let's check corner cases with
# separator='SEPARATOR':
# one less than max_seplen. Let's check corner cases with
# separator[-1]='SEPARATOR':
# * we have received almost complete separator (without last
# byte). i.e buffer='some textSEPARATO'. In this case we
# can safely consume len(separator) - 1 bytes.
# can safely consume max_seplen - 1 bytes.
# * last byte of buffer is first byte of separator, i.e.
# buffer='abcdefghijklmnopqrS'. We may safely consume
# everything except that last byte, but this require to
@@ -622,26 +630,35 @@ class StreamReader:
# messages :)
# `offset` is the number of bytes from the beginning of the buffer
# where there is no occurrence of `separator`.
# where there is no occurrence of any `separator`.
offset = 0
# Loop until we find `separator` in the buffer, exceed the buffer size,
# Loop until we find a `separator` in the buffer, exceed the buffer size,
# or an EOF has happened.
while True:
buflen = len(self._buffer)
# Check if we now have enough data in the buffer for `separator` to
# fit.
if buflen - offset >= seplen:
isep = self._buffer.find(separator, offset)
# Check if we now have enough data in the buffer for shortest
# separator to fit.
if buflen - offset >= min_seplen:
match_start = None
match_end = None
for sep in separator:
isep = self._buffer.find(sep, offset)
if isep != -1:
# `separator` is in the buffer. `isep` will be used later
# to retrieve the data.
if isep != -1:
# `separator` is in the buffer. `match_start` and
# `match_end` will be used later to retrieve the
# data.
end = isep + len(sep)
if match_end is None or end < match_end:
match_end = end
match_start = isep
if match_end is not None:
break
# see upper comment for explanation.
offset = buflen + 1 - seplen
offset = max(0, buflen + 1 - max_seplen)
if offset > self._limit:
raise exceptions.LimitOverrunError(
'Separator is not found, and chunk exceed the limit',
@@ -650,7 +667,7 @@ class StreamReader:
# Complete message (with full separator) may be present in buffer
# even when EOF flag is set. This may happen when the last chunk
# adds data which makes separator be found. That's why we check for
# EOF *ater* inspecting the buffer.
# EOF *after* inspecting the buffer.
if self._eof:
chunk = bytes(self._buffer)
self._buffer.clear()
@@ -659,12 +676,12 @@ class StreamReader:
# _wait_for_data() will resume reading if stream was paused.
await self._wait_for_data('readuntil')
if isep > self._limit:
if match_start > self._limit:
raise exceptions.LimitOverrunError(
'Separator is found, but chunk is longer than limit', isep)
'Separator is found, but chunk is longer than limit', match_start)
chunk = self._buffer[:isep + seplen]
del self._buffer[:isep + seplen]
chunk = self._buffer[:match_end]
del self._buffer[:match_end]
self._maybe_resume_transport()
return bytes(chunk)

View File

@@ -6,6 +6,7 @@ __all__ = ("TaskGroup",)
from . import events
from . import exceptions
from . import futures
from . import tasks
@@ -66,6 +67,20 @@ class TaskGroup:
return self
async def __aexit__(self, et, exc, tb):
tb = None
try:
return await self._aexit(et, exc)
finally:
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them. It would be nicer to use a try/finally
# in __aexit__ directly but that introduced some diff noise
self._parent_task = None
self._errors = None
self._base_error = None
exc = None
async def _aexit(self, et, exc):
self._exiting = True
if (exc is not None and
@@ -73,14 +88,10 @@ class TaskGroup:
self._base_error is None):
self._base_error = exc
propagate_cancellation_error = \
exc if et is exceptions.CancelledError else None
if self._parent_cancel_requested:
# If this flag is set we *must* call uncancel().
if self._parent_task.uncancel() == 0:
# If there are no pending cancellations left,
# don't propagate CancelledError.
propagate_cancellation_error = None
if et is not None and issubclass(et, exceptions.CancelledError):
propagate_cancellation_error = exc
else:
propagate_cancellation_error = None
if et is not None:
if not self._aborting:
@@ -126,51 +137,78 @@ class TaskGroup:
assert not self._tasks
if self._base_error is not None:
raise self._base_error
try:
raise self._base_error
finally:
exc = None
if self._parent_cancel_requested:
# If this flag is set we *must* call uncancel().
if self._parent_task.uncancel() == 0:
# If there are no pending cancellations left,
# don't propagate CancelledError.
propagate_cancellation_error = None
# Propagate CancelledError if there is one, except if there
# are other errors -- those have priority.
if propagate_cancellation_error and not self._errors:
raise propagate_cancellation_error
try:
if propagate_cancellation_error is not None and not self._errors:
try:
raise propagate_cancellation_error
finally:
exc = None
finally:
propagate_cancellation_error = None
if et is not None and et is not exceptions.CancelledError:
if et is not None and not issubclass(et, exceptions.CancelledError):
self._errors.append(exc)
if self._errors:
# Exceptions are heavy objects that can have object
# cycles (bad for GC); let's not keep a reference to
# a bunch of them.
# If the parent task is being cancelled from the outside
# of the taskgroup, un-cancel and re-cancel the parent task,
# which will keep the cancel count stable.
if self._parent_task.cancelling():
self._parent_task.uncancel()
self._parent_task.cancel()
try:
me = BaseExceptionGroup('unhandled errors in a TaskGroup', self._errors)
raise me from None
raise BaseExceptionGroup(
'unhandled errors in a TaskGroup',
self._errors,
) from None
finally:
self._errors = None
exc = None
def create_task(self, coro, *, name=None, context=None):
def create_task(self, coro, **kwargs):
"""Create a new task in this group and return it.
Similar to `asyncio.create_task`.
"""
if not self._entered:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} has not been entered")
if self._exiting and not self._tasks:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} is finished")
if self._aborting:
coro.close()
raise RuntimeError(f"TaskGroup {self!r} is shutting down")
if context is None:
task = self._loop.create_task(coro)
else:
task = self._loop.create_task(coro, context=context)
tasks._set_task_name(task, name)
# optimization: Immediately call the done callback if the task is
task = self._loop.create_task(coro, **kwargs)
futures.future_add_to_awaited_by(task, self._parent_task)
# Always schedule the done callback even if the task is
# already done (e.g. if the coro was able to complete eagerly),
# and skip scheduling a done callback
if task.done():
self._on_task_done(task)
else:
self._tasks.add(task)
task.add_done_callback(self._on_task_done)
return task
# otherwise if the task completes with an exception then it will cancel
# the current task too early. gh-128550, gh-128588
self._tasks.add(task)
task.add_done_callback(self._on_task_done)
try:
return task
finally:
# gh-128552: prevent a refcycle of
# task.exception().__traceback__->TaskGroup.create_task->task
del task
# Since Python 3.8 Tasks propagate all exceptions correctly,
# except for KeyboardInterrupt and SystemExit which are
@@ -190,6 +228,8 @@ class TaskGroup:
def _on_task_done(self, task):
self._tasks.discard(task)
futures.future_discard_from_awaited_by(task, self._parent_task)
if self._on_completed_fut is not None and not self._tasks:
if not self._on_completed_fut.done():
self._on_completed_fut.set_result(True)

298
Lib/asyncio/tasks.py vendored
View File

@@ -15,8 +15,8 @@ import contextvars
import functools
import inspect
import itertools
import math
import types
import warnings
import weakref
from types import GenericAlias
@@ -25,6 +25,7 @@ from . import coroutines
from . import events
from . import exceptions
from . import futures
from . import queues
from . import timeouts
# Helper to generate new task names
@@ -47,39 +48,11 @@ def all_tasks(loop=None):
# capturing the set of eager tasks first, so if an eager task "graduates"
# to a regular task in another thread, we don't risk missing it.
eager_tasks = list(_eager_tasks)
# Looping over the WeakSet isn't safe as it can be updated from another
# thread, therefore we cast it to list prior to filtering. The list cast
# itself requires iteration, so we repeat it several times ignoring
# RuntimeErrors (which are not very likely to occur).
# See issues 34970 and 36607 for details.
scheduled_tasks = None
i = 0
while True:
try:
scheduled_tasks = list(_scheduled_tasks)
except RuntimeError:
i += 1
if i >= 1000:
raise
else:
break
return {t for t in itertools.chain(scheduled_tasks, eager_tasks)
return {t for t in itertools.chain(_scheduled_tasks, eager_tasks)
if futures._get_loop(t) is loop and not t.done()}
def _set_task_name(task, name):
if name is not None:
try:
set_name = task.set_name
except AttributeError:
warnings.warn("Task.set_name() was added in Python 3.8, "
"the method support will be mandatory for third-party "
"task implementations since 3.13.",
DeprecationWarning, stacklevel=3)
else:
set_name(name)
class Task(futures._PyFuture): # Inherit Python Task implementation
# from a Python Future implementation.
@@ -137,7 +110,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
self.__eager_start()
else:
self._loop.call_soon(self.__step, context=self._context)
_register_task(self)
_py_register_task(self)
def __del__(self):
if self._state == futures._PENDING and self._log_destroy_pending:
@@ -267,42 +240,44 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
"""
if self._num_cancels_requested > 0:
self._num_cancels_requested -= 1
if self._num_cancels_requested == 0:
self._must_cancel = False
return self._num_cancels_requested
def __eager_start(self):
prev_task = _swap_current_task(self._loop, self)
prev_task = _py_swap_current_task(self._loop, self)
try:
_register_eager_task(self)
_py_register_eager_task(self)
try:
self._context.run(self.__step_run_and_handle_result, None)
finally:
_unregister_eager_task(self)
_py_unregister_eager_task(self)
finally:
try:
curtask = _swap_current_task(self._loop, prev_task)
curtask = _py_swap_current_task(self._loop, prev_task)
assert curtask is self
finally:
if self.done():
self._coro = None
self = None # Needed to break cycles when an exception occurs.
else:
_register_task(self)
_py_register_task(self)
def __step(self, exc=None):
if self.done():
raise exceptions.InvalidStateError(
f'_step(): already done: {self!r}, {exc!r}')
f'__step(): already done: {self!r}, {exc!r}')
if self._must_cancel:
if not isinstance(exc, exceptions.CancelledError):
exc = self._make_cancelled_error()
self._must_cancel = False
self._fut_waiter = None
_enter_task(self._loop, self)
_py_enter_task(self._loop, self)
try:
self.__step_run_and_handle_result(exc)
finally:
_leave_task(self._loop, self)
_py_leave_task(self._loop, self)
self = None # Needed to break cycles when an exception occurs.
def __step_run_and_handle_result(self, exc):
@@ -347,6 +322,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
self._loop.call_soon(
self.__step, new_exc, context=self._context)
else:
futures.future_add_to_awaited_by(result, self)
result._asyncio_future_blocking = False
result.add_done_callback(
self.__wakeup, context=self._context)
@@ -381,6 +357,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
self = None # Needed to break cycles when an exception occurs.
def __wakeup(self, future):
futures.future_discard_from_awaited_by(future, self)
try:
future.result()
except BaseException as exc:
@@ -389,7 +366,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
else:
# Don't pass the value of `future.result()` explicitly,
# as `Future.__iter__` and `Future.__await__` don't need it.
# If we call `_step(value, None)` instead of `_step()`,
# If we call `__step(value, None)` instead of `__step()`,
# Python eval loop would use `.send(value)` method call,
# instead of `__next__()`, which is slower for futures
# that return non-generator iterators from their `__iter__`.
@@ -409,20 +386,13 @@ else:
Task = _CTask = _asyncio.Task
def create_task(coro, *, name=None, context=None):
def create_task(coro, **kwargs):
"""Schedule the execution of a coroutine object in a spawn task.
Return a Task object.
"""
loop = events.get_running_loop()
if context is None:
# Use legacy API if context is not needed
task = loop.create_task(coro)
else:
task = loop.create_task(coro, context=context)
_set_task_name(task, name)
return task
return loop.create_task(coro, **kwargs)
# wait() and as_completed() similar to those in PEP 3148.
@@ -437,8 +407,6 @@ async def wait(fs, *, timeout=None, return_when=ALL_COMPLETED):
The fs iterable must not be empty.
Coroutines will be wrapped in Tasks.
Returns two sets of Future: (done, pending).
Usage:
@@ -530,6 +498,7 @@ async def _wait(fs, timeout, return_when, loop):
if timeout is not None:
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
counter = len(fs)
cur_task = current_task()
def _on_completion(f):
nonlocal counter
@@ -542,9 +511,11 @@ async def _wait(fs, timeout, return_when, loop):
timeout_handle.cancel()
if not waiter.done():
waiter.set_result(None)
futures.future_discard_from_awaited_by(f, cur_task)
for f in fs:
f.add_done_callback(_on_completion)
futures.future_add_to_awaited_by(f, cur_task)
try:
await waiter
@@ -580,62 +551,125 @@ async def _cancel_and_wait(fut):
fut.remove_done_callback(cb)
# This is *not* a @coroutine! It is just an iterator (yielding Futures).
def as_completed(fs, *, timeout=None):
"""Return an iterator whose values are coroutines.
class _AsCompletedIterator:
"""Iterator of awaitables representing tasks of asyncio.as_completed.
When waiting for the yielded coroutines you'll get the results (or
exceptions!) of the original Futures (or coroutines), in the order
in which and as soon as they complete.
This differs from PEP 3148; the proper way to use this is:
for f in as_completed(fs):
result = await f # The 'await' may raise.
# Use result.
If a timeout is specified, the 'await' will raise
TimeoutError when the timeout occurs before all Futures are done.
Note: The futures 'f' are not necessarily members of fs.
As an asynchronous iterator, iteration yields futures as they finish. As a
plain iterator, new coroutines are yielded that will return or raise the
result of the next underlying future to complete.
"""
if futures.isfuture(fs) or coroutines.iscoroutine(fs):
raise TypeError(f"expect an iterable of futures, not {type(fs).__name__}")
def __init__(self, aws, timeout):
self._done = queues.Queue()
self._timeout_handle = None
from .queues import Queue # Import here to avoid circular import problem.
done = Queue()
loop = events.get_event_loop()
todo = {ensure_future(f, loop=loop) for f in set(fs)}
timeout_handle = None
def _on_timeout():
loop = events.get_event_loop()
todo = {ensure_future(aw, loop=loop) for aw in set(aws)}
for f in todo:
f.remove_done_callback(_on_completion)
done.put_nowait(None) # Queue a dummy value for _wait_for_one().
todo.clear() # Can't do todo.remove(f) in the loop.
f.add_done_callback(self._handle_completion)
if todo and timeout is not None:
self._timeout_handle = (
loop.call_later(timeout, self._handle_timeout)
)
self._todo = todo
self._todo_left = len(todo)
def _on_completion(f):
if not todo:
return # _on_timeout() was here first.
todo.remove(f)
done.put_nowait(f)
if not todo and timeout_handle is not None:
timeout_handle.cancel()
def __aiter__(self):
return self
async def _wait_for_one():
f = await done.get()
def __iter__(self):
return self
async def __anext__(self):
if not self._todo_left:
raise StopAsyncIteration
assert self._todo_left > 0
self._todo_left -= 1
return await self._wait_for_one()
def __next__(self):
if not self._todo_left:
raise StopIteration
assert self._todo_left > 0
self._todo_left -= 1
return self._wait_for_one(resolve=True)
def _handle_timeout(self):
for f in self._todo:
f.remove_done_callback(self._handle_completion)
self._done.put_nowait(None) # Sentinel for _wait_for_one().
self._todo.clear() # Can't do todo.remove(f) in the loop.
def _handle_completion(self, f):
if not self._todo:
return # _handle_timeout() was here first.
self._todo.remove(f)
self._done.put_nowait(f)
if not self._todo and self._timeout_handle is not None:
self._timeout_handle.cancel()
async def _wait_for_one(self, resolve=False):
# Wait for the next future to be done and return it unless resolve is
# set, in which case return either the result of the future or raise
# an exception.
f = await self._done.get()
if f is None:
# Dummy value from _on_timeout().
# Dummy value from _handle_timeout().
raise exceptions.TimeoutError
return f.result() # May raise f.exception().
return f.result() if resolve else f
for f in todo:
f.add_done_callback(_on_completion)
if todo and timeout is not None:
timeout_handle = loop.call_later(timeout, _on_timeout)
for _ in range(len(todo)):
yield _wait_for_one()
def as_completed(fs, *, timeout=None):
"""Create an iterator of awaitables or their results in completion order.
Run the supplied awaitables concurrently. The returned object can be
iterated to obtain the results of the awaitables as they finish.
The object returned can be iterated as an asynchronous iterator or a plain
iterator. When asynchronous iteration is used, the originally-supplied
awaitables are yielded if they are tasks or futures. This makes it easy to
correlate previously-scheduled tasks with their results:
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
tasks = [ipv4_connect, ipv6_connect]
async for earliest_connect in as_completed(tasks):
# earliest_connect is done. The result can be obtained by
# awaiting it or calling earliest_connect.result()
reader, writer = await earliest_connect
if earliest_connect is ipv6_connect:
print("IPv6 connection established.")
else:
print("IPv4 connection established.")
During asynchronous iteration, implicitly-created tasks will be yielded for
supplied awaitables that aren't tasks or futures.
When used as a plain iterator, each iteration yields a new coroutine that
returns the result or raises the exception of the next completed awaitable.
This pattern is compatible with Python versions older than 3.13:
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
tasks = [ipv4_connect, ipv6_connect]
for next_connect in as_completed(tasks):
# next_connect is not one of the original task objects. It must be
# awaited to obtain the result value or raise the exception of the
# awaitable that finishes next.
reader, writer = await next_connect
A TimeoutError is raised if the timeout occurs before all awaitables are
done. This is raised by the async for loop during asynchronous iteration or
by the coroutines yielded during plain iteration.
"""
if inspect.isawaitable(fs):
raise TypeError(
f"expects an iterable of awaitables, not {type(fs).__name__}"
)
return _AsCompletedIterator(fs, timeout)
@types.coroutine
@@ -656,6 +690,9 @@ async def sleep(delay, result=None):
await __sleep0()
return result
if math.isnan(delay):
raise ValueError("Invalid delay: NaN (not a number)")
loop = events.get_running_loop()
future = loop.create_future()
h = loop.call_later(delay,
@@ -764,10 +801,19 @@ def gather(*coros_or_futures, return_exceptions=False):
outer.set_result([])
return outer
def _done_callback(fut):
loop = events._get_running_loop()
if loop is not None:
cur_task = current_task(loop)
else:
cur_task = None
def _done_callback(fut, cur_task=cur_task):
nonlocal nfinished
nfinished += 1
if cur_task is not None:
futures.future_discard_from_awaited_by(fut, cur_task)
if outer is None or outer.done():
if not fut.cancelled():
# Mark exception retrieved.
@@ -824,7 +870,6 @@ def gather(*coros_or_futures, return_exceptions=False):
nfuts = 0
nfinished = 0
done_futs = []
loop = None
outer = None # bpo-46672
for arg in coros_or_futures:
if arg not in arg_to_fut:
@@ -837,12 +882,13 @@ def gather(*coros_or_futures, return_exceptions=False):
# can't control it, disable the "destroy pending task"
# warning.
fut._log_destroy_pending = False
nfuts += 1
arg_to_fut[arg] = fut
if fut.done():
done_futs.append(fut)
else:
if cur_task is not None:
futures.future_add_to_awaited_by(fut, cur_task)
fut.add_done_callback(_done_callback)
else:
@@ -862,6 +908,25 @@ def gather(*coros_or_futures, return_exceptions=False):
return outer
def _log_on_exception(fut):
if fut.cancelled():
return
exc = fut.exception()
if exc is None:
return
context = {
'message':
f'{exc.__class__.__name__} exception in shielded future',
'exception': exc,
'future': fut,
}
if fut._source_traceback:
context['source_traceback'] = fut._source_traceback
fut._loop.call_exception_handler(context)
def shield(arg):
"""Wait for a future, shielding it from cancellation.
@@ -902,11 +967,16 @@ def shield(arg):
loop = futures._get_loop(inner)
outer = loop.create_future()
if loop is not None and (cur_task := current_task(loop)) is not None:
futures.future_add_to_awaited_by(inner, cur_task)
else:
cur_task = None
def _clear_awaited_by_callback(inner):
futures.future_discard_from_awaited_by(inner, cur_task)
def _inner_done_callback(inner):
if outer.cancelled():
if not inner.cancelled():
# Mark inner's result as retrieved.
inner.exception()
return
if inner.cancelled():
@@ -918,10 +988,16 @@ def shield(arg):
else:
outer.set_result(inner.result())
def _outer_done_callback(outer):
if not inner.done():
inner.remove_done_callback(_inner_done_callback)
# Keep only one callback to log on cancel
inner.remove_done_callback(_log_on_exception)
inner.add_done_callback(_log_on_exception)
if cur_task is not None:
inner.add_done_callback(_clear_awaited_by_callback)
inner.add_done_callback(_inner_done_callback)
outer.add_done_callback(_outer_done_callback)
@@ -970,9 +1046,9 @@ def create_eager_task_factory(custom_task_constructor):
used. E.g. `loop.set_task_factory(asyncio.eager_task_factory)`.
"""
def factory(loop, coro, *, name=None, context=None):
def factory(loop, coro, *, eager_start=True, **kwargs):
return custom_task_constructor(
coro, loop=loop, name=name, context=context, eager_start=True)
coro, loop=loop, eager_start=eager_start, **kwargs)
return factory
@@ -1044,14 +1120,13 @@ _py_unregister_eager_task = _unregister_eager_task
_py_enter_task = _enter_task
_py_leave_task = _leave_task
_py_swap_current_task = _swap_current_task
_py_all_tasks = all_tasks
try:
from _asyncio import (_register_task, _register_eager_task,
_unregister_task, _unregister_eager_task,
_enter_task, _leave_task, _swap_current_task,
_scheduled_tasks, _eager_tasks, _current_tasks,
current_task)
current_task, all_tasks)
except ImportError:
pass
else:
@@ -1063,3 +1138,4 @@ else:
_c_enter_task = _enter_task
_c_leave_task = _leave_task
_c_swap_current_task = _swap_current_task
_c_all_tasks = all_tasks

View File

@@ -1,7 +1,6 @@
import enum
from types import TracebackType
from typing import final, Optional, Type
from . import events
from . import exceptions
@@ -23,14 +22,13 @@ class _State(enum.Enum):
EXITED = "finished"
@final
class Timeout:
"""Asynchronous context manager for cancelling overdue coroutines.
Use `timeout()` or `timeout_at()` rather than instantiating this class directly.
"""
def __init__(self, when: Optional[float]) -> None:
def __init__(self, when: float | None) -> None:
"""Schedule a timeout that will trigger at a given loop time.
- If `when` is `None`, the timeout will never trigger.
@@ -39,15 +37,15 @@ class Timeout:
"""
self._state = _State.CREATED
self._timeout_handler: Optional[events.TimerHandle] = None
self._task: Optional[tasks.Task] = None
self._timeout_handler: events.TimerHandle | None = None
self._task: tasks.Task | None = None
self._when = when
def when(self) -> Optional[float]:
def when(self) -> float | None:
"""Return the current deadline."""
return self._when
def reschedule(self, when: Optional[float]) -> None:
def reschedule(self, when: float | None) -> None:
"""Reschedule the timeout."""
if self._state is not _State.ENTERED:
if self._state is _State.CREATED:
@@ -96,10 +94,10 @@ class Timeout:
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
) -> Optional[bool]:
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> bool | None:
assert self._state in (_State.ENTERED, _State.EXPIRING)
if self._timeout_handler is not None:
@@ -109,10 +107,16 @@ class Timeout:
if self._state is _State.EXPIRING:
self._state = _State.EXPIRED
if self._task.uncancel() <= self._cancelling and exc_type is exceptions.CancelledError:
if self._task.uncancel() <= self._cancelling and exc_type is not None:
# Since there are no new cancel requests, we're
# handling this.
raise TimeoutError from exc_val
if issubclass(exc_type, exceptions.CancelledError):
raise TimeoutError from exc_val
elif exc_val is not None:
self._insert_timeout_error(exc_val)
if isinstance(exc_val, ExceptionGroup):
for exc in exc_val.exceptions:
self._insert_timeout_error(exc)
elif self._state is _State.ENTERED:
self._state = _State.EXITED
@@ -125,8 +129,18 @@ class Timeout:
# drop the reference early
self._timeout_handler = None
@staticmethod
def _insert_timeout_error(exc_val: BaseException) -> None:
while exc_val.__context__ is not None:
if isinstance(exc_val.__context__, exceptions.CancelledError):
te = TimeoutError()
te.__context__ = te.__cause__ = exc_val.__context__
exc_val.__context__ = te
break
exc_val = exc_val.__context__
def timeout(delay: Optional[float]) -> Timeout:
def timeout(delay: float | None) -> Timeout:
"""Timeout async context manager.
Useful in cases when you want to apply timeout logic around block
@@ -146,7 +160,7 @@ def timeout(delay: Optional[float]) -> Timeout:
return Timeout(loop.time() + delay if delay is not None else None)
def timeout_at(when: Optional[float]) -> Timeout:
def timeout_at(when: float | None) -> Timeout:
"""Schedule the timeout at absolute time.
Like timeout() but argument gives absolute time in the same clock system

276
Lib/asyncio/tools.py vendored Normal file
View File

@@ -0,0 +1,276 @@
"""Tools to analyze tasks running in asyncio programs."""
from collections import defaultdict, namedtuple
from itertools import count
from enum import Enum
import sys
from _remote_debugging import RemoteUnwinder, FrameInfo
class NodeType(Enum):
COROUTINE = 1
TASK = 2
class CycleFoundException(Exception):
"""Raised when there is a cycle when drawing the call tree."""
def __init__(
self,
cycles: list[list[int]],
id2name: dict[int, str],
) -> None:
super().__init__(cycles, id2name)
self.cycles = cycles
self.id2name = id2name
# ─── indexing helpers ───────────────────────────────────────────
def _format_stack_entry(elem: str|FrameInfo) -> str:
if not isinstance(elem, str):
if elem.lineno == 0 and elem.filename == "":
return f"{elem.funcname}"
else:
return f"{elem.funcname} {elem.filename}:{elem.lineno}"
return elem
def _index(result):
id2name, awaits, task_stacks = {}, [], {}
for awaited_info in result:
for task_info in awaited_info.awaited_by:
task_id = task_info.task_id
task_name = task_info.task_name
id2name[task_id] = task_name
# Store the internal coroutine stack for this task
if task_info.coroutine_stack:
for coro_info in task_info.coroutine_stack:
call_stack = coro_info.call_stack
internal_stack = [_format_stack_entry(frame) for frame in call_stack]
task_stacks[task_id] = internal_stack
# Add the awaited_by relationships (external dependencies)
if task_info.awaited_by:
for coro_info in task_info.awaited_by:
call_stack = coro_info.call_stack
parent_task_id = coro_info.task_name
stack = [_format_stack_entry(frame) for frame in call_stack]
awaits.append((parent_task_id, stack, task_id))
return id2name, awaits, task_stacks
def _build_tree(id2name, awaits, task_stacks):
id2label = {(NodeType.TASK, tid): name for tid, name in id2name.items()}
children = defaultdict(list)
cor_nodes = defaultdict(dict) # Maps parent -> {frame_name: node_key}
next_cor_id = count(1)
def get_or_create_cor_node(parent, frame):
"""Get existing coroutine node or create new one under parent"""
if frame in cor_nodes[parent]:
return cor_nodes[parent][frame]
node_key = (NodeType.COROUTINE, f"c{next(next_cor_id)}")
id2label[node_key] = frame
children[parent].append(node_key)
cor_nodes[parent][frame] = node_key
return node_key
# Build task dependency tree with coroutine frames
for parent_id, stack, child_id in awaits:
cur = (NodeType.TASK, parent_id)
for frame in reversed(stack):
cur = get_or_create_cor_node(cur, frame)
child_key = (NodeType.TASK, child_id)
if child_key not in children[cur]:
children[cur].append(child_key)
# Add coroutine stacks for leaf tasks
awaiting_tasks = {parent_id for parent_id, _, _ in awaits}
for task_id in id2name:
if task_id not in awaiting_tasks and task_id in task_stacks:
cur = (NodeType.TASK, task_id)
for frame in reversed(task_stacks[task_id]):
cur = get_or_create_cor_node(cur, frame)
return id2label, children
def _roots(id2label, children):
all_children = {c for kids in children.values() for c in kids}
return [n for n in id2label if n not in all_children]
# ─── detect cycles in the task-to-task graph ───────────────────────
def _task_graph(awaits):
"""Return {parent_task_id: {child_task_id, …}, …}."""
g = defaultdict(set)
for parent_id, _stack, child_id in awaits:
g[parent_id].add(child_id)
return g
def _find_cycles(graph):
"""
Depth-first search for back-edges.
Returns a list of cycles (each cycle is a list of task-ids) or an
empty list if the graph is acyclic.
"""
WHITE, GREY, BLACK = 0, 1, 2
color = defaultdict(lambda: WHITE)
path, cycles = [], []
def dfs(v):
color[v] = GREY
path.append(v)
for w in graph.get(v, ()):
if color[w] == WHITE:
dfs(w)
elif color[w] == GREY: # back-edge → cycle!
i = path.index(w)
cycles.append(path[i:] + [w]) # make a copy
color[v] = BLACK
path.pop()
for v in list(graph):
if color[v] == WHITE:
dfs(v)
return cycles
# ─── PRINT TREE FUNCTION ───────────────────────────────────────
def get_all_awaited_by(pid):
unwinder = RemoteUnwinder(pid)
return unwinder.get_all_awaited_by()
def build_async_tree(result, task_emoji="(T)", cor_emoji=""):
"""
Build a list of strings for pretty-print an async call tree.
The call tree is produced by `get_all_async_stacks()`, prefixing tasks
with `task_emoji` and coroutine frames with `cor_emoji`.
"""
id2name, awaits, task_stacks = _index(result)
g = _task_graph(awaits)
cycles = _find_cycles(g)
if cycles:
raise CycleFoundException(cycles, id2name)
labels, children = _build_tree(id2name, awaits, task_stacks)
def pretty(node):
flag = task_emoji if node[0] == NodeType.TASK else cor_emoji
return f"{flag} {labels[node]}"
def render(node, prefix="", last=True, buf=None):
if buf is None:
buf = []
buf.append(f"{prefix}{'└── ' if last else '├── '}{pretty(node)}")
new_pref = prefix + (" " if last else "")
kids = children.get(node, [])
for i, kid in enumerate(kids):
render(kid, new_pref, i == len(kids) - 1, buf)
return buf
return [render(root) for root in _roots(labels, children)]
def build_task_table(result):
id2name, _, _ = _index(result)
table = []
for awaited_info in result:
thread_id = awaited_info.thread_id
for task_info in awaited_info.awaited_by:
# Get task info
task_id = task_info.task_id
task_name = task_info.task_name
# Build coroutine stack string
frames = [frame for coro in task_info.coroutine_stack
for frame in coro.call_stack]
coro_stack = " -> ".join(_format_stack_entry(x).split(" ")[0]
for x in frames)
# Handle tasks with no awaiters
if not task_info.awaited_by:
table.append([thread_id, hex(task_id), task_name, coro_stack,
"", "", "0x0"])
continue
# Handle tasks with awaiters
for coro_info in task_info.awaited_by:
parent_id = coro_info.task_name
awaiter_frames = [_format_stack_entry(x).split(" ")[0]
for x in coro_info.call_stack]
awaiter_chain = " -> ".join(awaiter_frames)
awaiter_name = id2name.get(parent_id, "Unknown")
parent_id_str = (hex(parent_id) if isinstance(parent_id, int)
else str(parent_id))
table.append([thread_id, hex(task_id), task_name, coro_stack,
awaiter_chain, awaiter_name, parent_id_str])
return table
def _print_cycle_exception(exception: CycleFoundException):
print("ERROR: await-graph contains cycles - cannot print a tree!", file=sys.stderr)
print("", file=sys.stderr)
for c in exception.cycles:
inames = "".join(exception.id2name.get(tid, hex(tid)) for tid in c)
print(f"cycle: {inames}", file=sys.stderr)
def exit_with_permission_help_text():
"""
Prints a message pointing to platform-specific permission help text and exits the program.
This function is called when a PermissionError is encountered while trying
to attach to a process.
"""
print(
"Error: The specified process cannot be attached to due to insufficient permissions.\n"
"See the Python documentation for details on required privileges and troubleshooting:\n"
"https://docs.python.org/3.14/howto/remote_debugging.html#permission-requirements\n"
)
sys.exit(1)
def _get_awaited_by_tasks(pid: int) -> list:
try:
return get_all_awaited_by(pid)
except RuntimeError as e:
while e.__context__ is not None:
e = e.__context__
print(f"Error retrieving tasks: {e}")
sys.exit(1)
except PermissionError as e:
exit_with_permission_help_text()
def display_awaited_by_tasks_table(pid: int) -> None:
"""Build and print a table of all pending tasks under `pid`."""
tasks = _get_awaited_by_tasks(pid)
table = build_task_table(tasks)
# Print the table in a simple tabular format
print(
f"{'tid':<10} {'task id':<20} {'task name':<20} {'coroutine stack':<50} {'awaiter chain':<50} {'awaiter name':<15} {'awaiter id':<15}"
)
print("-" * 180)
for row in table:
print(f"{row[0]:<10} {row[1]:<20} {row[2]:<20} {row[3]:<50} {row[4]:<50} {row[5]:<15} {row[6]:<15}")
def display_awaited_by_tasks_tree(pid: int) -> None:
"""Build and print a tree of all pending tasks under `pid`."""
tasks = _get_awaited_by_tasks(pid)
try:
result = build_async_tree(tasks)
except CycleFoundException as e:
_print_cycle_exception(e)
sys.exit(1)
for tree in result:
print("\n".join(tree))

View File

@@ -181,6 +181,8 @@ class DatagramTransport(BaseTransport):
to be sent out asynchronously.
addr is target socket address.
If addr is None use target address pointed on transport creation.
If data is an empty bytes object a zero-length datagram will be
sent.
"""
raise NotImplementedError

View File

@@ -28,10 +28,7 @@ from .log import logger
__all__ = (
'SelectorEventLoop',
'AbstractChildWatcher', 'SafeChildWatcher',
'FastChildWatcher', 'PidfdChildWatcher',
'MultiLoopChildWatcher', 'ThreadedChildWatcher',
'DefaultEventLoopPolicy',
'EventLoop',
)
@@ -63,6 +60,11 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
def __init__(self, selector=None):
super().__init__(selector)
self._signal_handlers = {}
self._unix_server_sockets = {}
if can_use_pidfd():
self._watcher = _PidfdChildWatcher()
else:
self._watcher = _ThreadedChildWatcher()
def close(self):
super().close()
@@ -92,7 +94,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
Raise RuntimeError if there is a problem setting up the handler.
"""
if (coroutines.iscoroutine(callback) or
coroutines.iscoroutinefunction(callback)):
coroutines._iscoroutinefunction(callback)):
raise TypeError("coroutines cannot be used "
"with add_signal_handler()")
self._check_signal(sig)
@@ -195,33 +197,22 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
async def _make_subprocess_transport(self, protocol, args, shell,
stdin, stdout, stderr, bufsize,
extra=None, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
watcher = events.get_child_watcher()
with watcher:
if not watcher.is_active():
# Check early.
# Raising exception before process creation
# prevents subprocess execution if the watcher
# is not ready to handle it.
raise RuntimeError("asyncio.get_child_watcher() is not activated, "
"subprocess support is not installed.")
waiter = self.create_future()
transp = _UnixSubprocessTransport(self, protocol, args, shell,
stdin, stdout, stderr, bufsize,
waiter=waiter, extra=extra,
**kwargs)
watcher.add_child_handler(transp.get_pid(),
self._child_watcher_callback, transp)
try:
await waiter
except (SystemExit, KeyboardInterrupt):
raise
except BaseException:
transp.close()
await transp._wait()
raise
watcher = self._watcher
waiter = self.create_future()
transp = _UnixSubprocessTransport(self, protocol, args, shell,
stdin, stdout, stderr, bufsize,
waiter=waiter, extra=extra,
**kwargs)
watcher.add_child_handler(transp.get_pid(),
self._child_watcher_callback, transp)
try:
await waiter
except (SystemExit, KeyboardInterrupt):
raise
except BaseException:
transp.close()
await transp._wait()
raise
return transp
@@ -283,7 +274,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
sock=None, backlog=100, ssl=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
start_serving=True):
start_serving=True, cleanup_socket=True):
if isinstance(ssl, bool):
raise TypeError('ssl argument must be an SSLContext or None')
@@ -339,6 +330,15 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
raise ValueError(
f'A UNIX Domain Stream Socket was expected, got {sock!r}')
if cleanup_socket:
path = sock.getsockname()
# Check for abstract socket. `str` and `bytes` paths are supported.
if path[0] not in (0, '\x00'):
try:
self._unix_server_sockets[sock] = os.stat(path).st_ino
except FileNotFoundError:
pass
sock.setblocking(False)
server = base_events.Server(self, [sock], protocol_factory,
ssl, backlog, ssl_handshake_timeout,
@@ -393,6 +393,9 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
fut.set_result(total_sent)
return
# On 32-bit architectures truncate to 1GiB to avoid OverflowError
blocksize = min(blocksize, sys.maxsize//2 + 1)
try:
sent = os.sendfile(fd, fileno, offset, blocksize)
except (BlockingIOError, InterruptedError):
@@ -456,6 +459,27 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
self.remove_writer(fd)
fut.add_done_callback(cb)
def _stop_serving(self, sock):
# Is this a unix socket that needs cleanup?
if sock in self._unix_server_sockets:
path = sock.getsockname()
else:
path = None
super()._stop_serving(sock)
if path is not None:
prev_ino = self._unix_server_sockets[sock]
del self._unix_server_sockets[sock]
try:
if os.stat(path).st_ino == prev_ino:
os.unlink(path)
except FileNotFoundError:
pass
except OSError as err:
logger.error('Unable to clean up listening UNIX socket '
'%r: %r', path, err)
class _UnixReadPipeTransport(transports.ReadTransport):
@@ -830,93 +854,7 @@ class _UnixSubprocessTransport(base_subprocess.BaseSubprocessTransport):
stdin_w.close()
class AbstractChildWatcher:
"""Abstract base class for monitoring child processes.
Objects derived from this class monitor a collection of subprocesses and
report their termination or interruption by a signal.
New callbacks are registered with .add_child_handler(). Starting a new
process must be done within a 'with' block to allow the watcher to suspend
its activity until the new process if fully registered (this is needed to
prevent a race condition in some implementations).
Example:
with watcher:
proc = subprocess.Popen("sleep 1")
watcher.add_child_handler(proc.pid, callback)
Notes:
Implementations of this class must be thread-safe.
Since child watcher objects may catch the SIGCHLD signal and call
waitpid(-1), there should be only one active object per process.
"""
def __init_subclass__(cls) -> None:
if cls.__module__ != __name__:
warnings._deprecated("AbstractChildWatcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.",
remove=(3, 14))
def add_child_handler(self, pid, callback, *args):
"""Register a new child handler.
Arrange for callback(pid, returncode, *args) to be called when
process 'pid' terminates. Specifying another callback for the same
process replaces the previous handler.
Note: callback() must be thread-safe.
"""
raise NotImplementedError()
def remove_child_handler(self, pid):
"""Removes the handler for process 'pid'.
The function returns True if the handler was successfully removed,
False if there was nothing to remove."""
raise NotImplementedError()
def attach_loop(self, loop):
"""Attach the watcher to an event loop.
If the watcher was previously attached to an event loop, then it is
first detached before attaching to the new loop.
Note: loop may be None.
"""
raise NotImplementedError()
def close(self):
"""Close the watcher.
This must be called to make sure that any underlying resource is freed.
"""
raise NotImplementedError()
def is_active(self):
"""Return ``True`` if the watcher is active and is used by the event loop.
Return True if the watcher is installed and ready to handle process exit
notifications.
"""
raise NotImplementedError()
def __enter__(self):
"""Enter the watcher's context and allow starting new processes
This function must return self"""
raise NotImplementedError()
def __exit__(self, a, b, c):
"""Exit the watcher's context"""
raise NotImplementedError()
class PidfdChildWatcher(AbstractChildWatcher):
class _PidfdChildWatcher:
"""Child watcher implementation using Linux's pid file descriptors.
This child watcher polls process file descriptors (pidfds) to await child
@@ -928,21 +866,6 @@ class PidfdChildWatcher(AbstractChildWatcher):
recent (5.3+) kernels.
"""
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
pass
def is_active(self):
return True
def close(self):
pass
def attach_loop(self, loop):
pass
def add_child_handler(self, pid, callback, *args):
loop = events.get_running_loop()
pidfd = os.pidfd_open(pid)
@@ -967,386 +890,7 @@ class PidfdChildWatcher(AbstractChildWatcher):
os.close(pidfd)
callback(pid, returncode, *args)
def remove_child_handler(self, pid):
# asyncio never calls remove_child_handler() !!!
# The method is no-op but is implemented because
# abstract base classes require it.
return True
class BaseChildWatcher(AbstractChildWatcher):
def __init__(self):
self._loop = None
self._callbacks = {}
def close(self):
self.attach_loop(None)
def is_active(self):
return self._loop is not None and self._loop.is_running()
def _do_waitpid(self, expected_pid):
raise NotImplementedError()
def _do_waitpid_all(self):
raise NotImplementedError()
def attach_loop(self, loop):
assert loop is None or isinstance(loop, events.AbstractEventLoop)
if self._loop is not None and loop is None and self._callbacks:
warnings.warn(
'A loop is being detached '
'from a child watcher with pending handlers',
RuntimeWarning)
if self._loop is not None:
self._loop.remove_signal_handler(signal.SIGCHLD)
self._loop = loop
if loop is not None:
loop.add_signal_handler(signal.SIGCHLD, self._sig_chld)
# Prevent a race condition in case a child terminated
# during the switch.
self._do_waitpid_all()
def _sig_chld(self):
try:
self._do_waitpid_all()
except (SystemExit, KeyboardInterrupt):
raise
except BaseException as exc:
# self._loop should always be available here
# as '_sig_chld' is added as a signal handler
# in 'attach_loop'
self._loop.call_exception_handler({
'message': 'Unknown exception in SIGCHLD handler',
'exception': exc,
})
class SafeChildWatcher(BaseChildWatcher):
"""'Safe' child watcher implementation.
This implementation avoids disrupting other code spawning processes by
polling explicitly each process in the SIGCHLD handler instead of calling
os.waitpid(-1).
This is a safe solution but it has a significant overhead when handling a
big number of children (O(n) each time SIGCHLD is raised)
"""
def __init__(self):
super().__init__()
warnings._deprecated("SafeChildWatcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.",
remove=(3, 14))
def close(self):
self._callbacks.clear()
super().close()
def __enter__(self):
return self
def __exit__(self, a, b, c):
pass
def add_child_handler(self, pid, callback, *args):
self._callbacks[pid] = (callback, args)
# Prevent a race condition in case the child is already terminated.
self._do_waitpid(pid)
def remove_child_handler(self, pid):
try:
del self._callbacks[pid]
return True
except KeyError:
return False
def _do_waitpid_all(self):
for pid in list(self._callbacks):
self._do_waitpid(pid)
def _do_waitpid(self, expected_pid):
assert expected_pid > 0
try:
pid, status = os.waitpid(expected_pid, os.WNOHANG)
except ChildProcessError:
# The child process is already reaped
# (may happen if waitpid() is called elsewhere).
pid = expected_pid
returncode = 255
logger.warning(
"Unknown child process pid %d, will report returncode 255",
pid)
else:
if pid == 0:
# The child process is still alive.
return
returncode = waitstatus_to_exitcode(status)
if self._loop.get_debug():
logger.debug('process %s exited with returncode %s',
expected_pid, returncode)
try:
callback, args = self._callbacks.pop(pid)
except KeyError: # pragma: no cover
# May happen if .remove_child_handler() is called
# after os.waitpid() returns.
if self._loop.get_debug():
logger.warning("Child watcher got an unexpected pid: %r",
pid, exc_info=True)
else:
callback(pid, returncode, *args)
class FastChildWatcher(BaseChildWatcher):
"""'Fast' child watcher implementation.
This implementation reaps every terminated processes by calling
os.waitpid(-1) directly, possibly breaking other code spawning processes
and waiting for their termination.
There is no noticeable overhead when handling a big number of children
(O(1) each time a child terminates).
"""
def __init__(self):
super().__init__()
self._lock = threading.Lock()
self._zombies = {}
self._forks = 0
warnings._deprecated("FastChildWatcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.",
remove=(3, 14))
def close(self):
self._callbacks.clear()
self._zombies.clear()
super().close()
def __enter__(self):
with self._lock:
self._forks += 1
return self
def __exit__(self, a, b, c):
with self._lock:
self._forks -= 1
if self._forks or not self._zombies:
return
collateral_victims = str(self._zombies)
self._zombies.clear()
logger.warning(
"Caught subprocesses termination from unknown pids: %s",
collateral_victims)
def add_child_handler(self, pid, callback, *args):
assert self._forks, "Must use the context manager"
with self._lock:
try:
returncode = self._zombies.pop(pid)
except KeyError:
# The child is running.
self._callbacks[pid] = callback, args
return
# The child is dead already. We can fire the callback.
callback(pid, returncode, *args)
def remove_child_handler(self, pid):
try:
del self._callbacks[pid]
return True
except KeyError:
return False
def _do_waitpid_all(self):
# Because of signal coalescing, we must keep calling waitpid() as
# long as we're able to reap a child.
while True:
try:
pid, status = os.waitpid(-1, os.WNOHANG)
except ChildProcessError:
# No more child processes exist.
return
else:
if pid == 0:
# A child process is still alive.
return
returncode = waitstatus_to_exitcode(status)
with self._lock:
try:
callback, args = self._callbacks.pop(pid)
except KeyError:
# unknown child
if self._forks:
# It may not be registered yet.
self._zombies[pid] = returncode
if self._loop.get_debug():
logger.debug('unknown process %s exited '
'with returncode %s',
pid, returncode)
continue
callback = None
else:
if self._loop.get_debug():
logger.debug('process %s exited with returncode %s',
pid, returncode)
if callback is None:
logger.warning(
"Caught subprocess termination from unknown pid: "
"%d -> %d", pid, returncode)
else:
callback(pid, returncode, *args)
class MultiLoopChildWatcher(AbstractChildWatcher):
"""A watcher that doesn't require running loop in the main thread.
This implementation registers a SIGCHLD signal handler on
instantiation (which may conflict with other code that
install own handler for this signal).
The solution is safe but it has a significant overhead when
handling a big number of processes (*O(n)* each time a
SIGCHLD is received).
"""
# Implementation note:
# The class keeps compatibility with AbstractChildWatcher ABC
# To achieve this it has empty attach_loop() method
# and doesn't accept explicit loop argument
# for add_child_handler()/remove_child_handler()
# but retrieves the current loop by get_running_loop()
def __init__(self):
self._callbacks = {}
self._saved_sighandler = None
warnings._deprecated("MultiLoopChildWatcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.",
remove=(3, 14))
def is_active(self):
return self._saved_sighandler is not None
def close(self):
self._callbacks.clear()
if self._saved_sighandler is None:
return
handler = signal.getsignal(signal.SIGCHLD)
if handler != self._sig_chld:
logger.warning("SIGCHLD handler was changed by outside code")
else:
signal.signal(signal.SIGCHLD, self._saved_sighandler)
self._saved_sighandler = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def add_child_handler(self, pid, callback, *args):
loop = events.get_running_loop()
self._callbacks[pid] = (loop, callback, args)
# Prevent a race condition in case the child is already terminated.
self._do_waitpid(pid)
def remove_child_handler(self, pid):
try:
del self._callbacks[pid]
return True
except KeyError:
return False
def attach_loop(self, loop):
# Don't save the loop but initialize itself if called first time
# The reason to do it here is that attach_loop() is called from
# unix policy only for the main thread.
# Main thread is required for subscription on SIGCHLD signal
if self._saved_sighandler is not None:
return
self._saved_sighandler = signal.signal(signal.SIGCHLD, self._sig_chld)
if self._saved_sighandler is None:
logger.warning("Previous SIGCHLD handler was set by non-Python code, "
"restore to default handler on watcher close.")
self._saved_sighandler = signal.SIG_DFL
# Set SA_RESTART to limit EINTR occurrences.
signal.siginterrupt(signal.SIGCHLD, False)
def _do_waitpid_all(self):
for pid in list(self._callbacks):
self._do_waitpid(pid)
def _do_waitpid(self, expected_pid):
assert expected_pid > 0
try:
pid, status = os.waitpid(expected_pid, os.WNOHANG)
except ChildProcessError:
# The child process is already reaped
# (may happen if waitpid() is called elsewhere).
pid = expected_pid
returncode = 255
logger.warning(
"Unknown child process pid %d, will report returncode 255",
pid)
debug_log = False
else:
if pid == 0:
# The child process is still alive.
return
returncode = waitstatus_to_exitcode(status)
debug_log = True
try:
loop, callback, args = self._callbacks.pop(pid)
except KeyError: # pragma: no cover
# May happen if .remove_child_handler() is called
# after os.waitpid() returns.
logger.warning("Child watcher got an unexpected pid: %r",
pid, exc_info=True)
else:
if loop.is_closed():
logger.warning("Loop %r that handles pid %r is closed", loop, pid)
else:
if debug_log and loop.get_debug():
logger.debug('process %s exited with returncode %s',
expected_pid, returncode)
loop.call_soon_threadsafe(callback, pid, returncode, *args)
def _sig_chld(self, signum, frame):
try:
self._do_waitpid_all()
except (SystemExit, KeyboardInterrupt):
raise
except BaseException:
logger.warning('Unknown exception in SIGCHLD handler', exc_info=True)
class ThreadedChildWatcher(AbstractChildWatcher):
class _ThreadedChildWatcher:
"""Threaded child watcher implementation.
The watcher uses a thread per process
@@ -1363,18 +907,6 @@ class ThreadedChildWatcher(AbstractChildWatcher):
self._pid_counter = itertools.count(0)
self._threads = {}
def is_active(self):
return True
def close(self):
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def __del__(self, _warn=warnings.warn):
threads = [thread for thread in list(self._threads.values())
if thread.is_alive()]
@@ -1392,15 +924,6 @@ class ThreadedChildWatcher(AbstractChildWatcher):
self._threads[pid] = thread
thread.start()
def remove_child_handler(self, pid):
# asyncio never calls remove_child_handler() !!!
# The method is no-op but is implemented because
# abstract base classes require it.
return True
def attach_loop(self, loop):
pass
def _do_waitpid(self, loop, expected_pid, callback, args):
assert expected_pid > 0
@@ -1439,62 +962,11 @@ def can_use_pidfd():
return True
class _UnixDefaultEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
"""UNIX event loop policy with a watcher for child processes."""
class _UnixDefaultEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
"""UNIX event loop policy"""
_loop_factory = _UnixSelectorEventLoop
def __init__(self):
super().__init__()
self._watcher = None
def _init_watcher(self):
with events._lock:
if self._watcher is None: # pragma: no branch
if can_use_pidfd():
self._watcher = PidfdChildWatcher()
else:
self._watcher = ThreadedChildWatcher()
def set_event_loop(self, loop):
"""Set the event loop.
As a side effect, if a child watcher was set before, then calling
.set_event_loop() from the main thread will call .attach_loop(loop) on
the child watcher.
"""
super().set_event_loop(loop)
if (self._watcher is not None and
threading.current_thread() is threading.main_thread()):
self._watcher.attach_loop(loop)
def get_child_watcher(self):
"""Get the watcher for child processes.
If not yet set, a ThreadedChildWatcher object is automatically created.
"""
if self._watcher is None:
self._init_watcher()
warnings._deprecated("get_child_watcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.", remove=(3, 14))
return self._watcher
def set_child_watcher(self, watcher):
"""Set the watcher for child processes."""
assert watcher is None or isinstance(watcher, AbstractChildWatcher)
if self._watcher is not None:
self._watcher.close()
self._watcher = watcher
warnings._deprecated("set_child_watcher",
"{name!r} is deprecated as of Python 3.12 and will be "
"removed in Python {remove}.", remove=(3, 14))
SelectorEventLoop = _UnixSelectorEventLoop
DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
_DefaultEventLoopPolicy = _UnixDefaultEventLoopPolicy
EventLoop = SelectorEventLoop

View File

@@ -29,8 +29,8 @@ from .log import logger
__all__ = (
'SelectorEventLoop', 'ProactorEventLoop', 'IocpProactor',
'DefaultEventLoopPolicy', 'WindowsSelectorEventLoopPolicy',
'WindowsProactorEventLoopPolicy',
'_DefaultEventLoopPolicy', '_WindowsSelectorEventLoopPolicy',
'_WindowsProactorEventLoopPolicy', 'EventLoop',
)
@@ -315,24 +315,25 @@ class ProactorEventLoop(proactor_events.BaseProactorEventLoop):
proactor = IocpProactor()
super().__init__(proactor)
def run_forever(self):
try:
assert self._self_reading_future is None
self.call_soon(self._loop_self_reading)
super().run_forever()
finally:
if self._self_reading_future is not None:
ov = self._self_reading_future._ov
self._self_reading_future.cancel()
# self_reading_future always uses IOCP, so even though it's
# been cancelled, we need to make sure that the IOCP message
# is received so that the kernel is not holding on to the
# memory, possibly causing memory corruption later. Only
# unregister it if IO is complete in all respects. Otherwise
# we need another _poll() later to complete the IO.
if ov is not None and not ov.pending:
self._proactor._unregister(ov)
self._self_reading_future = None
def _run_forever_setup(self):
assert self._self_reading_future is None
self.call_soon(self._loop_self_reading)
super()._run_forever_setup()
def _run_forever_cleanup(self):
super()._run_forever_cleanup()
if self._self_reading_future is not None:
ov = self._self_reading_future._ov
self._self_reading_future.cancel()
# self_reading_future always uses IOCP, so even though it's
# been cancelled, we need to make sure that the IOCP message
# is received so that the kernel is not holding on to the
# memory, possibly causing memory corruption later. Only
# unregister it if IO is complete in all respects. Otherwise
# we need another _poll() later to complete the IO.
if ov is not None and not ov.pending:
self._proactor._unregister(ov)
self._self_reading_future = None
async def create_pipe_connection(self, protocol_factory, address):
f = self._proactor.connect_pipe(address)
@@ -890,12 +891,13 @@ class _WindowsSubprocessTransport(base_subprocess.BaseSubprocessTransport):
SelectorEventLoop = _WindowsSelectorEventLoop
class WindowsSelectorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
class _WindowsSelectorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
_loop_factory = SelectorEventLoop
class WindowsProactorEventLoopPolicy(events.BaseDefaultEventLoopPolicy):
class _WindowsProactorEventLoopPolicy(events._BaseDefaultEventLoopPolicy):
_loop_factory = ProactorEventLoop
DefaultEventLoopPolicy = WindowsProactorEventLoopPolicy
_DefaultEventLoopPolicy = _WindowsProactorEventLoopPolicy
EventLoop = ProactorEventLoop

21
Lib/base64.py vendored
View File

@@ -1,12 +1,9 @@
#! /usr/bin/env python3
"""Base16, Base32, Base64 (RFC 3548), Base85 and Ascii85 data encodings"""
# Modified 04-Oct-1995 by Jack Jansen to use binascii module
# Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support
# Modified 22-May-2007 by Guido van Rossum to use bytes everywhere
import re
import struct
import binascii
@@ -286,7 +283,7 @@ def b16decode(s, casefold=False):
s = _bytes_from_decode_data(s)
if casefold:
s = s.upper()
if re.search(b'[^0-9A-F]', s):
if s.translate(None, delete=b'0123456789ABCDEF'):
raise binascii.Error('Non-base16 digit found')
return binascii.unhexlify(s)
@@ -465,9 +462,12 @@ def b85decode(b):
# Delay the initialization of tables to not waste memory
# if the function is never called
if _b85dec is None:
_b85dec = [None] * 256
# we don't assign to _b85dec directly to avoid issues when
# multiple threads call this function simultaneously
b85dec_tmp = [None] * 256
for i, c in enumerate(_b85alphabet):
_b85dec[c] = i
b85dec_tmp[c] = i
_b85dec = b85dec_tmp
b = _bytes_from_decode_data(b)
padding = (-len(b)) % 5
@@ -604,7 +604,14 @@ def main():
with open(args[0], 'rb') as f:
func(f, sys.stdout.buffer)
else:
func(sys.stdin.buffer, sys.stdout.buffer)
if sys.stdin.isatty():
# gh-138775: read terminal input data all at once to detect EOF
import io
data = sys.stdin.buffer.read()
buffer = io.BytesIO(data)
else:
buffer = sys.stdin.buffer
func(buffer, sys.stdout.buffer)
if __name__ == '__main__':

409
Lib/bdb.py vendored
View File

@@ -2,7 +2,10 @@
import fnmatch
import sys
import threading
import os
import weakref
from contextlib import contextmanager
from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
@@ -14,6 +17,166 @@ class BdbQuit(Exception):
"""Exception to give up completely."""
E = sys.monitoring.events
class _MonitoringTracer:
EVENT_CALLBACK_MAP = {
E.PY_START: 'call',
E.PY_RESUME: 'call',
E.PY_THROW: 'call',
E.LINE: 'line',
E.JUMP: 'jump',
E.PY_RETURN: 'return',
E.PY_YIELD: 'return',
E.PY_UNWIND: 'unwind',
E.RAISE: 'exception',
E.STOP_ITERATION: 'exception',
E.INSTRUCTION: 'opcode',
}
GLOBAL_EVENTS = E.PY_START | E.PY_RESUME | E.PY_THROW | E.PY_UNWIND | E.RAISE
LOCAL_EVENTS = E.LINE | E.JUMP | E.PY_RETURN | E.PY_YIELD | E.STOP_ITERATION
def __init__(self):
self._tool_id = sys.monitoring.DEBUGGER_ID
self._name = 'bdbtracer'
self._tracefunc = None
self._disable_current_event = False
self._tracing_thread = None
self._enabled = False
def start_trace(self, tracefunc):
self._tracefunc = tracefunc
self._tracing_thread = threading.current_thread()
curr_tool = sys.monitoring.get_tool(self._tool_id)
if curr_tool is None:
sys.monitoring.use_tool_id(self._tool_id, self._name)
elif curr_tool == self._name:
sys.monitoring.clear_tool_id(self._tool_id)
else:
raise ValueError('Another debugger is using the monitoring tool')
E = sys.monitoring.events
all_events = 0
for event, cb_name in self.EVENT_CALLBACK_MAP.items():
callback = self.callback_wrapper(getattr(self, f'{cb_name}_callback'), event)
sys.monitoring.register_callback(self._tool_id, event, callback)
if event != E.INSTRUCTION:
all_events |= event
self.update_local_events()
sys.monitoring.set_events(self._tool_id, self.GLOBAL_EVENTS)
self._enabled = True
def stop_trace(self):
self._enabled = False
self._tracing_thread = None
curr_tool = sys.monitoring.get_tool(self._tool_id)
if curr_tool != self._name:
return
sys.monitoring.clear_tool_id(self._tool_id)
sys.monitoring.free_tool_id(self._tool_id)
def disable_current_event(self):
self._disable_current_event = True
def restart_events(self):
if sys.monitoring.get_tool(self._tool_id) == self._name:
sys.monitoring.restart_events()
def callback_wrapper(self, func, event):
import functools
@functools.wraps(func)
def wrapper(*args):
if self._tracing_thread != threading.current_thread():
return
try:
frame = sys._getframe().f_back
ret = func(frame, *args)
if self._enabled and frame.f_trace:
self.update_local_events()
if (
self._disable_current_event
and event not in (E.PY_THROW, E.PY_UNWIND, E.RAISE)
):
return sys.monitoring.DISABLE
else:
return ret
except BaseException:
self.stop_trace()
sys._getframe().f_back.f_trace = None
raise
finally:
self._disable_current_event = False
return wrapper
def call_callback(self, frame, code, *args):
local_tracefunc = self._tracefunc(frame, 'call', None)
if local_tracefunc is not None:
frame.f_trace = local_tracefunc
if self._enabled:
sys.monitoring.set_local_events(self._tool_id, code, self.LOCAL_EVENTS)
def return_callback(self, frame, code, offset, retval):
if frame.f_trace:
frame.f_trace(frame, 'return', retval)
def unwind_callback(self, frame, code, *args):
if frame.f_trace:
frame.f_trace(frame, 'return', None)
def line_callback(self, frame, code, *args):
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)
def jump_callback(self, frame, code, inst_offset, dest_offset):
if dest_offset > inst_offset:
return sys.monitoring.DISABLE
inst_lineno = self._get_lineno(code, inst_offset)
dest_lineno = self._get_lineno(code, dest_offset)
if inst_lineno != dest_lineno:
return sys.monitoring.DISABLE
if frame.f_trace and frame.f_trace_lines:
frame.f_trace(frame, 'line', None)
def exception_callback(self, frame, code, offset, exc):
if frame.f_trace:
if exc.__traceback__ and hasattr(exc.__traceback__, 'tb_frame'):
tb = exc.__traceback__
while tb:
if tb.tb_frame.f_locals.get('self') is self:
return
tb = tb.tb_next
frame.f_trace(frame, 'exception', (type(exc), exc, exc.__traceback__))
def opcode_callback(self, frame, code, offset):
if frame.f_trace and frame.f_trace_opcodes:
frame.f_trace(frame, 'opcode', None)
def update_local_events(self, frame=None):
if sys.monitoring.get_tool(self._tool_id) != self._name:
return
if frame is None:
frame = sys._getframe().f_back
while frame is not None:
if frame.f_trace is not None:
if frame.f_trace_opcodes:
events = self.LOCAL_EVENTS | E.INSTRUCTION
else:
events = self.LOCAL_EVENTS
sys.monitoring.set_local_events(self._tool_id, frame.f_code, events)
frame = frame.f_back
def _get_lineno(self, code, offset):
import dis
last_lineno = None
for start, lineno in dis.findlinestarts(code):
if offset < start:
return last_lineno
last_lineno = lineno
return last_lineno
class Bdb:
"""Generic Python debugger base class.
@@ -28,11 +191,24 @@ class Bdb:
is determined by the __name__ in the frame globals.
"""
def __init__(self, skip=None):
def __init__(self, skip=None, backend='settrace'):
self.skip = set(skip) if skip else None
self.breaks = {}
self.fncache = {}
self.frame_trace_lines_opcodes = {}
self.frame_returning = None
self.trace_opcodes = False
self.enterframe = None
self.cmdframe = None
self.cmdlineno = None
self.code_linenos = weakref.WeakKeyDictionary()
self.backend = backend
if backend == 'monitoring':
self.monitoring_tracer = _MonitoringTracer()
elif backend == 'settrace':
self.monitoring_tracer = None
else:
raise ValueError(f"Invalid backend '{backend}'")
self._load_breaks()
@@ -53,6 +229,18 @@ class Bdb:
self.fncache[filename] = canonic
return canonic
def start_trace(self):
if self.monitoring_tracer:
self.monitoring_tracer.start_trace(self.trace_dispatch)
else:
sys.settrace(self.trace_dispatch)
def stop_trace(self):
if self.monitoring_tracer:
self.monitoring_tracer.stop_trace()
else:
sys.settrace(None)
def reset(self):
"""Set values of attributes as ready to start debugging."""
import linecache
@@ -60,6 +248,12 @@ class Bdb:
self.botframe = None
self._set_stopinfo(None, None)
@contextmanager
def set_enterframe(self, frame):
self.enterframe = frame
yield
self.enterframe = None
def trace_dispatch(self, frame, event, arg):
"""Dispatch a trace function for debugged frames based on the event.
@@ -84,24 +278,28 @@ class Bdb:
The arg parameter depends on the previous event.
"""
if self.quitting:
return # None
if event == 'line':
return self.dispatch_line(frame)
if event == 'call':
return self.dispatch_call(frame, arg)
if event == 'return':
return self.dispatch_return(frame, arg)
if event == 'exception':
return self.dispatch_exception(frame, arg)
if event == 'c_call':
with self.set_enterframe(frame):
if self.quitting:
return # None
if event == 'line':
return self.dispatch_line(frame)
if event == 'call':
return self.dispatch_call(frame, arg)
if event == 'return':
return self.dispatch_return(frame, arg)
if event == 'exception':
return self.dispatch_exception(frame, arg)
if event == 'c_call':
return self.trace_dispatch
if event == 'c_exception':
return self.trace_dispatch
if event == 'c_return':
return self.trace_dispatch
if event == 'opcode':
return self.dispatch_opcode(frame, arg)
print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
return self.trace_dispatch
if event == 'c_exception':
return self.trace_dispatch
if event == 'c_return':
return self.trace_dispatch
print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
return self.trace_dispatch
def dispatch_line(self, frame):
"""Invoke user function and return trace function for line event.
@@ -110,9 +308,17 @@ class Bdb:
self.user_line(). Raise BdbQuit if self.quitting is set.
Return self.trace_dispatch to continue tracing in this scope.
"""
if self.stop_here(frame) or self.break_here(frame):
# GH-136057
# For line events, we don't want to stop at the same line where
# the latest next/step command was issued.
if (self.stop_here(frame) or self.break_here(frame)) and not (
self.cmdframe == frame and self.cmdlineno == frame.f_lineno
):
self.user_line(frame)
self.restart_events()
if self.quitting: raise BdbQuit
elif not self.get_break(frame.f_code.co_filename, frame.f_lineno):
self.disable_current_event()
return self.trace_dispatch
def dispatch_call(self, frame, arg):
@@ -128,12 +334,18 @@ class Bdb:
self.botframe = frame.f_back # (CT) Note that this may also be None!
return self.trace_dispatch
if not (self.stop_here(frame) or self.break_anywhere(frame)):
# No need to trace this function
# We already know there's no breakpoint in this function
# If it's a next/until/return command, we don't need any CALL event
# and we don't need to set the f_trace on any new frame.
# If it's a step command, it must either hit stop_here, or skip the
# whole module. Either way, we don't need the CALL event here.
self.disable_current_event()
return # None
# Ignore call events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
return self.trace_dispatch
self.user_call(frame, arg)
self.restart_events()
if self.quitting: raise BdbQuit
return self.trace_dispatch
@@ -147,16 +359,25 @@ class Bdb:
if self.stop_here(frame) or frame == self.returnframe:
# Ignore return events in generator except when stepping.
if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
# It's possible to trigger a StopIteration exception in
# the caller so we must set the trace function in the caller
self._set_caller_tracefunc(frame)
return self.trace_dispatch
try:
self.frame_returning = frame
self.user_return(frame, arg)
self.restart_events()
finally:
self.frame_returning = None
if self.quitting: raise BdbQuit
# The user issued a 'next' or 'until' command.
if self.stopframe is frame and self.stoplineno != -1:
self._set_stopinfo(None, None)
# The previous frame might not have f_trace set, unless we are
# issuing a command that does not expect to stop, we should set
# f_trace
if self.stoplineno != -1:
self._set_caller_tracefunc(frame)
return self.trace_dispatch
def dispatch_exception(self, frame, arg):
@@ -173,6 +394,7 @@ class Bdb:
if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
and arg[0] is StopIteration and arg[2] is None):
self.user_exception(frame, arg)
self.restart_events()
if self.quitting: raise BdbQuit
# Stop at the StopIteration or GeneratorExit exception when the user
# has set stopframe in a generator by issuing a return command, or a
@@ -182,10 +404,26 @@ class Bdb:
and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
and arg[0] in (StopIteration, GeneratorExit)):
self.user_exception(frame, arg)
self.restart_events()
if self.quitting: raise BdbQuit
return self.trace_dispatch
def dispatch_opcode(self, frame, arg):
"""Invoke user function and return trace function for opcode event.
If the debugger stops on the current opcode, invoke
self.user_opcode(). Raise BdbQuit if self.quitting is set.
Return self.trace_dispatch to continue tracing in this scope.
Opcode event will always trigger the user callback. For now the only
opcode event is from an inline set_trace() and we want to stop there
unconditionally.
"""
self.user_opcode(frame)
self.restart_events()
if self.quitting: raise BdbQuit
return self.trace_dispatch
# Normally derived classes don't override the following
# methods, but they may if they want to redefine the
# definition of stopping and breakpoints.
@@ -249,9 +487,25 @@ class Bdb:
raise NotImplementedError("subclass of bdb must implement do_clear()")
def break_anywhere(self, frame):
"""Return True if there is any breakpoint for frame's filename.
"""Return True if there is any breakpoint in that frame
"""
return self.canonic(frame.f_code.co_filename) in self.breaks
filename = self.canonic(frame.f_code.co_filename)
if filename not in self.breaks:
return False
for lineno in self.breaks[filename]:
if self._lineno_in_frame(lineno, frame):
return True
return False
def _lineno_in_frame(self, lineno, frame):
"""Return True if the line number is in the frame's code object.
"""
code = frame.f_code
if lineno < code.co_firstlineno:
return False
if code not in self.code_linenos:
self.code_linenos[code] = set(lineno for _, _, lineno in code.co_lines())
return lineno in self.code_linenos[code]
# Derived classes should override the user_* methods
# to gain control.
@@ -272,7 +526,24 @@ class Bdb:
"""Called when we stop on an exception."""
pass
def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
def user_opcode(self, frame):
"""Called when we are about to execute an opcode."""
pass
def _set_trace_opcodes(self, trace_opcodes):
if trace_opcodes != self.trace_opcodes:
self.trace_opcodes = trace_opcodes
frame = self.enterframe
while frame is not None:
frame.f_trace_opcodes = trace_opcodes
if frame is self.botframe:
break
frame = frame.f_back
if self.monitoring_tracer:
self.monitoring_tracer.update_local_events()
def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False,
cmdframe=None, cmdlineno=None):
"""Set the attributes for stopping.
If stoplineno is greater than or equal to 0, then stop at line
@@ -285,6 +556,21 @@ class Bdb:
# stoplineno >= 0 means: stop at line >= the stoplineno
# stoplineno -1 means: don't stop at all
self.stoplineno = stoplineno
# cmdframe/cmdlineno is the frame/line number when the user issued
# step/next commands.
self.cmdframe = cmdframe
self.cmdlineno = cmdlineno
self._set_trace_opcodes(opcode)
def _set_caller_tracefunc(self, current_frame):
# Issue #13183: pdb skips frames after hitting a breakpoint and running
# step commands.
# Restore the trace function in the caller (that may not have been set
# for performance reasons) when returning from the current frame, unless
# the caller is the botframe.
caller_frame = current_frame.f_back
if caller_frame and not caller_frame.f_trace and caller_frame is not self.botframe:
caller_frame.f_trace = self.trace_dispatch
# Derived classes and clients can call the following methods
# to affect the stepping state.
@@ -299,24 +585,22 @@ class Bdb:
def set_step(self):
"""Stop after one line of code."""
# Issue #13183: pdb skips frames after hitting a breakpoint and running
# step commands.
# Restore the trace function in the caller (that may not have been set
# for performance reasons) when returning from the current frame.
if self.frame_returning:
caller_frame = self.frame_returning.f_back
if caller_frame and not caller_frame.f_trace:
caller_frame.f_trace = self.trace_dispatch
self._set_stopinfo(None, None)
# set_step() could be called from signal handler so enterframe might be None
self._set_stopinfo(None, None, cmdframe=self.enterframe,
cmdlineno=getattr(self.enterframe, 'f_lineno', None))
def set_stepinstr(self):
"""Stop before the next instruction."""
self._set_stopinfo(None, None, opcode=True)
def set_next(self, frame):
"""Stop on the next line in or below the given frame."""
self._set_stopinfo(frame, None)
self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno)
def set_return(self, frame):
"""Stop when returning from the given frame."""
if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
self._set_stopinfo(frame, None, -1)
self._set_stopinfo(frame, frame, -1)
else:
self._set_stopinfo(frame.f_back, frame)
@@ -325,15 +609,21 @@ class Bdb:
If frame is not specified, debugging starts from caller's frame.
"""
self.stop_trace()
if frame is None:
frame = sys._getframe().f_back
self.reset()
while frame:
frame.f_trace = self.trace_dispatch
self.botframe = frame
frame = frame.f_back
self.set_step()
sys.settrace(self.trace_dispatch)
with self.set_enterframe(frame):
while frame:
frame.f_trace = self.trace_dispatch
self.botframe = frame
self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes)
# We need f_trace_lines == True for the debugger to work
frame.f_trace_lines = True
frame = frame.f_back
self.set_stepinstr()
self.enterframe = None
self.start_trace()
def set_continue(self):
"""Stop only at breakpoints or when finished.
@@ -344,11 +634,16 @@ class Bdb:
self._set_stopinfo(self.botframe, None, -1)
if not self.breaks:
# no breakpoints; run without debugger overhead
sys.settrace(None)
self.stop_trace()
frame = sys._getframe().f_back
while frame and frame is not self.botframe:
del frame.f_trace
frame = frame.f_back
for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items():
frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes
if self.backend == 'monitoring':
self.monitoring_tracer.update_local_events()
self.frame_trace_lines_opcodes = {}
def set_quit(self):
"""Set quitting attribute to True.
@@ -358,7 +653,7 @@ class Bdb:
self.stopframe = self.botframe
self.returnframe = None
self.quitting = True
sys.settrace(None)
self.stop_trace()
# Derived classes and clients can call the following methods
# to manipulate breakpoints. These methods return an
@@ -387,6 +682,14 @@ class Bdb:
return 'Line %s:%d does not exist' % (filename, lineno)
self._add_to_breaks(filename, lineno)
bp = Breakpoint(filename, lineno, temporary, cond, funcname)
# After we set a new breakpoint, we need to search through all frames
# and set f_trace to trace_dispatch if there could be a breakpoint in
# that frame.
frame = self.enterframe
while frame:
if self.break_anywhere(frame):
frame.f_trace = self.trace_dispatch
frame = frame.f_back
return None
def _load_breaks(self):
@@ -578,6 +881,16 @@ class Bdb:
s += f'{lprefix}Warning: lineno is None'
return s
def disable_current_event(self):
"""Disable the current event."""
if self.backend == 'monitoring':
self.monitoring_tracer.disable_current_event()
def restart_events(self):
"""Restart all events."""
if self.backend == 'monitoring':
self.monitoring_tracer.restart_events()
# The following methods can be called by clients to use
# a debugger to debug a statement or an expression.
# Both can be given as a string, or a code object.
@@ -595,14 +908,14 @@ class Bdb:
self.reset()
if isinstance(cmd, str):
cmd = compile(cmd, "<string>", "exec")
sys.settrace(self.trace_dispatch)
self.start_trace()
try:
exec(cmd, globals, locals)
except BdbQuit:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()
def runeval(self, expr, globals=None, locals=None):
"""Debug an expression executed via the eval() function.
@@ -615,14 +928,14 @@ class Bdb:
if locals is None:
locals = globals
self.reset()
sys.settrace(self.trace_dispatch)
self.start_trace()
try:
return eval(expr, globals, locals)
except BdbQuit:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()
def runctx(self, cmd, globals, locals):
"""For backwards-compatibility. Defers to run()."""
@@ -637,7 +950,7 @@ class Bdb:
Return the result of the function call.
"""
self.reset()
sys.settrace(self.trace_dispatch)
self.start_trace()
res = None
try:
res = func(*args, **kwds)
@@ -645,7 +958,7 @@ class Bdb:
pass
finally:
self.quitting = True
sys.settrace(None)
self.stop_trace()
return res

26
Lib/bz2.py vendored
View File

@@ -10,20 +10,20 @@ __all__ = ["BZ2File", "BZ2Compressor", "BZ2Decompressor",
__author__ = "Nadeem Vawda <nadeem.vawda@gmail.com>"
from builtins import open as _builtin_open
from compression._common import _streams
import io
import os
import _compression
from _bz2 import BZ2Compressor, BZ2Decompressor
_MODE_CLOSED = 0
# Value 0 no longer used
_MODE_READ = 1
# Value 2 no longer used
_MODE_WRITE = 3
class BZ2File(_compression.BaseStream):
class BZ2File(_streams.BaseStream):
"""A file object providing transparent bzip2 (de)compression.
@@ -54,7 +54,7 @@ class BZ2File(_compression.BaseStream):
"""
self._fp = None
self._closefp = False
self._mode = _MODE_CLOSED
self._mode = None
if not (1 <= compresslevel <= 9):
raise ValueError("compresslevel must be between 1 and 9")
@@ -88,7 +88,7 @@ class BZ2File(_compression.BaseStream):
raise TypeError("filename must be a str, bytes, file or PathLike object")
if self._mode == _MODE_READ:
raw = _compression.DecompressReader(self._fp,
raw = _streams.DecompressReader(self._fp,
BZ2Decompressor, trailing_error=OSError)
self._buffer = io.BufferedReader(raw)
else:
@@ -100,7 +100,7 @@ class BZ2File(_compression.BaseStream):
May be called more than once without error. Once the file is
closed, any other operation on it will raise a ValueError.
"""
if self._mode == _MODE_CLOSED:
if self.closed:
return
try:
if self._mode == _MODE_READ:
@@ -115,13 +115,21 @@ class BZ2File(_compression.BaseStream):
finally:
self._fp = None
self._closefp = False
self._mode = _MODE_CLOSED
self._buffer = None
@property
def closed(self):
"""True if this file is closed."""
return self._mode == _MODE_CLOSED
return self._fp is None
@property
def name(self):
self._check_not_closed()
return self._fp.name
@property
def mode(self):
return 'wb' if self._mode == _MODE_WRITE else 'rb'
def fileno(self):
"""Return the file descriptor for the underlying file."""
@@ -240,7 +248,7 @@ class BZ2File(_compression.BaseStream):
Line separators are not added between the written byte strings.
"""
return _compression.BaseStream.writelines(self, seq)
return _streams.BaseStream.writelines(self, seq)
def seek(self, offset, whence=io.SEEK_SET):
"""Change the file position.

125
Lib/calendar.py vendored
View File

@@ -428,6 +428,7 @@ class TextCalendar(Calendar):
headers = (header for k in months)
a(formatstring(headers, colwidth, c).rstrip())
a('\n'*l)
# max number of weeks for this row
height = max(len(cal) for cal in row)
for j in range(height):
@@ -646,6 +647,117 @@ class LocaleHTMLCalendar(HTMLCalendar):
with different_locale(self.locale):
return super().formatmonthname(theyear, themonth, withyear)
class _CLIDemoCalendar(TextCalendar):
def __init__(self, highlight_day=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.highlight_day = highlight_day
def formatweek(self, theweek, width, *, highlight_day=None):
"""
Returns a single week in a string (no newline).
"""
if highlight_day:
from _colorize import get_colors
ansi = get_colors()
highlight = f"{ansi.BLACK}{ansi.BACKGROUND_YELLOW}"
reset = ansi.RESET
else:
highlight = reset = ""
return ' '.join(
(
f"{highlight}{self.formatday(d, wd, width)}{reset}"
if d == highlight_day
else self.formatday(d, wd, width)
)
for (d, wd) in theweek
)
def formatmonth(self, theyear, themonth, w=0, l=0):
"""
Return a month's calendar string (multi-line).
"""
if (
self.highlight_day
and self.highlight_day.year == theyear
and self.highlight_day.month == themonth
):
highlight_day = self.highlight_day.day
else:
highlight_day = None
w = max(2, w)
l = max(1, l)
s = self.formatmonthname(theyear, themonth, 7 * (w + 1) - 1)
s = s.rstrip()
s += '\n' * l
s += self.formatweekheader(w).rstrip()
s += '\n' * l
for week in self.monthdays2calendar(theyear, themonth):
s += self.formatweek(week, w, highlight_day=highlight_day).rstrip()
s += '\n' * l
return s
def formatyear(self, theyear, w=2, l=1, c=6, m=3):
"""
Returns a year's calendar as a multi-line string.
"""
w = max(2, w)
l = max(1, l)
c = max(2, c)
colwidth = (w + 1) * 7 - 1
v = []
a = v.append
a(repr(theyear).center(colwidth*m+c*(m-1)).rstrip())
a('\n'*l)
header = self.formatweekheader(w)
for (i, row) in enumerate(self.yeardays2calendar(theyear, m)):
# months in this row
months = range(m*i+1, min(m*(i+1)+1, 13))
a('\n'*l)
names = (self.formatmonthname(theyear, k, colwidth, False)
for k in months)
a(formatstring(names, colwidth, c).rstrip())
a('\n'*l)
headers = (header for k in months)
a(formatstring(headers, colwidth, c).rstrip())
a('\n'*l)
if (
self.highlight_day
and self.highlight_day.year == theyear
and self.highlight_day.month in months
):
month_pos = months.index(self.highlight_day.month)
else:
month_pos = None
# max number of weeks for this row
height = max(len(cal) for cal in row)
for j in range(height):
weeks = []
for k, cal in enumerate(row):
if j >= len(cal):
weeks.append('')
else:
day = (
self.highlight_day.day if k == month_pos else None
)
weeks.append(
self.formatweek(cal[j], w, highlight_day=day)
)
a(formatstring(weeks, colwidth, c).rstrip())
a('\n' * l)
return ''.join(v)
class _CLIDemoLocaleCalendar(LocaleTextCalendar, _CLIDemoCalendar):
def __init__(self, highlight_day=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.highlight_day = highlight_day
# Support for old module level interface
c = TextCalendar()
@@ -698,7 +810,7 @@ def timegm(tuple):
def main(args=None):
import argparse
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(color=True)
textgroup = parser.add_argument_group('text only arguments')
htmlgroup = parser.add_argument_group('html only arguments')
textgroup.add_argument(
@@ -765,6 +877,7 @@ def main(args=None):
sys.exit(1)
locale = options.locale, options.encoding
today = datetime.date.today()
if options.type == "html":
if options.month:
@@ -781,23 +894,23 @@ def main(args=None):
optdict = dict(encoding=encoding, css=options.css)
write = sys.stdout.buffer.write
if options.year is None:
write(cal.formatyearpage(datetime.date.today().year, **optdict))
write(cal.formatyearpage(today.year, **optdict))
else:
write(cal.formatyearpage(options.year, **optdict))
else:
if options.locale:
cal = LocaleTextCalendar(locale=locale)
cal = _CLIDemoLocaleCalendar(highlight_day=today, locale=locale)
else:
cal = TextCalendar()
cal = _CLIDemoCalendar(highlight_day=today)
cal.setfirstweekday(options.first_weekday)
optdict = dict(w=options.width, l=options.lines)
if options.month is None:
optdict["c"] = options.spacing
optdict["m"] = options.months
if options.month is not None:
else:
_validate_month(options.month)
if options.year is None:
result = cal.formatyear(datetime.date.today().year, **optdict)
result = cal.formatyear(today.year, **optdict)
elif options.month is None:
result = cal.formatyear(options.year, **optdict)
else:

37
Lib/cmd.py vendored
View File

@@ -5,16 +5,16 @@ Interpreters constructed with this class obey the following conventions:
1. End of file on input is processed as the command 'EOF'.
2. A command is parsed out of each line by collecting the prefix composed
of characters in the identchars member.
3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method
3. A command 'foo' is dispatched to a method 'do_foo()'; the do_ method
is passed a single argument consisting of the remainder of the line.
4. Typing an empty line repeats the last command. (Actually, it calls the
method `emptyline', which may be overridden in a subclass.)
5. There is a predefined `help' method. Given an argument `topic', it
calls the command `help_topic'. With no arguments, it lists all topics
method 'emptyline', which may be overridden in a subclass.)
5. There is a predefined 'help' method. Given an argument 'topic', it
calls the command 'help_topic'. With no arguments, it lists all topics
with defined help_ functions, broken into up to three topics; documented
commands, miscellaneous help topics, and undocumented commands.
6. The command '?' is a synonym for `help'. The command '!' is a synonym
for `shell', if a do_shell method exists.
6. The command '?' is a synonym for 'help'. The command '!' is a synonym
for 'shell', if a do_shell method exists.
7. If completion is enabled, completing commands will be done automatically,
and completing of commands args is done by calling complete_foo() with
arguments text, line, begidx, endidx. text is string we are matching
@@ -23,31 +23,34 @@ Interpreters constructed with this class obey the following conventions:
indexes of the text being matched, which could be used to provide
different completion depending upon which position the argument is in.
The `default' method may be overridden to intercept commands for which there
The 'default' method may be overridden to intercept commands for which there
is no do_ method.
The `completedefault' method may be overridden to intercept completions for
The 'completedefault' method may be overridden to intercept completions for
commands that have no complete_ method.
The data member `self.ruler' sets the character used to draw separator lines
The data member 'self.ruler' sets the character used to draw separator lines
in the help messages. If empty, no ruler line is drawn. It defaults to "=".
If the value of `self.intro' is nonempty when the cmdloop method is called,
If the value of 'self.intro' is nonempty when the cmdloop method is called,
it is printed out on interpreter startup. This value may be overridden
via an optional argument to the cmdloop() method.
The data members `self.doc_header', `self.misc_header', and
`self.undoc_header' set the headers used for the help function's
The data members 'self.doc_header', 'self.misc_header', and
'self.undoc_header' set the headers used for the help function's
listings of documented functions, miscellaneous topics, and undocumented
functions respectively.
"""
import inspect, string, sys
import sys
__all__ = ["Cmd"]
PROMPT = '(Cmd) '
IDENTCHARS = string.ascii_letters + string.digits + '_'
IDENTCHARS = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'abcdefghijklmnopqrstuvwxyz'
'0123456789'
'_')
class Cmd:
"""A simple framework for writing line-oriented command interpreters.
@@ -270,7 +273,7 @@ class Cmd:
endidx = readline.get_endidx() - stripped
if begidx>0:
cmd, args, foo = self.parseline(line)
if cmd == '':
if not cmd:
compfunc = self.completedefault
else:
try:
@@ -303,9 +306,11 @@ class Cmd:
try:
func = getattr(self, 'help_' + arg)
except AttributeError:
from inspect import cleandoc
try:
doc=getattr(self, 'do_' + arg).__doc__
doc = inspect.cleandoc(doc)
doc = cleandoc(doc)
if doc:
self.stdout.write("%s\n"%str(doc))
return

217
Lib/code.py vendored
View File

@@ -5,6 +5,7 @@
# Inspired by similar code by Jeff Epler and Fredrik Lundh.
import builtins
import sys
import traceback
from codeop import CommandCompiler, compile_command
@@ -24,10 +25,10 @@ class InteractiveInterpreter:
def __init__(self, locals=None):
"""Constructor.
The optional 'locals' argument specifies the dictionary in
which code will be executed; it defaults to a newly created
dictionary with key "__name__" set to "__console__" and key
"__doc__" set to None.
The optional 'locals' argument specifies a mapping to use as the
namespace in which code will be executed; it defaults to a newly
created dictionary with key "__name__" set to "__console__" and
key "__doc__" set to None.
"""
if locals is None:
@@ -63,7 +64,7 @@ class InteractiveInterpreter:
code = self.compile(source, filename, symbol)
except (OverflowError, SyntaxError, ValueError):
# Case 1
self.showsyntaxerror(filename)
self.showsyntaxerror(filename, source=source)
return False
if code is None:
@@ -93,7 +94,7 @@ class InteractiveInterpreter:
except:
self.showtraceback()
def showsyntaxerror(self, filename=None):
def showsyntaxerror(self, filename=None, **kwargs):
"""Display the syntax error that just occurred.
This doesn't display a stack trace because there isn't one.
@@ -105,29 +106,14 @@ class InteractiveInterpreter:
The output is written by self.write(), below.
"""
type, value, tb = sys.exc_info()
sys.last_exc = value
sys.last_type = type
sys.last_value = value
sys.last_traceback = tb
if filename and type is SyntaxError:
# Work hard to stuff the correct filename in the exception
try:
msg, (dummy_filename, lineno, offset, line) = value.args
except ValueError:
# Not the format we expect; leave it alone
pass
else:
# Stuff in the right filename
value = SyntaxError(msg, (filename, lineno, offset, line))
sys.last_exc = sys.last_value = value
if sys.excepthook is sys.__excepthook__:
lines = traceback.format_exception_only(type, value)
self.write(''.join(lines))
else:
# If someone has set sys.excepthook, we let that take precedence
# over self.write
sys.excepthook(type, value, tb)
try:
typ, value, tb = sys.exc_info()
if filename and issubclass(typ, SyntaxError):
value.filename = filename
source = kwargs.pop('source', "")
self._showtraceback(typ, value, None, source)
finally:
typ = value = tb = None
def showtraceback(self):
"""Display the exception that just occurred.
@@ -137,19 +123,46 @@ class InteractiveInterpreter:
The output is written by self.write(), below.
"""
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
sys.last_traceback = last_tb
sys.last_exc = ei[1]
try:
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
if sys.excepthook is sys.__excepthook__:
self.write(''.join(lines))
else:
# If someone has set sys.excepthook, we let that take precedence
# over self.write
sys.excepthook(ei[0], ei[1], last_tb)
typ, value, tb = sys.exc_info()
self._showtraceback(typ, value, tb.tb_next, "")
finally:
last_tb = ei = None
typ = value = tb = None
def _showtraceback(self, typ, value, tb, source):
sys.last_type = typ
sys.last_traceback = tb
value = value.with_traceback(tb)
# Set the line of text that the exception refers to
lines = source.splitlines()
if (source and typ is SyntaxError
and not value.text and value.lineno is not None
and len(lines) >= value.lineno):
value.text = lines[value.lineno - 1]
sys.last_exc = sys.last_value = value
if sys.excepthook is sys.__excepthook__:
self._excepthook(typ, value, tb)
else:
# If someone has set sys.excepthook, we let that take precedence
# over self.write
try:
sys.excepthook(typ, value, tb)
except SystemExit:
raise
except BaseException as e:
e.__context__ = None
e = e.with_traceback(e.__traceback__.tb_next)
print('Error in sys.excepthook:', file=sys.stderr)
sys.__excepthook__(type(e), e, e.__traceback__)
print(file=sys.stderr)
print('Original exception was:', file=sys.stderr)
sys.__excepthook__(typ, value, tb)
def _excepthook(self, typ, value, tb):
# This method is being overwritten in
# _pyrepl.console.InteractiveColoredConsole
lines = traceback.format_exception(typ, value, tb)
self.write(''.join(lines))
def write(self, data):
"""Write a string.
@@ -169,7 +182,7 @@ class InteractiveConsole(InteractiveInterpreter):
"""
def __init__(self, locals=None, filename="<console>"):
def __init__(self, locals=None, filename="<console>", *, local_exit=False):
"""Constructor.
The optional locals argument will be passed to the
@@ -181,6 +194,7 @@ class InteractiveConsole(InteractiveInterpreter):
"""
InteractiveInterpreter.__init__(self, locals)
self.filename = filename
self.local_exit = local_exit
self.resetbuffer()
def resetbuffer(self):
@@ -205,12 +219,17 @@ class InteractiveConsole(InteractiveInterpreter):
"""
try:
sys.ps1
delete_ps1_after = False
except AttributeError:
sys.ps1 = ">>> "
delete_ps1_after = True
try:
sys.ps2
_ps2 = sys.ps2
delete_ps2_after = False
except AttributeError:
sys.ps2 = "... "
delete_ps2_after = True
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
if banner is None:
self.write("Python %s on %s\n%s\n(%s)\n" %
@@ -219,29 +238,72 @@ class InteractiveConsole(InteractiveInterpreter):
elif banner:
self.write("%s\n" % str(banner))
more = 0
while 1:
try:
if more:
prompt = sys.ps2
else:
prompt = sys.ps1
try:
line = self.raw_input(prompt)
except EOFError:
self.write("\n")
break
else:
more = self.push(line)
except KeyboardInterrupt:
self.write("\nKeyboardInterrupt\n")
self.resetbuffer()
more = 0
if exitmsg is None:
self.write('now exiting %s...\n' % self.__class__.__name__)
elif exitmsg != '':
self.write('%s\n' % exitmsg)
def push(self, line):
# When the user uses exit() or quit() in their interactive shell
# they probably just want to exit the created shell, not the whole
# process. exit and quit in builtins closes sys.stdin which makes
# it super difficult to restore
#
# When self.local_exit is True, we overwrite the builtins so
# exit() and quit() only raises SystemExit and we can catch that
# to only exit the interactive shell
_exit = None
_quit = None
if self.local_exit:
if hasattr(builtins, "exit"):
_exit = builtins.exit
builtins.exit = Quitter("exit")
if hasattr(builtins, "quit"):
_quit = builtins.quit
builtins.quit = Quitter("quit")
try:
while True:
try:
if more:
prompt = sys.ps2
else:
prompt = sys.ps1
try:
line = self.raw_input(prompt)
except EOFError:
self.write("\n")
break
else:
more = self.push(line)
except KeyboardInterrupt:
self.write("\nKeyboardInterrupt\n")
self.resetbuffer()
more = 0
except SystemExit as e:
if self.local_exit:
self.write("\n")
break
else:
raise e
finally:
# restore exit and quit in builtins if they were modified
if _exit is not None:
builtins.exit = _exit
if _quit is not None:
builtins.quit = _quit
if delete_ps1_after:
del sys.ps1
if delete_ps2_after:
del sys.ps2
if exitmsg is None:
self.write('now exiting %s...\n' % self.__class__.__name__)
elif exitmsg != '':
self.write('%s\n' % exitmsg)
def push(self, line, filename=None, _symbol="single"):
"""Push a line to the interpreter.
The line should not have a trailing newline; it may have
@@ -257,7 +319,9 @@ class InteractiveConsole(InteractiveInterpreter):
"""
self.buffer.append(line)
source = "\n".join(self.buffer)
more = self.runsource(source, self.filename)
if filename is None:
filename = self.filename
more = self.runsource(source, filename, symbol=_symbol)
if not more:
self.resetbuffer()
return more
@@ -276,8 +340,22 @@ class InteractiveConsole(InteractiveInterpreter):
return input(prompt)
class Quitter:
def __init__(self, name):
self.name = name
if sys.platform == "win32":
self.eof = 'Ctrl-Z plus Return'
else:
self.eof = 'Ctrl-D (i.e. EOF)'
def interact(banner=None, readfunc=None, local=None, exitmsg=None):
def __repr__(self):
return f'Use {self.name} or {self.eof} to exit'
def __call__(self, code=None):
raise SystemExit(code)
def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False):
"""Closely emulate the interactive Python interpreter.
This is a backwards compatible interface to the InteractiveConsole
@@ -290,14 +368,15 @@ def interact(banner=None, readfunc=None, local=None, exitmsg=None):
readfunc -- if not None, replaces InteractiveConsole.raw_input()
local -- passed to InteractiveInterpreter.__init__()
exitmsg -- passed to InteractiveConsole.interact()
local_exit -- passed to InteractiveConsole.__init__()
"""
console = InteractiveConsole(local)
console = InteractiveConsole(local, local_exit=local_exit)
if readfunc is not None:
console.raw_input = readfunc
else:
try:
import readline
import readline # noqa: F401
except ImportError:
pass
console.interact(banner, exitmsg)
@@ -306,7 +385,7 @@ def interact(banner=None, readfunc=None, local=None, exitmsg=None):
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(color=True)
parser.add_argument('-q', action='store_true',
help="don't print version and copyright messages")
args = parser.parse_args()

42
Lib/codecs.py vendored
View File

@@ -111,6 +111,9 @@ class CodecInfo(tuple):
(self.__class__.__module__, self.__class__.__qualname__,
self.name, id(self))
def __getnewargs__(self):
return tuple(self)
class Codec:
""" Defines the interface for stateless encoders/decoders.
@@ -615,7 +618,7 @@ class StreamReader(Codec):
method and are included in the list entries.
sizehint, if given, is ignored since there is no efficient
way to finding the true end-of-line.
way of finding the true end-of-line.
"""
data = self.read()
@@ -706,13 +709,13 @@ class StreamReaderWriter:
return self.reader.read(size)
def readline(self, size=None):
def readline(self, size=None, keepends=True):
return self.reader.readline(size)
return self.reader.readline(size, keepends)
def readlines(self, sizehint=None):
def readlines(self, sizehint=None, keepends=True):
return self.reader.readlines(sizehint)
return self.reader.readlines(sizehint, keepends)
def __next__(self):
@@ -881,7 +884,6 @@ class StreamRecoder:
### Shortcuts
def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
""" Open an encoded file using the given mode and return
a wrapped version providing transparent encoding/decoding.
@@ -909,8 +911,11 @@ def open(filename, mode='r', encoding=None, errors='strict', buffering=-1):
.encoding which allows querying the used encoding. This
attribute is only available if an encoding was specified as
parameter.
"""
import warnings
warnings.warn("codecs.open() is deprecated. Use open() instead.",
DeprecationWarning, stacklevel=2)
if encoding is not None and \
'b' not in mode:
# Force opening of the file in binary mode
@@ -1106,24 +1111,15 @@ def make_encoding_map(decoding_map):
### error handlers
try:
strict_errors = lookup_error("strict")
ignore_errors = lookup_error("ignore")
replace_errors = lookup_error("replace")
xmlcharrefreplace_errors = lookup_error("xmlcharrefreplace")
backslashreplace_errors = lookup_error("backslashreplace")
namereplace_errors = lookup_error("namereplace")
except LookupError:
# In --disable-unicode builds, these error handler are missing
strict_errors = None
ignore_errors = None
replace_errors = None
xmlcharrefreplace_errors = None
backslashreplace_errors = None
namereplace_errors = None
strict_errors = lookup_error("strict")
ignore_errors = lookup_error("ignore")
replace_errors = lookup_error("replace")
xmlcharrefreplace_errors = lookup_error("xmlcharrefreplace")
backslashreplace_errors = lookup_error("backslashreplace")
namereplace_errors = lookup_error("namereplace")
# Tell modulefinder that using codecs probably needs the encodings
# package
_false = 0
if _false:
import encodings
import encodings # noqa: F401

32
Lib/codeop.py vendored
View File

@@ -44,9 +44,10 @@ __all__ = ["compile_command", "Compile", "CommandCompiler"]
# Caveat emptor: These flags are undocumented on purpose and depending
# on their effect outside the standard library is **unsupported**.
PyCF_DONT_IMPLY_DEDENT = 0x200
PyCF_ONLY_AST = 0x400
PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000
def _maybe_compile(compiler, source, filename, symbol):
def _maybe_compile(compiler, source, filename, symbol, flags):
# Check for source consisting of only blank lines and comments.
for line in source.split("\n"):
line = line.strip()
@@ -60,10 +61,10 @@ def _maybe_compile(compiler, source, filename, symbol):
with warnings.catch_warnings():
warnings.simplefilter("ignore", (SyntaxWarning, DeprecationWarning))
try:
compiler(source, filename, symbol)
compiler(source, filename, symbol, flags=flags)
except SyntaxError: # Let other compile() errors propagate.
try:
compiler(source + "\n", filename, symbol)
compiler(source + "\n", filename, symbol, flags=flags)
return None
except _IncompleteInputError as e:
return None
@@ -73,24 +74,13 @@ def _maybe_compile(compiler, source, filename, symbol):
return compiler(source, filename, symbol, incomplete_input=False)
def _is_syntax_error(err1, err2):
rep1 = repr(err1)
rep2 = repr(err2)
if "was never closed" in rep1 and "was never closed" in rep2:
return False
if rep1 == rep2:
return True
return False
def _compile(source, filename, symbol, incomplete_input=True):
flags = 0
def _compile(source, filename, symbol, incomplete_input=True, *, flags=0):
if incomplete_input:
flags |= PyCF_ALLOW_INCOMPLETE_INPUT
flags |= PyCF_DONT_IMPLY_DEDENT
return compile(source, filename, symbol, flags)
def compile_command(source, filename="<input>", symbol="single"):
def compile_command(source, filename="<input>", symbol="single", flags=0):
r"""Compile a command and determine whether it is incomplete.
Arguments:
@@ -109,7 +99,7 @@ def compile_command(source, filename="<input>", symbol="single"):
syntax error (OverflowError and ValueError can be produced by
malformed literals).
"""
return _maybe_compile(_compile, source, filename, symbol)
return _maybe_compile(_compile, source, filename, symbol, flags)
class Compile:
"""Instances of this class behave much like the built-in compile
@@ -119,12 +109,14 @@ class Compile:
def __init__(self):
self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT
def __call__(self, source, filename, symbol, **kwargs):
flags = self.flags
def __call__(self, source, filename, symbol, flags=0, **kwargs):
flags |= self.flags
if kwargs.get('incomplete_input', True) is False:
flags &= ~PyCF_DONT_IMPLY_DEDENT
flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT
codeob = compile(source, filename, symbol, flags, True)
if flags & PyCF_ONLY_AST:
return codeob # this is an ast.Module in this case
for feature in _features:
if codeob.co_flags & feature.compiler_flag:
self.flags |= feature.compiler_flag
@@ -159,4 +151,4 @@ class CommandCompiler:
syntax error (OverflowError and ValueError can be produced by
malformed literals).
"""
return _maybe_compile(self.compiler, source, filename, symbol)
return _maybe_compile(self.compiler, source, filename, symbol, flags=self.compiler.flags)

View File

@@ -29,6 +29,9 @@ __all__ = [
import _collections_abc
import sys as _sys
_sys.modules['collections.abc'] = _collections_abc
abc = _collections_abc
from itertools import chain as _chain
from itertools import repeat as _repeat
from itertools import starmap as _starmap
@@ -46,19 +49,19 @@ else:
_collections_abc.MutableSequence.register(deque)
try:
from _collections import _deque_iterator
# Expose _deque_iterator to support pickling deque iterators
from _collections import _deque_iterator # noqa: F401
except ImportError:
pass
try:
from _collections import defaultdict
except ImportError:
# FIXME: try to implement defaultdict in collections.rs rather than in Python
# I (coolreader18) couldn't figure out some class stuff with __new__ and
# __init__ and __missing__ and subclassing built-in types from Rust, so I went
# with this instead.
# TODO: RUSTPYTHON - implement defaultdict in Rust
from ._defaultdict import defaultdict
heapq = None # Lazily imported
################################################################################
### OrderedDict
@@ -461,7 +464,7 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non
def _replace(self, /, **kwds):
result = self._make(_map(kwds.pop, field_names, self))
if kwds:
raise ValueError(f'Got unexpected field names: {list(kwds)!r}')
raise TypeError(f'Got unexpected field names: {list(kwds)!r}')
return result
_replace.__doc__ = (f'Return a new {typename} object replacing specified '
@@ -499,6 +502,7 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non
'_field_defaults': field_defaults,
'__new__': __new__,
'_make': _make,
'__replace__': _replace,
'_replace': _replace,
'__repr__': __repr__,
'_asdict': _asdict,
@@ -592,7 +596,7 @@ class Counter(dict):
# References:
# http://en.wikipedia.org/wiki/Multiset
# http://www.gnu.org/software/smalltalk/manual-base/html_node/Bag.html
# http://www.demo2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm
# http://www.java2s.com/Tutorial/Cpp/0380__set-multiset/Catalog0380__set-multiset.htm
# http://code.activestate.com/recipes/259174/
# Knuth, TAOCP Vol. II section 4.6.3
@@ -632,7 +636,10 @@ class Counter(dict):
return sorted(self.items(), key=_itemgetter(1), reverse=True)
# Lazy import to speedup Python startup time
import heapq
global heapq
if heapq is None:
import heapq
return heapq.nlargest(n, self.items(), key=_itemgetter(1))
def elements(self):
@@ -642,7 +649,8 @@ class Counter(dict):
>>> sorted(c.elements())
['A', 'A', 'B', 'B', 'C', 'C']
# Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
>>> import math
>>> prime_factors = Counter({2: 2, 3: 3, 17: 1})
>>> math.prod(prime_factors.elements())
@@ -683,7 +691,7 @@ class Counter(dict):
'''
# The regular dict.update() operation makes no sense here because the
# replace behavior results in the some of original untouched counts
# replace behavior results in some of the original untouched counts
# being mixed-in with all of the other counts for a mismash that
# doesn't have a straight-forward interpretation in most counting
# contexts. Instead, we implement straight-addition. Both the inputs
@@ -1018,7 +1026,7 @@ class ChainMap(_collections_abc.MutableMapping):
return self.__missing__(key) # support subclasses that define __missing__
def get(self, key, default=None):
return self[key] if key in self else default
return self[key] if key in self else default # needs to make use of __contains__
def __len__(self):
return len(set().union(*self.maps)) # reuses stored hash values if possible
@@ -1030,7 +1038,10 @@ class ChainMap(_collections_abc.MutableMapping):
return iter(d)
def __contains__(self, key):
return any(key in m for m in self.maps)
for mapping in self.maps:
if key in mapping:
return True
return False
def __bool__(self):
return any(self.maps)
@@ -1040,9 +1051,9 @@ class ChainMap(_collections_abc.MutableMapping):
return f'{self.__class__.__name__}({", ".join(map(repr, self.maps))})'
@classmethod
def fromkeys(cls, iterable, *args):
'Create a ChainMap with a single dict created from the iterable.'
return cls(dict.fromkeys(iterable, *args))
def fromkeys(cls, iterable, value=None, /):
'Create a new ChainMap with keys from iterable and values set to value.'
return cls(dict.fromkeys(iterable, value))
def copy(self):
'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
@@ -1485,6 +1496,8 @@ class UserString(_collections_abc.Sequence):
return self.data.format_map(mapping)
def index(self, sub, start=0, end=_sys.maxsize):
if isinstance(sub, UserString):
sub = sub.data
return self.data.index(sub, start, end)
def isalpha(self):
@@ -1553,6 +1566,8 @@ class UserString(_collections_abc.Sequence):
return self.data.rfind(sub, start, end)
def rindex(self, sub, start=0, end=_sys.maxsize):
if isinstance(sub, UserString):
sub = sub.data
return self.data.rindex(sub, start, end)
def rjust(self, width, *args):

View File

@@ -1,3 +0,0 @@
from _collections_abc import *
from _collections_abc import __all__
from _collections_abc import _CallableGenericAlias

4
Lib/compileall.py vendored
View File

@@ -317,7 +317,9 @@ def main():
import argparse
parser = argparse.ArgumentParser(
description='Utilities to support installing Python libraries.')
description='Utilities to support installing Python libraries.',
color=True,
)
parser.add_argument('-l', action='store_const', const=0,
default=None, dest='maxlevels',
help="don't recurse into subdirectories")

0
Lib/compression/__init__.py vendored Normal file
View File

0
Lib/compression/_common/__init__.py vendored Normal file
View File

View File

@@ -1,4 +1,4 @@
"""Internal classes used by the gzip, lzma and bz2 modules"""
"""Internal classes used by compression modules"""
import io
import sys

5
Lib/compression/bz2.py vendored Normal file
View File

@@ -0,0 +1,5 @@
import bz2
__doc__ = bz2.__doc__
del bz2
from bz2 import *

5
Lib/compression/gzip.py vendored Normal file
View File

@@ -0,0 +1,5 @@
import gzip
__doc__ = gzip.__doc__
del gzip
from gzip import *

5
Lib/compression/lzma.py vendored Normal file
View File

@@ -0,0 +1,5 @@
import lzma
__doc__ = lzma.__doc__
del lzma
from lzma import *

5
Lib/compression/zlib.py vendored Normal file
View File

@@ -0,0 +1,5 @@
import zlib
__doc__ = zlib.__doc__
del zlib
from zlib import *

242
Lib/compression/zstd/__init__.py vendored Normal file
View File

@@ -0,0 +1,242 @@
"""Python bindings to the Zstandard (zstd) compression library (RFC-8878)."""
__all__ = (
# compression.zstd
'COMPRESSION_LEVEL_DEFAULT',
'compress',
'CompressionParameter',
'decompress',
'DecompressionParameter',
'finalize_dict',
'get_frame_info',
'Strategy',
'train_dict',
# compression.zstd._zstdfile
'open',
'ZstdFile',
# _zstd
'get_frame_size',
'zstd_version',
'zstd_version_info',
'ZstdCompressor',
'ZstdDecompressor',
'ZstdDict',
'ZstdError',
)
import _zstd
import enum
from _zstd import (ZstdCompressor, ZstdDecompressor, ZstdDict, ZstdError,
get_frame_size, zstd_version)
from compression.zstd._zstdfile import ZstdFile, open, _nbytes
# zstd_version_number is (MAJOR * 100 * 100 + MINOR * 100 + RELEASE)
zstd_version_info = (*divmod(_zstd.zstd_version_number // 100, 100),
_zstd.zstd_version_number % 100)
"""Version number of the runtime zstd library as a tuple of integers."""
COMPRESSION_LEVEL_DEFAULT = _zstd.ZSTD_CLEVEL_DEFAULT
"""The default compression level for Zstandard, currently '3'."""
class FrameInfo:
"""Information about a Zstandard frame."""
__slots__ = 'decompressed_size', 'dictionary_id'
def __init__(self, decompressed_size, dictionary_id):
super().__setattr__('decompressed_size', decompressed_size)
super().__setattr__('dictionary_id', dictionary_id)
def __repr__(self):
return (f'FrameInfo(decompressed_size={self.decompressed_size}, '
f'dictionary_id={self.dictionary_id})')
def __setattr__(self, name, _):
raise AttributeError(f"can't set attribute {name!r}")
def get_frame_info(frame_buffer):
"""Get Zstandard frame information from a frame header.
*frame_buffer* is a bytes-like object. It should start from the beginning
of a frame, and needs to include at least the frame header (6 to 18 bytes).
The returned FrameInfo object has two attributes.
'decompressed_size' is the size in bytes of the data in the frame when
decompressed, or None when the decompressed size is unknown.
'dictionary_id' is an int in the range (0, 2**32). The special value 0
means that the dictionary ID was not recorded in the frame header,
the frame may or may not need a dictionary to be decoded,
and the ID of such a dictionary is not specified.
"""
return FrameInfo(*_zstd.get_frame_info(frame_buffer))
def train_dict(samples, dict_size):
"""Return a ZstdDict representing a trained Zstandard dictionary.
*samples* is an iterable of samples, where a sample is a bytes-like
object representing a file.
*dict_size* is the dictionary's maximum size, in bytes.
"""
if not isinstance(dict_size, int):
ds_cls = type(dict_size).__qualname__
raise TypeError(f'dict_size must be an int object, not {ds_cls!r}.')
samples = tuple(samples)
chunks = b''.join(samples)
chunk_sizes = tuple(_nbytes(sample) for sample in samples)
if not chunks:
raise ValueError("samples contained no data; can't train dictionary.")
dict_content = _zstd.train_dict(chunks, chunk_sizes, dict_size)
return ZstdDict(dict_content)
def finalize_dict(zstd_dict, /, samples, dict_size, level):
"""Return a ZstdDict representing a finalized Zstandard dictionary.
Given a custom content as a basis for dictionary, and a set of samples,
finalize *zstd_dict* by adding headers and statistics according to the
Zstandard dictionary format.
You may compose an effective dictionary content by hand, which is used as
basis dictionary, and use some samples to finalize a dictionary. The basis
dictionary may be a "raw content" dictionary. See *is_raw* in ZstdDict.
*samples* is an iterable of samples, where a sample is a bytes-like object
representing a file.
*dict_size* is the dictionary's maximum size, in bytes.
*level* is the expected compression level. The statistics for each
compression level differ, so tuning the dictionary to the compression level
can provide improvements.
"""
if not isinstance(zstd_dict, ZstdDict):
raise TypeError('zstd_dict argument should be a ZstdDict object.')
if not isinstance(dict_size, int):
raise TypeError('dict_size argument should be an int object.')
if not isinstance(level, int):
raise TypeError('level argument should be an int object.')
samples = tuple(samples)
chunks = b''.join(samples)
chunk_sizes = tuple(_nbytes(sample) for sample in samples)
if not chunks:
raise ValueError("The samples are empty content, can't finalize the "
"dictionary.")
dict_content = _zstd.finalize_dict(zstd_dict.dict_content, chunks,
chunk_sizes, dict_size, level)
return ZstdDict(dict_content)
def compress(data, level=None, options=None, zstd_dict=None):
"""Return Zstandard compressed *data* as bytes.
*level* is an int specifying the compression level to use, defaulting to
COMPRESSION_LEVEL_DEFAULT ('3').
*options* is a dict object that contains advanced compression
parameters. See CompressionParameter for more on options.
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See
the function train_dict for how to train a ZstdDict on sample data.
For incremental compression, use a ZstdCompressor instead.
"""
comp = ZstdCompressor(level=level, options=options, zstd_dict=zstd_dict)
return comp.compress(data, mode=ZstdCompressor.FLUSH_FRAME)
def decompress(data, zstd_dict=None, options=None):
"""Decompress one or more frames of Zstandard compressed *data*.
*zstd_dict* is a ZstdDict object, a pre-trained Zstandard dictionary. See
the function train_dict for how to train a ZstdDict on sample data.
*options* is a dict object that contains advanced compression
parameters. See DecompressionParameter for more on options.
For incremental decompression, use a ZstdDecompressor instead.
"""
results = []
while True:
decomp = ZstdDecompressor(options=options, zstd_dict=zstd_dict)
results.append(decomp.decompress(data))
if not decomp.eof:
raise ZstdError('Compressed data ended before the '
'end-of-stream marker was reached')
data = decomp.unused_data
if not data:
break
return b''.join(results)
class CompressionParameter(enum.IntEnum):
"""Compression parameters."""
compression_level = _zstd.ZSTD_c_compressionLevel
window_log = _zstd.ZSTD_c_windowLog
hash_log = _zstd.ZSTD_c_hashLog
chain_log = _zstd.ZSTD_c_chainLog
search_log = _zstd.ZSTD_c_searchLog
min_match = _zstd.ZSTD_c_minMatch
target_length = _zstd.ZSTD_c_targetLength
strategy = _zstd.ZSTD_c_strategy
enable_long_distance_matching = _zstd.ZSTD_c_enableLongDistanceMatching
ldm_hash_log = _zstd.ZSTD_c_ldmHashLog
ldm_min_match = _zstd.ZSTD_c_ldmMinMatch
ldm_bucket_size_log = _zstd.ZSTD_c_ldmBucketSizeLog
ldm_hash_rate_log = _zstd.ZSTD_c_ldmHashRateLog
content_size_flag = _zstd.ZSTD_c_contentSizeFlag
checksum_flag = _zstd.ZSTD_c_checksumFlag
dict_id_flag = _zstd.ZSTD_c_dictIDFlag
nb_workers = _zstd.ZSTD_c_nbWorkers
job_size = _zstd.ZSTD_c_jobSize
overlap_log = _zstd.ZSTD_c_overlapLog
def bounds(self):
"""Return the (lower, upper) int bounds of a compression parameter.
Both the lower and upper bounds are inclusive.
"""
return _zstd.get_param_bounds(self.value, is_compress=True)
class DecompressionParameter(enum.IntEnum):
"""Decompression parameters."""
window_log_max = _zstd.ZSTD_d_windowLogMax
def bounds(self):
"""Return the (lower, upper) int bounds of a decompression parameter.
Both the lower and upper bounds are inclusive.
"""
return _zstd.get_param_bounds(self.value, is_compress=False)
class Strategy(enum.IntEnum):
"""Compression strategies, listed from fastest to strongest.
Note that new strategies might be added in the future.
Only the order (from fast to strong) is guaranteed,
the numeric value might change.
"""
fast = _zstd.ZSTD_fast
dfast = _zstd.ZSTD_dfast
greedy = _zstd.ZSTD_greedy
lazy = _zstd.ZSTD_lazy
lazy2 = _zstd.ZSTD_lazy2
btlazy2 = _zstd.ZSTD_btlazy2
btopt = _zstd.ZSTD_btopt
btultra = _zstd.ZSTD_btultra
btultra2 = _zstd.ZSTD_btultra2
# Check validity of the CompressionParameter & DecompressionParameter types
_zstd.set_parameter_types(CompressionParameter, DecompressionParameter)

345
Lib/compression/zstd/_zstdfile.py vendored Normal file
View File

@@ -0,0 +1,345 @@
import io
from os import PathLike
from _zstd import ZstdCompressor, ZstdDecompressor, ZSTD_DStreamOutSize
from compression._common import _streams
__all__ = ('ZstdFile', 'open')
_MODE_CLOSED = 0
_MODE_READ = 1
_MODE_WRITE = 2
def _nbytes(dat, /):
if isinstance(dat, (bytes, bytearray)):
return len(dat)
with memoryview(dat) as mv:
return mv.nbytes
class ZstdFile(_streams.BaseStream):
"""A file-like object providing transparent Zstandard (de)compression.
A ZstdFile can act as a wrapper for an existing file object, or refer
directly to a named file on disk.
ZstdFile provides a *binary* file interface. Data is read and returned as
bytes, and may only be written to objects that support the Buffer Protocol.
"""
FLUSH_BLOCK = ZstdCompressor.FLUSH_BLOCK
FLUSH_FRAME = ZstdCompressor.FLUSH_FRAME
def __init__(self, file, /, mode='r', *,
level=None, options=None, zstd_dict=None):
"""Open a Zstandard compressed file in binary mode.
*file* can be either an file-like object, or a file name to open.
*mode* can be 'r' for reading (default), 'w' for (over)writing, 'x' for
creating exclusively, or 'a' for appending. These can equivalently be
given as 'rb', 'wb', 'xb' and 'ab' respectively.
*level* is an optional int specifying the compression level to use,
or COMPRESSION_LEVEL_DEFAULT if not given.
*options* is an optional dict for advanced compression parameters.
See CompressionParameter and DecompressionParameter for the possible
options.
*zstd_dict* is an optional ZstdDict object, a pre-trained Zstandard
dictionary. See train_dict() to train ZstdDict on sample data.
"""
self._fp = None
self._close_fp = False
self._mode = _MODE_CLOSED
self._buffer = None
if not isinstance(mode, str):
raise ValueError('mode must be a str')
if options is not None and not isinstance(options, dict):
raise TypeError('options must be a dict or None')
mode = mode.removesuffix('b') # handle rb, wb, xb, ab
if mode == 'r':
if level is not None:
raise TypeError('level is illegal in read mode')
self._mode = _MODE_READ
elif mode in {'w', 'a', 'x'}:
if level is not None and not isinstance(level, int):
raise TypeError('level must be int or None')
self._mode = _MODE_WRITE
self._compressor = ZstdCompressor(level=level, options=options,
zstd_dict=zstd_dict)
self._pos = 0
else:
raise ValueError(f'Invalid mode: {mode!r}')
if isinstance(file, (str, bytes, PathLike)):
self._fp = io.open(file, f'{mode}b')
self._close_fp = True
elif ((mode == 'r' and hasattr(file, 'read'))
or (mode != 'r' and hasattr(file, 'write'))):
self._fp = file
else:
raise TypeError('file must be a file-like object '
'or a str, bytes, or PathLike object')
if self._mode == _MODE_READ:
raw = _streams.DecompressReader(
self._fp,
ZstdDecompressor,
zstd_dict=zstd_dict,
options=options,
)
self._buffer = io.BufferedReader(raw)
def close(self):
"""Flush and close the file.
May be called multiple times. Once the file has been closed,
any other operation on it will raise ValueError.
"""
if self._fp is None:
return
try:
if self._mode == _MODE_READ:
if getattr(self, '_buffer', None):
self._buffer.close()
self._buffer = None
elif self._mode == _MODE_WRITE:
self.flush(self.FLUSH_FRAME)
self._compressor = None
finally:
self._mode = _MODE_CLOSED
try:
if self._close_fp:
self._fp.close()
finally:
self._fp = None
self._close_fp = False
def write(self, data, /):
"""Write a bytes-like object *data* to the file.
Returns the number of uncompressed bytes written, which is
always the length of data in bytes. Note that due to buffering,
the file on disk may not reflect the data written until .flush()
or .close() is called.
"""
self._check_can_write()
length = _nbytes(data)
compressed = self._compressor.compress(data)
self._fp.write(compressed)
self._pos += length
return length
def flush(self, mode=FLUSH_BLOCK):
"""Flush remaining data to the underlying stream.
The mode argument can be FLUSH_BLOCK or FLUSH_FRAME. Abuse of this
method will reduce compression ratio, use it only when necessary.
If the program is interrupted afterwards, all data can be recovered.
To ensure saving to disk, also need to use os.fsync(fd).
This method does nothing in reading mode.
"""
if self._mode == _MODE_READ:
return
self._check_not_closed()
if mode not in {self.FLUSH_BLOCK, self.FLUSH_FRAME}:
raise ValueError('Invalid mode argument, expected either '
'ZstdFile.FLUSH_FRAME or '
'ZstdFile.FLUSH_BLOCK')
if self._compressor.last_mode == mode:
return
# Flush zstd block/frame, and write.
data = self._compressor.flush(mode)
self._fp.write(data)
if hasattr(self._fp, 'flush'):
self._fp.flush()
def read(self, size=-1):
"""Read up to size uncompressed bytes from the file.
If size is negative or omitted, read until EOF is reached.
Returns b'' if the file is already at EOF.
"""
if size is None:
size = -1
self._check_can_read()
return self._buffer.read(size)
def read1(self, size=-1):
"""Read up to size uncompressed bytes, while trying to avoid
making multiple reads from the underlying stream. Reads up to a
buffer's worth of data if size is negative.
Returns b'' if the file is at EOF.
"""
self._check_can_read()
if size < 0:
# Note this should *not* be io.DEFAULT_BUFFER_SIZE.
# ZSTD_DStreamOutSize is the minimum amount to read guaranteeing
# a full block is read.
size = ZSTD_DStreamOutSize
return self._buffer.read1(size)
def readinto(self, b):
"""Read bytes into b.
Returns the number of bytes read (0 for EOF).
"""
self._check_can_read()
return self._buffer.readinto(b)
def readinto1(self, b):
"""Read bytes into b, while trying to avoid making multiple reads
from the underlying stream.
Returns the number of bytes read (0 for EOF).
"""
self._check_can_read()
return self._buffer.readinto1(b)
def readline(self, size=-1):
"""Read a line of uncompressed bytes from the file.
The terminating newline (if present) is retained. If size is
non-negative, no more than size bytes will be read (in which
case the line may be incomplete). Returns b'' if already at EOF.
"""
self._check_can_read()
return self._buffer.readline(size)
def seek(self, offset, whence=io.SEEK_SET):
"""Change the file position.
The new position is specified by offset, relative to the
position indicated by whence. Possible values for whence are:
0: start of stream (default): offset must not be negative
1: current stream position
2: end of stream; offset must not be positive
Returns the new file position.
Note that seeking is emulated, so depending on the arguments,
this operation may be extremely slow.
"""
self._check_can_read()
# BufferedReader.seek() checks seekable
return self._buffer.seek(offset, whence)
def peek(self, size=-1):
"""Return buffered data without advancing the file position.
Always returns at least one byte of data, unless at EOF.
The exact number of bytes returned is unspecified.
"""
# Relies on the undocumented fact that BufferedReader.peek() always
# returns at least one byte (except at EOF)
self._check_can_read()
return self._buffer.peek(size)
def __next__(self):
if ret := self._buffer.readline():
return ret
raise StopIteration
def tell(self):
"""Return the current file position."""
self._check_not_closed()
if self._mode == _MODE_READ:
return self._buffer.tell()
elif self._mode == _MODE_WRITE:
return self._pos
def fileno(self):
"""Return the file descriptor for the underlying file."""
self._check_not_closed()
return self._fp.fileno()
@property
def name(self):
self._check_not_closed()
return self._fp.name
@property
def mode(self):
return 'wb' if self._mode == _MODE_WRITE else 'rb'
@property
def closed(self):
"""True if this file is closed."""
return self._mode == _MODE_CLOSED
def seekable(self):
"""Return whether the file supports seeking."""
return self.readable() and self._buffer.seekable()
def readable(self):
"""Return whether the file was opened for reading."""
self._check_not_closed()
return self._mode == _MODE_READ
def writable(self):
"""Return whether the file was opened for writing."""
self._check_not_closed()
return self._mode == _MODE_WRITE
def open(file, /, mode='rb', *, level=None, options=None, zstd_dict=None,
encoding=None, errors=None, newline=None):
"""Open a Zstandard compressed file in binary or text mode.
file can be either a file name (given as a str, bytes, or PathLike object),
in which case the named file is opened, or it can be an existing file object
to read from or write to.
The mode parameter can be 'r', 'rb' (default), 'w', 'wb', 'x', 'xb', 'a',
'ab' for binary mode, or 'rt', 'wt', 'xt', 'at' for text mode.
The level, options, and zstd_dict parameters specify the settings the same
as ZstdFile.
When using read mode (decompression), the options parameter is a dict
representing advanced decompression options. The level parameter is not
supported in this case. When using write mode (compression), only one of
level, an int representing the compression level, or options, a dict
representing advanced compression options, may be passed. In both modes,
zstd_dict is a ZstdDict instance containing a trained Zstandard dictionary.
For binary mode, this function is equivalent to the ZstdFile constructor:
ZstdFile(filename, mode, ...). In this case, the encoding, errors and
newline parameters must not be provided.
For text mode, an ZstdFile object is created, and wrapped in an
io.TextIOWrapper instance with the specified encoding, error handling
behavior, and line ending(s).
"""
text_mode = 't' in mode
mode = mode.replace('t', '')
if text_mode:
if 'b' in mode:
raise ValueError(f'Invalid mode: {mode!r}')
else:
if encoding is not None:
raise ValueError('Argument "encoding" not supported in binary mode')
if errors is not None:
raise ValueError('Argument "errors" not supported in binary mode')
if newline is not None:
raise ValueError('Argument "newline" not supported in binary mode')
binary_file = ZstdFile(file, mode, level=level, options=options,
zstd_dict=zstd_dict)
if text_mode:
return io.TextIOWrapper(binary_file, encoding, errors, newline)
else:
return binary_file

View File

@@ -23,6 +23,7 @@ __all__ = (
'ALL_COMPLETED',
'CancelledError',
'TimeoutError',
'InvalidStateError',
'BrokenExecutor',
'Future',
'Executor',
@@ -50,4 +51,4 @@ def __getattr__(name):
ThreadPoolExecutor = te
return te
raise AttributeError(f"module {__name__} has no attribute {name}")
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

View File

@@ -50,9 +50,7 @@ class CancelledError(Error):
"""The Future was cancelled."""
pass
class TimeoutError(Error):
"""The operation exceeded the given deadline."""
pass
TimeoutError = TimeoutError # make local alias for the standard exception
class InvalidStateError(Error):
"""The operation is not allowed in this state."""
@@ -284,7 +282,7 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
A named 2-tuple of sets. The first set, named 'done', contains the
futures that completed (is finished or cancelled) before the wait
completed. The second set, named 'not_done', contains uncompleted
futures. Duplicate futures given to *fs* are removed and will be
futures. Duplicate futures given to *fs* are removed and will be
returned only once.
"""
fs = set(fs)
@@ -312,6 +310,18 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
done.update(waiter.finished_futures)
return DoneAndNotDoneFutures(done, fs - done)
def _result_or_cancel(fut, timeout=None):
try:
try:
return fut.result(timeout)
finally:
fut.cancel()
finally:
# Break a reference cycle with the exception in self._exception
del fut
class Future(object):
"""Represents the result of an asynchronous computation."""
@@ -386,7 +396,7 @@ class Future(object):
return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]
def __get_result(self):
if self._exception:
if self._exception is not None:
try:
raise self._exception
finally:
@@ -606,9 +616,9 @@ class Executor(object):
while fs:
# Careful not to keep a reference to the popped future
if timeout is None:
yield fs.pop().result()
yield _result_or_cancel(fs.pop())
else:
yield fs.pop().result(end_time - time.monotonic())
yield _result_or_cancel(fs.pop(), end_time - time.monotonic())
finally:
for future in fs:
future.cancel()

Some files were not shown because too many files have changed in this diff Show More