From e096ce7bc75c703f0a6be897d82308126ee98f40 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sat, 1 Nov 2025 18:30:03 +0900 Subject: [PATCH 0001/1459] 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> 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 --- Lib/test/test_property.py | 1 - vm/src/builtins/property.rs | 45 ++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_property.py b/Lib/test/test_property.py index 65153395b7e..49dc2331e94 100644 --- a/Lib/test/test_property.py +++ b/Lib/test/test_property.py @@ -201,7 +201,6 @@ def test_gh_115618(self): self.assertIsNone(prop.fdel) self.assertAlmostEqual(gettotalrefcount() - refs_before, 0, delta=10) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_property_name(self): def getter(self): return 42 diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index 1568cb08d8a..a75357f7611 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -67,20 +67,30 @@ impl GetDescriptor for PyProperty { #[pyclass(with(Constructor, Initializer, GetDescriptor), flags(BASETYPE))] impl PyProperty { // Helper method to get property name - fn get_property_name(&self, vm: &VirtualMachine) -> Option { + // Returns the name if available, None if not found, or propagates errors + fn get_property_name(&self, vm: &VirtualMachine) -> PyResult> { // First check if name was set via __set_name__ if let Some(name) = self.name.read().as_ref() { - return Some(name.clone()); + return Ok(Some(name.clone())); } - // Otherwise try to get __name__ from getter - if let Some(getter) = self.getter.read().as_ref() - && let Ok(name) = getter.get_attr("__name__", vm) - { - return Some(name); - } + let getter = self.getter.read(); + let Some(getter) = getter.as_ref() else { + return Ok(None); + }; - None + match getter.get_attr("__name__", vm) { + Ok(name) => Ok(Some(name)), + Err(e) => { + // If it's an AttributeError from the getter, return None + // Otherwise, propagate the original exception (e.g., RuntimeError) + if e.class().is(vm.ctx.exceptions.attribute_error) { + Ok(None) + } else { + Err(e) + } + } + } } // Descriptor methods @@ -143,6 +153,21 @@ impl PyProperty { self.deleter.read().clone() } + #[pygetset(name = "__name__")] + fn name_getter(&self, vm: &VirtualMachine) -> PyResult { + match self.get_property_name(vm)? { + Some(name) => Ok(name), + None => Err( + vm.new_attribute_error("'property' object has no attribute '__name__'".to_owned()) + ), + } + } + + #[pygetset(name = "__name__", setter)] + fn name_setter(&self, value: PyObjectRef) { + *self.name.write() = Some(value); + } + fn doc_getter(&self) -> Option { self.doc.read().clone() } @@ -288,7 +313,7 @@ impl PyProperty { error_type: &str, vm: &VirtualMachine, ) -> PyResult { - let prop_name = self.get_property_name(vm); + let prop_name = self.get_property_name(vm)?; let obj_type = obj.class(); let qualname = obj_type.__qualname__(vm); From a7e8ac684b0d225e7de34ab9c7a03f024934f5ac Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:01:02 +0200 Subject: [PATCH 0002/1459] Remove user defined docstrings (#6232) --- vm/src/builtins/str.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 3ee5e3369dd..80da626f318 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -890,10 +890,6 @@ impl PyStr { ) } - /// Return a str with the given prefix string removed if present. - /// - /// If the string starts with the prefix string, return string[len(prefix):] - /// Otherwise, return a copy of the original string. #[pymethod] fn removeprefix(&self, pref: PyStrRef) -> Wtf8Buf { self.as_wtf8() @@ -901,10 +897,6 @@ impl PyStr { .to_owned() } - /// Return a str with the given suffix string removed if present. - /// - /// If the string ends with the suffix string, return string[:len(suffix)] - /// Otherwise, return a copy of the original string. #[pymethod] fn removesuffix(&self, suffix: PyStrRef) -> Wtf8Buf { self.as_wtf8() @@ -955,10 +947,6 @@ impl PyStr { format(&format_str, &args, vm) } - /// S.format_map(mapping) -> str - /// - /// Return a formatted version of S, using substitutions from mapping. - /// The substitutions are identified by braces ('{' and '}'). #[pymethod] fn format_map(&self, mapping: PyObjectRef, vm: &VirtualMachine) -> PyResult { let format_string = @@ -989,8 +977,6 @@ impl PyStr { Ok(vm.ctx.new_str(s)) } - /// Return a titlecased version of the string where words start with an - /// uppercase character and the remaining characters are lowercase. #[pymethod] fn title(&self) -> Wtf8Buf { let mut title = Wtf8Buf::with_capacity(self.data.len()); @@ -1066,21 +1052,6 @@ impl PyStr { } } - /// Return true if all characters in the string are printable or the string is empty, - /// false otherwise. Nonprintable characters are those characters defined in the - /// Unicode character database as `Other` or `Separator`, - /// excepting the ASCII space (0x20) which is considered printable. - /// - /// All characters except those characters defined in the Unicode character - /// database as following categories are considered printable. - /// * Cc (Other, Control) - /// * Cf (Other, Format) - /// * Cs (Other, Surrogate) - /// * Co (Other, Private Use) - /// * Cn (Other, Not Assigned) - /// * Zl Separator, Line ('\u2028', LINE SEPARATOR) - /// * Zp Separator, Paragraph ('\u2029', PARAGRAPH SEPARATOR) - /// * Zs (Separator, Space) other than ASCII space('\x20'). #[pymethod] fn isprintable(&self) -> bool { self.char_all(|c| c == '\u{0020}' || rustpython_literal::char::is_printable(c)) @@ -1246,8 +1217,6 @@ impl PyStr { .to_pyobject(vm)) } - /// Return `true` if the sequence is ASCII titlecase and the sequence is not - /// empty, `false` otherwise. #[pymethod] fn istitle(&self) -> bool { if self.data.is_empty() { From 377151a57f1451df5febd09b183d8c4f31ede32d Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Tue, 4 Nov 2025 14:07:22 +0530 Subject: [PATCH 0003/1459] Update CI auto-format (#6233) --------- Signed-off-by: Yash Suthar --- .github/workflows/auto-format.yaml | 64 ++++++++++++++++++++++++++++++ .github/workflows/ci.yaml | 6 +-- 2 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/auto-format.yaml diff --git a/.github/workflows/auto-format.yaml b/.github/workflows/auto-format.yaml new file mode 100644 index 00000000000..2acf83715a0 --- /dev/null +++ b/.github/workflows/auto-format.yaml @@ -0,0 +1,64 @@ +on: + workflow_run: + workflows: ["CI"] + types: + - completed + +name: Auto format + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +jobs: + auto_format_commit: + permissions: + contents: write + pull-requests: write + name: Auto-format code + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' && !contains(github.event.workflow_run.head_commit.message, '[skip ci]') }} + concurrency: + group: fmt-${{ github.event.workflow_run.head_branch }} + cancel-in-progress: true + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: ${{ github.event.workflow_run.head_branch }} + repository: ${{ github.event.workflow_run.head_repository.full_name }} + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Run cargo fmt + run: | + echo "Running cargo fmt --all" + cargo fmt --all + + - name: Commit and push if changes + id: commit + 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 -u + git commit -m "Auto-format code [skip ci]" + git push + echo "formatted=true" >> $GITHUB_OUTPUT + else + echo "formatted=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR if formatting was applied + if: steps.commit.outputs.formatted == 'true' && github.event.workflow_run.event == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + message: | + Code has been automatically formatted. + No action needed. + the changes were committed with `[skip ci]`. diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 977f27f3762..2ce4b475773 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -307,15 +307,13 @@ jobs: run: python -I whats_left.py lint: - name: Check Rust code with rustfmt and clippy + name: Check Rust code with clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy - - name: run rustfmt - run: cargo fmt --check + components: clippy - name: run clippy on wasm run: cargo clippy --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings - uses: actions/setup-python@v6 From 5cad66cebfc62a7912197ae299ac13e8629c560d Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Tue, 4 Nov 2025 23:44:27 +0530 Subject: [PATCH 0004/1459] Revert "Update CI auto-format (#6233)" (#6236) This reverts commit 377151a57f1451df5febd09b183d8c4f31ede32d. --- .github/workflows/auto-format.yaml | 64 ------------------------------ .github/workflows/ci.yaml | 6 ++- 2 files changed, 4 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/auto-format.yaml diff --git a/.github/workflows/auto-format.yaml b/.github/workflows/auto-format.yaml deleted file mode 100644 index 2acf83715a0..00000000000 --- a/.github/workflows/auto-format.yaml +++ /dev/null @@ -1,64 +0,0 @@ -on: - workflow_run: - workflows: ["CI"] - types: - - completed - -name: Auto format - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} - cancel-in-progress: true - -jobs: - auto_format_commit: - permissions: - contents: write - pull-requests: write - name: Auto-format code - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' && !contains(github.event.workflow_run.head_commit.message, '[skip ci]') }} - concurrency: - group: fmt-${{ github.event.workflow_run.head_branch }} - cancel-in-progress: true - - steps: - - name: Checkout code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - ref: ${{ github.event.workflow_run.head_branch }} - repository: ${{ github.event.workflow_run.head_repository.full_name }} - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - - name: Run cargo fmt - run: | - echo "Running cargo fmt --all" - cargo fmt --all - - - name: Commit and push if changes - id: commit - 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 -u - git commit -m "Auto-format code [skip ci]" - git push - echo "formatted=true" >> $GITHUB_OUTPUT - else - echo "formatted=false" >> $GITHUB_OUTPUT - fi - - - name: Comment on PR if formatting was applied - if: steps.commit.outputs.formatted == 'true' && github.event.workflow_run.event == 'pull_request' - uses: marocchino/sticky-pull-request-comment@v2 - with: - message: | - Code has been automatically formatted. - No action needed. - the changes were committed with `[skip ci]`. diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2ce4b475773..977f27f3762 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -307,13 +307,15 @@ jobs: run: python -I whats_left.py lint: - name: Check Rust code with clippy + name: Check Rust code with rustfmt and clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: - components: clippy + 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@v6 From ed9a61d956b0a94fea5c82fd896be18848b13844 Mon Sep 17 00:00:00 2001 From: fanninpm Date: Sun, 9 Nov 2025 04:00:59 -0500 Subject: [PATCH 0005/1459] Add builtin_items updater to whats_left job (#6238) Corresponds to RustPython/rustpython.github.io#81. --- .github/workflows/cron-ci.yaml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cron-ci.yaml b/.github/workflows/cron-ci.yaml index 239733ca306..a94c8df9e23 100644 --- a/.github/workflows/cron-ci.yaml +++ b/.github/workflows/cron-ci.yaml @@ -1,6 +1,6 @@ on: schedule: - - cron: '0 0 * * 6' + - cron: "0 0 * * 6" workflow_dispatch: push: paths: @@ -34,7 +34,7 @@ jobs: 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' @@ -109,6 +109,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 From d8a4a09ec0487b2b5ee96018243de5bb89dca62e Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:47:57 +0900 Subject: [PATCH 0006/1459] Fix #[pyclass(base=...)] notation (#6242) --- derive-impl/src/pyclass.rs | 4 +- derive-impl/src/util.rs | 33 +++- derive/src/lib.rs | 2 +- stdlib/src/ssl.rs | 14 +- vm/src/builtins/bool.rs | 2 +- vm/src/builtins/builtin_func.rs | 2 +- vm/src/exceptions.rs | 136 ++++++++--------- vm/src/stdlib/ast/pyast.rs | 246 +++++++++++++++--------------- vm/src/stdlib/ctypes/array.rs | 4 +- vm/src/stdlib/ctypes/base.rs | 4 +- vm/src/stdlib/ctypes/function.rs | 2 +- vm/src/stdlib/ctypes/structure.rs | 2 +- vm/src/stdlib/ctypes/union.rs | 2 +- vm/src/stdlib/io.rs | 22 +-- 14 files changed, 251 insertions(+), 224 deletions(-) diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 99cddc84623..0b0769cac3c 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -308,7 +308,7 @@ fn generate_class_def( ident: &Ident, name: &str, module_name: Option<&str>, - base: Option, + base: Option, metaclass: Option, unhashable: bool, attrs: &[Attribute], @@ -358,7 +358,6 @@ fn generate_class_def( Some(quote! { rustpython_vm::builtins::PyTuple }) } else { base.as_ref().map(|typ| { - let typ = Ident::new(typ, ident.span()); quote_spanned! { ident.span() => #typ } }) } @@ -382,7 +381,6 @@ fn generate_class_def( }); let base_or_object = if let Some(base) = base { - let base = Ident::new(&base, ident.span()); quote! { #base } } else { quote! { ::rustpython_vm::builtins::PyBaseObject } diff --git a/derive-impl/src/util.rs b/derive-impl/src/util.rs index f7f0d28fb92..379adc65b57 100644 --- a/derive-impl/src/util.rs +++ b/derive-impl/src/util.rs @@ -187,6 +187,35 @@ impl ItemMetaInner { Ok(value) } + pub fn _optional_path(&self, key: &str) -> Result> { + let value = if let Some((_, meta)) = self.meta_map.get(key) { + let Meta::NameValue(syn::MetaNameValue { value, .. }) = meta else { + bail_span!( + meta, + "#[{}({} = ...)] must be a name-value pair", + self.meta_name(), + key + ) + }; + + // Try to parse as a Path (identifier or path like Foo or foo::Bar) + match syn::parse2::(value.to_token_stream()) { + Ok(path) => Some(path), + Err(_) => { + bail_span!( + value, + "#[{}({} = ...)] must be a valid type path (e.g., PyBaseException)", + self.meta_name(), + key + ) + } + } + } else { + None + }; + Ok(value) + } + pub fn _has_key(&self, key: &str) -> Result { Ok(matches!(self.meta_map.get(key), Some((_, _)))) } @@ -384,8 +413,8 @@ impl ClassItemMeta { self.inner()._optional_str("ctx") } - pub fn base(&self) -> Result> { - self.inner()._optional_str("base") + pub fn base(&self) -> Result> { + self.inner()._optional_path("base") } pub fn unhashable(&self) -> Result { diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 3b95c594a13..6aef58a2aa3 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -34,7 +34,7 @@ pub fn derive_from_args(input: TokenStream) -> TokenStream { /// - `IMMUTABLETYPE`: class attributes are immutable. /// - `with`: which trait implementations are to be included in the python class. /// ```rust, ignore -/// #[pyclass(module = "my_module", name = "MyClass", base = "BaseClass")] +/// #[pyclass(module = "my_module", name = "MyClass", base = BaseClass)] /// struct MyStruct { /// x: i32, /// } diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index 9fd050a7efa..9604999d7da 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -246,7 +246,7 @@ mod _ssl { /// An error occurred in the SSL implementation. #[pyattr] - #[pyexception(name = "SSLError", base = "PyOSError")] + #[pyexception(name = "SSLError", base = PyOSError)] #[derive(Debug)] pub struct PySslError {} @@ -269,7 +269,7 @@ mod _ssl { /// A certificate could not be verified. #[pyattr] - #[pyexception(name = "SSLCertVerificationError", base = "PySslError")] + #[pyexception(name = "SSLCertVerificationError", base = PySslError)] #[derive(Debug)] pub struct PySslCertVerificationError {} @@ -278,7 +278,7 @@ mod _ssl { /// SSL/TLS session closed cleanly. #[pyattr] - #[pyexception(name = "SSLZeroReturnError", base = "PySslError")] + #[pyexception(name = "SSLZeroReturnError", base = PySslError)] #[derive(Debug)] pub struct PySslZeroReturnError {} @@ -287,7 +287,7 @@ mod _ssl { /// Non-blocking SSL socket needs to read more data. #[pyattr] - #[pyexception(name = "SSLWantReadError", base = "PySslError")] + #[pyexception(name = "SSLWantReadError", base = PySslError)] #[derive(Debug)] pub struct PySslWantReadError {} @@ -296,7 +296,7 @@ mod _ssl { /// Non-blocking SSL socket needs to write more data. #[pyattr] - #[pyexception(name = "SSLWantWriteError", base = "PySslError")] + #[pyexception(name = "SSLWantWriteError", base = PySslError)] #[derive(Debug)] pub struct PySslWantWriteError {} @@ -305,7 +305,7 @@ mod _ssl { /// System error when attempting SSL operation. #[pyattr] - #[pyexception(name = "SSLSyscallError", base = "PySslError")] + #[pyexception(name = "SSLSyscallError", base = PySslError)] #[derive(Debug)] pub struct PySslSyscallError {} @@ -314,7 +314,7 @@ mod _ssl { /// SSL/TLS connection terminated abruptly. #[pyattr] - #[pyexception(name = "SSLEOFError", base = "PySslError")] + #[pyexception(name = "SSLEOFError", base = PySslError)] #[derive(Debug)] pub struct PySslEOFError {} diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index a20e821d2d1..47e46541e39 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -77,7 +77,7 @@ impl PyObjectRef { } } -#[pyclass(name = "bool", module = false, base = "PyInt")] +#[pyclass(name = "bool", module = false, base = PyInt)] pub struct PyBool; impl PyPayload for PyBool { diff --git a/vm/src/builtins/builtin_func.rs b/vm/src/builtins/builtin_func.rs index 3df93398dd9..8c160c2eb4b 100644 --- a/vm/src/builtins/builtin_func.rs +++ b/vm/src/builtins/builtin_func.rs @@ -148,7 +148,7 @@ impl Representable for PyNativeFunction { impl Unconstructible for PyNativeFunction {} // `PyCMethodObject` in CPython -#[pyclass(name = "builtin_method", module = false, base = "PyNativeFunction")] +#[pyclass(name = "builtin_method", module = false, base = PyNativeFunction)] pub struct PyNativeMethod { pub(crate) func: PyNativeFunction, pub(crate) class: &'static Py, // TODO: the actual life is &'self diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 029b12f5a47..1a9c95a930e 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1225,11 +1225,11 @@ pub(super) mod types { pub(super) args: PyRwLock, } - #[pyexception(name, base = "PyBaseException", ctx = "system_exit", impl)] + #[pyexception(name, base = PyBaseException, ctx = "system_exit", impl)] #[derive(Debug)] pub struct PySystemExit {} - #[pyexception(name, base = "PyBaseException", ctx = "base_exception_group")] + #[pyexception(name, base = PyBaseException, ctx = "base_exception_group")] #[derive(Debug)] pub struct PyBaseExceptionGroup {} @@ -1245,23 +1245,23 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyBaseExceptionGroup", ctx = "exception_group", impl)] + #[pyexception(name, base = PyBaseExceptionGroup, ctx = "exception_group", impl)] #[derive(Debug)] pub struct PyExceptionGroup {} - #[pyexception(name, base = "PyBaseException", ctx = "generator_exit", impl)] + #[pyexception(name, base = PyBaseException, ctx = "generator_exit", impl)] #[derive(Debug)] pub struct PyGeneratorExit {} - #[pyexception(name, base = "PyBaseException", ctx = "keyboard_interrupt", impl)] + #[pyexception(name, base = PyBaseException, ctx = "keyboard_interrupt", impl)] #[derive(Debug)] pub struct PyKeyboardInterrupt {} - #[pyexception(name, base = "PyBaseException", ctx = "exception_type", impl)] + #[pyexception(name, base = PyBaseException, ctx = "exception_type", impl)] #[derive(Debug)] pub struct PyException {} - #[pyexception(name, base = "PyException", ctx = "stop_iteration")] + #[pyexception(name, base = PyException, ctx = "stop_iteration")] #[derive(Debug)] pub struct PyStopIteration {} @@ -1279,31 +1279,31 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyException", ctx = "stop_async_iteration", impl)] + #[pyexception(name, base = PyException, ctx = "stop_async_iteration", impl)] #[derive(Debug)] pub struct PyStopAsyncIteration {} - #[pyexception(name, base = "PyException", ctx = "arithmetic_error", impl)] + #[pyexception(name, base = PyException, ctx = "arithmetic_error", impl)] #[derive(Debug)] pub struct PyArithmeticError {} - #[pyexception(name, base = "PyArithmeticError", ctx = "floating_point_error", impl)] + #[pyexception(name, base = PyArithmeticError, ctx = "floating_point_error", impl)] #[derive(Debug)] pub struct PyFloatingPointError {} - #[pyexception(name, base = "PyArithmeticError", ctx = "overflow_error", impl)] + #[pyexception(name, base = PyArithmeticError, ctx = "overflow_error", impl)] #[derive(Debug)] pub struct PyOverflowError {} - #[pyexception(name, base = "PyArithmeticError", ctx = "zero_division_error", impl)] + #[pyexception(name, base = PyArithmeticError, ctx = "zero_division_error", impl)] #[derive(Debug)] pub struct PyZeroDivisionError {} - #[pyexception(name, base = "PyException", ctx = "assertion_error", impl)] + #[pyexception(name, base = PyException, ctx = "assertion_error", impl)] #[derive(Debug)] pub struct PyAssertionError {} - #[pyexception(name, base = "PyException", ctx = "attribute_error")] + #[pyexception(name, base = PyException, ctx = "attribute_error")] #[derive(Debug)] pub struct PyAttributeError {} @@ -1330,15 +1330,15 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyException", ctx = "buffer_error", impl)] + #[pyexception(name, base = PyException, ctx = "buffer_error", impl)] #[derive(Debug)] pub struct PyBufferError {} - #[pyexception(name, base = "PyException", ctx = "eof_error", impl)] + #[pyexception(name, base = PyException, ctx = "eof_error", impl)] #[derive(Debug)] pub struct PyEOFError {} - #[pyexception(name, base = "PyException", ctx = "import_error")] + #[pyexception(name, base = PyException, ctx = "import_error")] #[derive(Debug)] pub struct PyImportError {} @@ -1383,19 +1383,19 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyImportError", ctx = "module_not_found_error", impl)] + #[pyexception(name, base = PyImportError, ctx = "module_not_found_error", impl)] #[derive(Debug)] pub struct PyModuleNotFoundError {} - #[pyexception(name, base = "PyException", ctx = "lookup_error", impl)] + #[pyexception(name, base = PyException, ctx = "lookup_error", impl)] #[derive(Debug)] pub struct PyLookupError {} - #[pyexception(name, base = "PyLookupError", ctx = "index_error", impl)] + #[pyexception(name, base = PyLookupError, ctx = "index_error", impl)] #[derive(Debug)] pub struct PyIndexError {} - #[pyexception(name, base = "PyLookupError", ctx = "key_error")] + #[pyexception(name, base = PyLookupError, ctx = "key_error")] #[derive(Debug)] pub struct PyKeyError {} @@ -1415,19 +1415,19 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyException", ctx = "memory_error", impl)] + #[pyexception(name, base = PyException, ctx = "memory_error", impl)] #[derive(Debug)] pub struct PyMemoryError {} - #[pyexception(name, base = "PyException", ctx = "name_error", impl)] + #[pyexception(name, base = PyException, ctx = "name_error", impl)] #[derive(Debug)] pub struct PyNameError {} - #[pyexception(name, base = "PyNameError", ctx = "unbound_local_error", impl)] + #[pyexception(name, base = PyNameError, ctx = "unbound_local_error", impl)] #[derive(Debug)] pub struct PyUnboundLocalError {} - #[pyexception(name, base = "PyException", ctx = "os_error")] + #[pyexception(name, base = PyException, ctx = "os_error")] #[derive(Debug)] pub struct PyOSError {} @@ -1558,25 +1558,25 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyOSError", ctx = "blocking_io_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "blocking_io_error", impl)] #[derive(Debug)] pub struct PyBlockingIOError {} - #[pyexception(name, base = "PyOSError", ctx = "child_process_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "child_process_error", impl)] #[derive(Debug)] pub struct PyChildProcessError {} - #[pyexception(name, base = "PyOSError", ctx = "connection_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "connection_error", impl)] #[derive(Debug)] pub struct PyConnectionError {} - #[pyexception(name, base = "PyConnectionError", ctx = "broken_pipe_error", impl)] + #[pyexception(name, base = PyConnectionError, ctx = "broken_pipe_error", impl)] #[derive(Debug)] pub struct PyBrokenPipeError {} #[pyexception( name, - base = "PyConnectionError", + base = PyConnectionError, ctx = "connection_aborted_error", impl )] @@ -1585,66 +1585,66 @@ pub(super) mod types { #[pyexception( name, - base = "PyConnectionError", + base = PyConnectionError, ctx = "connection_refused_error", impl )] #[derive(Debug)] pub struct PyConnectionRefusedError {} - #[pyexception(name, base = "PyConnectionError", ctx = "connection_reset_error", impl)] + #[pyexception(name, base = PyConnectionError, ctx = "connection_reset_error", impl)] #[derive(Debug)] pub struct PyConnectionResetError {} - #[pyexception(name, base = "PyOSError", ctx = "file_exists_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "file_exists_error", impl)] #[derive(Debug)] pub struct PyFileExistsError {} - #[pyexception(name, base = "PyOSError", ctx = "file_not_found_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "file_not_found_error", impl)] #[derive(Debug)] pub struct PyFileNotFoundError {} - #[pyexception(name, base = "PyOSError", ctx = "interrupted_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "interrupted_error", impl)] #[derive(Debug)] pub struct PyInterruptedError {} - #[pyexception(name, base = "PyOSError", ctx = "is_a_directory_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "is_a_directory_error", impl)] #[derive(Debug)] pub struct PyIsADirectoryError {} - #[pyexception(name, base = "PyOSError", ctx = "not_a_directory_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "not_a_directory_error", impl)] #[derive(Debug)] pub struct PyNotADirectoryError {} - #[pyexception(name, base = "PyOSError", ctx = "permission_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "permission_error", impl)] #[derive(Debug)] pub struct PyPermissionError {} - #[pyexception(name, base = "PyOSError", ctx = "process_lookup_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "process_lookup_error", impl)] #[derive(Debug)] pub struct PyProcessLookupError {} - #[pyexception(name, base = "PyOSError", ctx = "timeout_error", impl)] + #[pyexception(name, base = PyOSError, ctx = "timeout_error", impl)] #[derive(Debug)] pub struct PyTimeoutError {} - #[pyexception(name, base = "PyException", ctx = "reference_error", impl)] + #[pyexception(name, base = PyException, ctx = "reference_error", impl)] #[derive(Debug)] pub struct PyReferenceError {} - #[pyexception(name, base = "PyException", ctx = "runtime_error", impl)] + #[pyexception(name, base = PyException, ctx = "runtime_error", impl)] #[derive(Debug)] pub struct PyRuntimeError {} - #[pyexception(name, base = "PyRuntimeError", ctx = "not_implemented_error", impl)] + #[pyexception(name, base = PyRuntimeError, ctx = "not_implemented_error", impl)] #[derive(Debug)] pub struct PyNotImplementedError {} - #[pyexception(name, base = "PyRuntimeError", ctx = "recursion_error", impl)] + #[pyexception(name, base = PyRuntimeError, ctx = "recursion_error", impl)] #[derive(Debug)] pub struct PyRecursionError {} - #[pyexception(name, base = "PyException", ctx = "syntax_error")] + #[pyexception(name, base = PyException, ctx = "syntax_error")] #[derive(Debug)] pub struct PySyntaxError {} @@ -1736,7 +1736,7 @@ pub(super) mod types { #[pyexception( name = "_IncompleteInputError", - base = "PySyntaxError", + base = PySyntaxError, ctx = "incomplete_input_error" )] #[derive(Debug)] @@ -1756,31 +1756,31 @@ pub(super) mod types { } } - #[pyexception(name, base = "PySyntaxError", ctx = "indentation_error", impl)] + #[pyexception(name, base = PySyntaxError, ctx = "indentation_error", impl)] #[derive(Debug)] pub struct PyIndentationError {} - #[pyexception(name, base = "PyIndentationError", ctx = "tab_error", impl)] + #[pyexception(name, base = PyIndentationError, ctx = "tab_error", impl)] #[derive(Debug)] pub struct PyTabError {} - #[pyexception(name, base = "PyException", ctx = "system_error", impl)] + #[pyexception(name, base = PyException, ctx = "system_error", impl)] #[derive(Debug)] pub struct PySystemError {} - #[pyexception(name, base = "PyException", ctx = "type_error", impl)] + #[pyexception(name, base = PyException, ctx = "type_error", impl)] #[derive(Debug)] pub struct PyTypeError {} - #[pyexception(name, base = "PyException", ctx = "value_error", impl)] + #[pyexception(name, base = PyException, ctx = "value_error", impl)] #[derive(Debug)] pub struct PyValueError {} - #[pyexception(name, base = "PyValueError", ctx = "unicode_error", impl)] + #[pyexception(name, base = PyValueError, ctx = "unicode_error", impl)] #[derive(Debug)] pub struct PyUnicodeError {} - #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_decode_error")] + #[pyexception(name, base = PyUnicodeError, ctx = "unicode_decode_error")] #[derive(Debug)] pub struct PyUnicodeDecodeError {} @@ -1831,7 +1831,7 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_encode_error")] + #[pyexception(name, base = PyUnicodeError, ctx = "unicode_encode_error")] #[derive(Debug)] pub struct PyUnicodeEncodeError {} @@ -1882,7 +1882,7 @@ pub(super) mod types { } } - #[pyexception(name, base = "PyUnicodeError", ctx = "unicode_translate_error")] + #[pyexception(name, base = PyUnicodeError, ctx = "unicode_translate_error")] #[derive(Debug)] pub struct PyUnicodeTranslateError {} @@ -1930,56 +1930,56 @@ pub(super) mod types { /// JIT error. #[cfg(feature = "jit")] - #[pyexception(name, base = "PyException", ctx = "jit_error", impl)] + #[pyexception(name, base = PyException, ctx = "jit_error", impl)] #[derive(Debug)] pub struct PyJitError {} // Warnings - #[pyexception(name, base = "PyException", ctx = "warning", impl)] + #[pyexception(name, base = PyException, ctx = "warning", impl)] #[derive(Debug)] pub struct PyWarning {} - #[pyexception(name, base = "PyWarning", ctx = "deprecation_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "deprecation_warning", impl)] #[derive(Debug)] pub struct PyDeprecationWarning {} - #[pyexception(name, base = "PyWarning", ctx = "pending_deprecation_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "pending_deprecation_warning", impl)] #[derive(Debug)] pub struct PyPendingDeprecationWarning {} - #[pyexception(name, base = "PyWarning", ctx = "runtime_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "runtime_warning", impl)] #[derive(Debug)] pub struct PyRuntimeWarning {} - #[pyexception(name, base = "PyWarning", ctx = "syntax_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "syntax_warning", impl)] #[derive(Debug)] pub struct PySyntaxWarning {} - #[pyexception(name, base = "PyWarning", ctx = "user_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "user_warning", impl)] #[derive(Debug)] pub struct PyUserWarning {} - #[pyexception(name, base = "PyWarning", ctx = "future_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "future_warning", impl)] #[derive(Debug)] pub struct PyFutureWarning {} - #[pyexception(name, base = "PyWarning", ctx = "import_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "import_warning", impl)] #[derive(Debug)] pub struct PyImportWarning {} - #[pyexception(name, base = "PyWarning", ctx = "unicode_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "unicode_warning", impl)] #[derive(Debug)] pub struct PyUnicodeWarning {} - #[pyexception(name, base = "PyWarning", ctx = "bytes_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "bytes_warning", impl)] #[derive(Debug)] pub struct PyBytesWarning {} - #[pyexception(name, base = "PyWarning", ctx = "resource_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "resource_warning", impl)] #[derive(Debug)] pub struct PyResourceWarning {} - #[pyexception(name, base = "PyWarning", ctx = "encoding_warning", impl)] + #[pyexception(name, base = PyWarning, ctx = "encoding_warning", impl)] #[derive(Debug)] pub struct PyEncodingWarning {} } diff --git a/vm/src/stdlib/ast/pyast.rs b/vm/src/stdlib/ast/pyast.rs index 8aae6c72e04..b891a605dc2 100644 --- a/vm/src/stdlib/ast/pyast.rs +++ b/vm/src/stdlib/ast/pyast.rs @@ -77,774 +77,774 @@ macro_rules! impl_node { }; } -#[pyclass(module = "_ast", name = "mod", base = "NodeAst")] +#[pyclass(module = "_ast", name = "mod", base = NodeAst)] pub(crate) struct NodeMod; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeMod {} impl_node!( - #[pyclass(module = "_ast", name = "Module", base = "NodeMod")] + #[pyclass(module = "_ast", name = "Module", base = NodeMod)] pub(crate) struct NodeModModule, fields: ["body", "type_ignores"], ); impl_node!( - #[pyclass(module = "_ast", name = "Interactive", base = "NodeMod")] + #[pyclass(module = "_ast", name = "Interactive", base = NodeMod)] pub(crate) struct NodeModInteractive, fields: ["body"], ); impl_node!( - #[pyclass(module = "_ast", name = "Expression", base = "NodeMod")] + #[pyclass(module = "_ast", name = "Expression", base = NodeMod)] pub(crate) struct NodeModExpression, fields: ["body"], ); -#[pyclass(module = "_ast", name = "stmt", base = "NodeAst")] +#[pyclass(module = "_ast", name = "stmt", base = NodeAst)] pub(crate) struct NodeStmt; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeStmt {} impl_node!( - #[pyclass(module = "_ast", name = "FunctionType", base = "NodeMod")] + #[pyclass(module = "_ast", name = "FunctionType", base = NodeMod)] pub(crate) struct NodeModFunctionType, fields: ["argtypes", "returns"], ); impl_node!( - #[pyclass(module = "_ast", name = "FunctionDef", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "FunctionDef", base = NodeStmt)] pub(crate) struct NodeStmtFunctionDef, fields: ["name", "args", "body", "decorator_list", "returns", "type_comment", "type_params"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "AsyncFunctionDef", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "AsyncFunctionDef", base = NodeStmt)] pub(crate) struct NodeStmtAsyncFunctionDef, fields: ["name", "args", "body", "decorator_list", "returns", "type_comment", "type_params"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "ClassDef", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "ClassDef", base = NodeStmt)] pub(crate) struct NodeStmtClassDef, fields: ["name", "bases", "keywords", "body", "decorator_list", "type_params"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Return", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Return", base = NodeStmt)] pub(crate) struct NodeStmtReturn, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Delete", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Delete", base = NodeStmt)] pub(crate) struct NodeStmtDelete, fields: ["targets"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Assign", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Assign", base = NodeStmt)] pub(crate) struct NodeStmtAssign, fields: ["targets", "value", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "TypeAlias", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "TypeAlias", base = NodeStmt)] pub(crate) struct NodeStmtTypeAlias, fields: ["name", "type_params", "value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "AugAssign", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "AugAssign", base = NodeStmt)] pub(crate) struct NodeStmtAugAssign, fields: ["target", "op", "value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "AnnAssign", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "AnnAssign", base = NodeStmt)] pub(crate) struct NodeStmtAnnAssign, fields: ["target", "annotation", "value", "simple"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "For", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "For", base = NodeStmt)] pub(crate) struct NodeStmtFor, fields: ["target", "iter", "body", "orelse", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "AsyncFor", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "AsyncFor", base = NodeStmt)] pub(crate) struct NodeStmtAsyncFor, fields: ["target", "iter", "body", "orelse", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "While", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "While", base = NodeStmt)] pub(crate) struct NodeStmtWhile, fields: ["test", "body", "orelse"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "If", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "If", base = NodeStmt)] pub(crate) struct NodeStmtIf, fields: ["test", "body", "orelse"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "With", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "With", base = NodeStmt)] pub(crate) struct NodeStmtWith, fields: ["items", "body", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "AsyncWith", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "AsyncWith", base = NodeStmt)] pub(crate) struct NodeStmtAsyncWith, fields: ["items", "body", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Match", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Match", base = NodeStmt)] pub(crate) struct NodeStmtMatch, fields: ["subject", "cases"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Raise", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Raise", base = NodeStmt)] pub(crate) struct NodeStmtRaise, fields: ["exc", "cause"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Try", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Try", base = NodeStmt)] pub(crate) struct NodeStmtTry, fields: ["body", "handlers", "orelse", "finalbody"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "TryStar", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "TryStar", base = NodeStmt)] pub(crate) struct NodeStmtTryStar, fields: ["body", "handlers", "orelse", "finalbody"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Assert", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Assert", base = NodeStmt)] pub(crate) struct NodeStmtAssert, fields: ["test", "msg"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Import", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Import", base = NodeStmt)] pub(crate) struct NodeStmtImport, fields: ["names"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "ImportFrom", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "ImportFrom", base = NodeStmt)] pub(crate) struct NodeStmtImportFrom, fields: ["module", "names", "level"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Global", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Global", base = NodeStmt)] pub(crate) struct NodeStmtGlobal, fields: ["names"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Nonlocal", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Nonlocal", base = NodeStmt)] pub(crate) struct NodeStmtNonlocal, fields: ["names"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Expr", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Expr", base = NodeStmt)] pub(crate) struct NodeStmtExpr, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Pass", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Pass", base = NodeStmt)] pub(crate) struct NodeStmtPass, attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Break", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Break", base = NodeStmt)] pub(crate) struct NodeStmtBreak, attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); -#[pyclass(module = "_ast", name = "expr", base = "NodeAst")] +#[pyclass(module = "_ast", name = "expr", base = NodeAst)] pub(crate) struct NodeExpr; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeExpr {} impl_node!( - #[pyclass(module = "_ast", name = "Continue", base = "NodeStmt")] + #[pyclass(module = "_ast", name = "Continue", base = NodeStmt)] pub(crate) struct NodeStmtContinue, attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "BoolOp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "BoolOp", base = NodeExpr)] pub(crate) struct NodeExprBoolOp, fields: ["op", "values"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "NamedExpr", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "NamedExpr", base = NodeExpr)] pub(crate) struct NodeExprNamedExpr, fields: ["target", "value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "BinOp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "BinOp", base = NodeExpr)] pub(crate) struct NodeExprBinOp, fields: ["left", "op", "right"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "UnaryOp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "UnaryOp", base = NodeExpr)] pub(crate) struct NodeExprUnaryOp, fields: ["op", "operand"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Lambda", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Lambda", base = NodeExpr)] pub(crate) struct NodeExprLambda, fields: ["args", "body"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "IfExp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "IfExp", base = NodeExpr)] pub(crate) struct NodeExprIfExp, fields: ["test", "body", "orelse"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Dict", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Dict", base = NodeExpr)] pub(crate) struct NodeExprDict, fields: ["keys", "values"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Set", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Set", base = NodeExpr)] pub(crate) struct NodeExprSet, fields: ["elts"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "ListComp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "ListComp", base = NodeExpr)] pub(crate) struct NodeExprListComp, fields: ["elt", "generators"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "SetComp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "SetComp", base = NodeExpr)] pub(crate) struct NodeExprSetComp, fields: ["elt", "generators"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "DictComp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "DictComp", base = NodeExpr)] pub(crate) struct NodeExprDictComp, fields: ["key", "value", "generators"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "GeneratorExp", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "GeneratorExp", base = NodeExpr)] pub(crate) struct NodeExprGeneratorExp, fields: ["elt", "generators"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Await", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Await", base = NodeExpr)] pub(crate) struct NodeExprAwait, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Yield", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Yield", base = NodeExpr)] pub(crate) struct NodeExprYield, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "YieldFrom", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "YieldFrom", base = NodeExpr)] pub(crate) struct NodeExprYieldFrom, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Compare", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Compare", base = NodeExpr)] pub(crate) struct NodeExprCompare, fields: ["left", "ops", "comparators"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Call", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Call", base = NodeExpr)] pub(crate) struct NodeExprCall, fields: ["func", "args", "keywords"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "FormattedValue", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "FormattedValue", base = NodeExpr)] pub(crate) struct NodeExprFormattedValue, fields: ["value", "conversion", "format_spec"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "JoinedStr", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "JoinedStr", base = NodeExpr)] pub(crate) struct NodeExprJoinedStr, fields: ["values"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Constant", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Constant", base = NodeExpr)] pub(crate) struct NodeExprConstant, fields: ["value", "kind"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Attribute", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Attribute", base = NodeExpr)] pub(crate) struct NodeExprAttribute, fields: ["value", "attr", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Subscript", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Subscript", base = NodeExpr)] pub(crate) struct NodeExprSubscript, fields: ["value", "slice", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Starred", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Starred", base = NodeExpr)] pub(crate) struct NodeExprStarred, fields: ["value", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Name", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Name", base = NodeExpr)] pub(crate) struct NodeExprName, fields: ["id", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "List", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "List", base = NodeExpr)] pub(crate) struct NodeExprList, fields: ["elts", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Tuple", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Tuple", base = NodeExpr)] pub(crate) struct NodeExprTuple, fields: ["elts", "ctx"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); -#[pyclass(module = "_ast", name = "expr_context", base = "NodeAst")] +#[pyclass(module = "_ast", name = "expr_context", base = NodeAst)] pub(crate) struct NodeExprContext; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeExprContext {} impl_node!( - #[pyclass(module = "_ast", name = "Slice", base = "NodeExpr")] + #[pyclass(module = "_ast", name = "Slice", base = NodeExpr)] pub(crate) struct NodeExprSlice, fields: ["lower", "upper", "step"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "Load", base = "NodeExprContext")] + #[pyclass(module = "_ast", name = "Load", base = NodeExprContext)] pub(crate) struct NodeExprContextLoad, ); impl_node!( - #[pyclass(module = "_ast", name = "Store", base = "NodeExprContext")] + #[pyclass(module = "_ast", name = "Store", base = NodeExprContext)] pub(crate) struct NodeExprContextStore, ); -#[pyclass(module = "_ast", name = "boolop", base = "NodeAst")] +#[pyclass(module = "_ast", name = "boolop", base = NodeAst)] pub(crate) struct NodeBoolOp; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeBoolOp {} impl_node!( - #[pyclass(module = "_ast", name = "Del", base = "NodeExprContext")] + #[pyclass(module = "_ast", name = "Del", base = NodeExprContext)] pub(crate) struct NodeExprContextDel, ); impl_node!( - #[pyclass(module = "_ast", name = "And", base = "NodeBoolOp")] + #[pyclass(module = "_ast", name = "And", base = NodeBoolOp)] pub(crate) struct NodeBoolOpAnd, ); -#[pyclass(module = "_ast", name = "operator", base = "NodeAst")] +#[pyclass(module = "_ast", name = "operator", base = NodeAst)] pub(crate) struct NodeOperator; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeOperator {} impl_node!( - #[pyclass(module = "_ast", name = "Or", base = "NodeBoolOp")] + #[pyclass(module = "_ast", name = "Or", base = NodeBoolOp)] pub(crate) struct NodeBoolOpOr, ); impl_node!( - #[pyclass(module = "_ast", name = "Add", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Add", base = NodeOperator)] pub(crate) struct NodeOperatorAdd, ); impl_node!( - #[pyclass(module = "_ast", name = "Sub", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Sub", base = NodeOperator)] pub(crate) struct NodeOperatorSub, ); impl_node!( - #[pyclass(module = "_ast", name = "Mult", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Mult", base = NodeOperator)] pub(crate) struct NodeOperatorMult, ); impl_node!( - #[pyclass(module = "_ast", name = "MatMult", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "MatMult", base = NodeOperator)] pub(crate) struct NodeOperatorMatMult, ); impl_node!( - #[pyclass(module = "_ast", name = "Div", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Div", base = NodeOperator)] pub(crate) struct NodeOperatorDiv, ); impl_node!( - #[pyclass(module = "_ast", name = "Mod", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Mod", base = NodeOperator)] pub(crate) struct NodeOperatorMod, ); impl_node!( - #[pyclass(module = "_ast", name = "Pow", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "Pow", base = NodeOperator)] pub(crate) struct NodeOperatorPow, ); impl_node!( - #[pyclass(module = "_ast", name = "LShift", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "LShift", base = NodeOperator)] pub(crate) struct NodeOperatorLShift, ); impl_node!( - #[pyclass(module = "_ast", name = "RShift", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "RShift", base = NodeOperator)] pub(crate) struct NodeOperatorRShift, ); impl_node!( - #[pyclass(module = "_ast", name = "BitOr", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "BitOr", base = NodeOperator)] pub(crate) struct NodeOperatorBitOr, ); impl_node!( - #[pyclass(module = "_ast", name = "BitXor", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "BitXor", base = NodeOperator)] pub(crate) struct NodeOperatorBitXor, ); impl_node!( - #[pyclass(module = "_ast", name = "BitAnd", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "BitAnd", base = NodeOperator)] pub(crate) struct NodeOperatorBitAnd, ); -#[pyclass(module = "_ast", name = "unaryop", base = "NodeAst")] +#[pyclass(module = "_ast", name = "unaryop", base = NodeAst)] pub(crate) struct NodeUnaryOp; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeUnaryOp {} impl_node!( - #[pyclass(module = "_ast", name = "FloorDiv", base = "NodeOperator")] + #[pyclass(module = "_ast", name = "FloorDiv", base = NodeOperator)] pub(crate) struct NodeOperatorFloorDiv, ); impl_node!( - #[pyclass(module = "_ast", name = "Invert", base = "NodeUnaryOp")] + #[pyclass(module = "_ast", name = "Invert", base = NodeUnaryOp)] pub(crate) struct NodeUnaryOpInvert, ); impl_node!( - #[pyclass(module = "_ast", name = "Not", base = "NodeUnaryOp")] + #[pyclass(module = "_ast", name = "Not", base = NodeUnaryOp)] pub(crate) struct NodeUnaryOpNot, ); impl_node!( - #[pyclass(module = "_ast", name = "UAdd", base = "NodeUnaryOp")] + #[pyclass(module = "_ast", name = "UAdd", base = NodeUnaryOp)] pub(crate) struct NodeUnaryOpUAdd, ); -#[pyclass(module = "_ast", name = "cmpop", base = "NodeAst")] +#[pyclass(module = "_ast", name = "cmpop", base = NodeAst)] pub(crate) struct NodeCmpOp; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeCmpOp {} impl_node!( - #[pyclass(module = "_ast", name = "USub", base = "NodeUnaryOp")] + #[pyclass(module = "_ast", name = "USub", base = NodeUnaryOp)] pub(crate) struct NodeUnaryOpUSub, ); impl_node!( - #[pyclass(module = "_ast", name = "Eq", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "Eq", base = NodeCmpOp)] pub(crate) struct NodeCmpOpEq, ); impl_node!( - #[pyclass(module = "_ast", name = "NotEq", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "NotEq", base = NodeCmpOp)] pub(crate) struct NodeCmpOpNotEq, ); impl_node!( - #[pyclass(module = "_ast", name = "Lt", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "Lt", base = NodeCmpOp)] pub(crate) struct NodeCmpOpLt, ); impl_node!( - #[pyclass(module = "_ast", name = "LtE", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "LtE", base = NodeCmpOp)] pub(crate) struct NodeCmpOpLtE, ); impl_node!( - #[pyclass(module = "_ast", name = "Gt", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "Gt", base = NodeCmpOp)] pub(crate) struct NodeCmpOpGt, ); impl_node!( - #[pyclass(module = "_ast", name = "GtE", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "GtE", base = NodeCmpOp)] pub(crate) struct NodeCmpOpGtE, ); impl_node!( - #[pyclass(module = "_ast", name = "Is", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "Is", base = NodeCmpOp)] pub(crate) struct NodeCmpOpIs, ); impl_node!( - #[pyclass(module = "_ast", name = "IsNot", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "IsNot", base = NodeCmpOp)] pub(crate) struct NodeCmpOpIsNot, ); impl_node!( - #[pyclass(module = "_ast", name = "In", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "In", base = NodeCmpOp)] pub(crate) struct NodeCmpOpIn, ); impl_node!( - #[pyclass(module = "_ast", name = "NotIn", base = "NodeCmpOp")] + #[pyclass(module = "_ast", name = "NotIn", base = NodeCmpOp)] pub(crate) struct NodeCmpOpNotIn, ); -#[pyclass(module = "_ast", name = "excepthandler", base = "NodeAst")] +#[pyclass(module = "_ast", name = "excepthandler", base = NodeAst)] pub(crate) struct NodeExceptHandler; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeExceptHandler {} impl_node!( - #[pyclass(module = "_ast", name = "comprehension", base = "NodeAst")] + #[pyclass(module = "_ast", name = "comprehension", base = NodeAst)] pub(crate) struct NodeComprehension, fields: ["target", "iter", "ifs", "is_async"], ); impl_node!( - #[pyclass(module = "_ast", name = "ExceptHandler", base = "NodeExceptHandler")] + #[pyclass(module = "_ast", name = "ExceptHandler", base = NodeExceptHandler)] pub(crate) struct NodeExceptHandlerExceptHandler, fields: ["type", "name", "body"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "arguments", base = "NodeAst")] + #[pyclass(module = "_ast", name = "arguments", base = NodeAst)] pub(crate) struct NodeArguments, fields: ["posonlyargs", "args", "vararg", "kwonlyargs", "kw_defaults", "kwarg", "defaults"], ); impl_node!( - #[pyclass(module = "_ast", name = "arg", base = "NodeAst")] + #[pyclass(module = "_ast", name = "arg", base = NodeAst)] pub(crate) struct NodeArg, fields: ["arg", "annotation", "type_comment"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "keyword", base = "NodeAst")] + #[pyclass(module = "_ast", name = "keyword", base = NodeAst)] pub(crate) struct NodeKeyword, fields: ["arg", "value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "alias", base = "NodeAst")] + #[pyclass(module = "_ast", name = "alias", base = NodeAst)] pub(crate) struct NodeAlias, fields: ["name", "asname"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "withitem", base = "NodeAst")] + #[pyclass(module = "_ast", name = "withitem", base = NodeAst)] pub(crate) struct NodeWithItem, fields: ["context_expr", "optional_vars"], ); -#[pyclass(module = "_ast", name = "pattern", base = "NodeAst")] +#[pyclass(module = "_ast", name = "pattern", base = NodeAst)] pub(crate) struct NodePattern; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodePattern {} impl_node!( - #[pyclass(module = "_ast", name = "match_case", base = "NodeAst")] + #[pyclass(module = "_ast", name = "match_case", base = NodeAst)] pub(crate) struct NodeMatchCase, fields: ["pattern", "guard", "body"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchValue", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchValue", base = NodePattern)] pub(crate) struct NodePatternMatchValue, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchSingleton", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchSingleton", base = NodePattern)] pub(crate) struct NodePatternMatchSingleton, fields: ["value"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchSequence", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchSequence", base = NodePattern)] pub(crate) struct NodePatternMatchSequence, fields: ["patterns"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchMapping", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchMapping", base = NodePattern)] pub(crate) struct NodePatternMatchMapping, fields: ["keys", "patterns", "rest"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchClass", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchClass", base = NodePattern)] pub(crate) struct NodePatternMatchClass, fields: ["cls", "patterns", "kwd_attrs", "kwd_patterns"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchStar", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchStar", base = NodePattern)] pub(crate) struct NodePatternMatchStar, fields: ["name"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "MatchAs", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchAs", base = NodePattern)] pub(crate) struct NodePatternMatchAs, fields: ["pattern", "name"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); -#[pyclass(module = "_ast", name = "type_ignore", base = "NodeAst")] +#[pyclass(module = "_ast", name = "type_ignore", base = NodeAst)] pub(crate) struct NodeTypeIgnore; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeTypeIgnore {} impl_node!( - #[pyclass(module = "_ast", name = "MatchOr", base = "NodePattern")] + #[pyclass(module = "_ast", name = "MatchOr", base = NodePattern)] pub(crate) struct NodePatternMatchOr, fields: ["patterns"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); -#[pyclass(module = "_ast", name = "type_param", base = "NodeAst")] +#[pyclass(module = "_ast", name = "type_param", base = NodeAst)] pub(crate) struct NodeTypeParam; #[pyclass(flags(HAS_DICT, BASETYPE))] impl NodeTypeParam {} impl_node!( - #[pyclass(module = "_ast", name = "TypeIgnore", base = "NodeTypeIgnore")] + #[pyclass(module = "_ast", name = "TypeIgnore", base = NodeTypeIgnore)] pub(crate) struct NodeTypeIgnoreTypeIgnore, fields: ["lineno", "tag"], ); impl_node!( - #[pyclass(module = "_ast", name = "TypeVar", base = "NodeTypeParam")] + #[pyclass(module = "_ast", name = "TypeVar", base = NodeTypeParam)] pub(crate) struct NodeTypeParamTypeVar, fields: ["name", "bound"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "ParamSpec", base = "NodeTypeParam")] + #[pyclass(module = "_ast", name = "ParamSpec", base = NodeTypeParam)] pub(crate) struct NodeTypeParamParamSpec, fields: ["name"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], ); impl_node!( - #[pyclass(module = "_ast", name = "TypeVarTuple", base = "NodeTypeParam")] + #[pyclass(module = "_ast", name = "TypeVarTuple", base = NodeTypeParam)] pub(crate) struct NodeTypeParamTypeVarTuple, fields: ["name"], attributes: ["lineno", "col_offset", "end_lineno", "end_col_offset"], diff --git a/vm/src/stdlib/ctypes/array.rs b/vm/src/stdlib/ctypes/array.rs index 82306c8b0b1..5290ec42f37 100644 --- a/vm/src/stdlib/ctypes/array.rs +++ b/vm/src/stdlib/ctypes/array.rs @@ -10,7 +10,7 @@ use crossbeam_utils::atomic::AtomicCell; use rustpython_common::lock::PyRwLock; use rustpython_vm::stdlib::ctypes::base::PyCData; -#[pyclass(name = "PyCArrayType", base = "PyType", module = "_ctypes")] +#[pyclass(name = "PyCArrayType", base = PyType, module = "_ctypes")] #[derive(PyPayload)] pub struct PyCArrayType { pub(super) inner: PyCArray, @@ -49,7 +49,7 @@ impl PyCArrayType {} #[pyclass( name = "Array", - base = "PyCData", + base = PyCData, metaclass = "PyCArrayType", module = "_ctypes" )] diff --git a/vm/src/stdlib/ctypes/base.rs b/vm/src/stdlib/ctypes/base.rs index 2fcac469b95..23cb505adaf 100644 --- a/vm/src/stdlib/ctypes/base.rs +++ b/vm/src/stdlib/ctypes/base.rs @@ -157,7 +157,7 @@ pub struct PyCData { #[pyclass] impl PyCData {} -#[pyclass(module = "_ctypes", name = "PyCSimpleType", base = "PyType")] +#[pyclass(module = "_ctypes", name = "PyCSimpleType", base = PyType)] pub struct PyCSimpleType {} #[pyclass(flags(BASETYPE))] @@ -176,7 +176,7 @@ impl PyCSimpleType { #[pyclass( module = "_ctypes", name = "_SimpleCData", - base = "PyCData", + base = PyCData, metaclass = "PyCSimpleType" )] #[derive(PyPayload)] diff --git a/vm/src/stdlib/ctypes/function.rs b/vm/src/stdlib/ctypes/function.rs index 034b1bd0723..6703dcc0f52 100644 --- a/vm/src/stdlib/ctypes/function.rs +++ b/vm/src/stdlib/ctypes/function.rs @@ -122,7 +122,7 @@ impl Function { } } -#[pyclass(module = "_ctypes", name = "CFuncPtr", base = "PyCData")] +#[pyclass(module = "_ctypes", name = "CFuncPtr", base = PyCData)] #[derive(PyPayload)] pub struct PyCFuncPtr { pub name: PyRwLock, diff --git a/vm/src/stdlib/ctypes/structure.rs b/vm/src/stdlib/ctypes/structure.rs index 8ca8bb51dfc..aef95fe5619 100644 --- a/vm/src/stdlib/ctypes/structure.rs +++ b/vm/src/stdlib/ctypes/structure.rs @@ -8,7 +8,7 @@ use rustpython_vm::types::Constructor; use std::collections::HashMap; use std::fmt::Debug; -#[pyclass(module = "_ctypes", name = "Structure", base = "PyCData")] +#[pyclass(module = "_ctypes", name = "Structure", base = PyCData)] #[derive(PyPayload, Debug)] pub struct PyCStructure { #[allow(dead_code)] diff --git a/vm/src/stdlib/ctypes/union.rs b/vm/src/stdlib/ctypes/union.rs index 2d76dbc9ca4..93a53b2b6db 100644 --- a/vm/src/stdlib/ctypes/union.rs +++ b/vm/src/stdlib/ctypes/union.rs @@ -1,7 +1,7 @@ use super::base::PyCData; // TODO: metaclass = "UnionType" -#[pyclass(module = "_ctypes", name = "Union", base = "PyCData")] +#[pyclass(module = "_ctypes", name = "Union", base = PyCData)] pub struct PyCUnion {} #[pyclass(flags(BASETYPE, IMMUTABLETYPE))] diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index e06560780ce..d1ffe5a9723 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -607,7 +607,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "_RawIOBase", base = "_IOBase")] + #[pyclass(name = "_RawIOBase", base = _IOBase)] pub(super) struct _RawIOBase; #[pyclass(flags(BASETYPE, HAS_DICT))] @@ -665,7 +665,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "_BufferedIOBase", base = "_IOBase")] + #[pyclass(name = "_BufferedIOBase", base = _IOBase)] struct _BufferedIOBase; #[pyclass(flags(BASETYPE))] @@ -728,7 +728,7 @@ mod _io { // TextIO Base has no public constructor #[pyattr] - #[pyclass(name = "_TextIOBase", base = "_IOBase")] + #[pyclass(name = "_TextIOBase", base = _IOBase)] #[derive(Debug, PyPayload)] struct _TextIOBase; @@ -1728,7 +1728,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "BufferedReader", base = "_BufferedIOBase")] + #[pyclass(name = "BufferedReader", base = _BufferedIOBase)] #[derive(Debug, Default, PyPayload)] struct BufferedReader { data: PyThreadMutex, @@ -1785,7 +1785,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "BufferedWriter", base = "_BufferedIOBase")] + #[pyclass(name = "BufferedWriter", base = _BufferedIOBase)] #[derive(Debug, Default, PyPayload)] struct BufferedWriter { data: PyThreadMutex, @@ -1818,7 +1818,7 @@ mod _io { impl DefaultConstructor for BufferedWriter {} #[pyattr] - #[pyclass(name = "BufferedRandom", base = "_BufferedIOBase")] + #[pyclass(name = "BufferedRandom", base = _BufferedIOBase)] #[derive(Debug, Default, PyPayload)] struct BufferedRandom { data: PyThreadMutex, @@ -1860,7 +1860,7 @@ mod _io { impl DefaultConstructor for BufferedRandom {} #[pyattr] - #[pyclass(name = "BufferedRWPair", base = "_BufferedIOBase")] + #[pyclass(name = "BufferedRWPair", base = _BufferedIOBase)] #[derive(Debug, Default, PyPayload)] struct BufferedRWPair { read: BufferedReader, @@ -2274,7 +2274,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "TextIOWrapper", base = "_TextIOBase")] + #[pyclass(name = "TextIOWrapper", base = _TextIOBase)] #[derive(Debug, Default, PyPayload)] struct TextIOWrapper { data: PyThreadMutex>, @@ -3460,7 +3460,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "StringIO", base = "_TextIOBase")] + #[pyclass(name = "StringIO", base = _TextIOBase)] #[derive(Debug, PyPayload)] struct StringIO { buffer: PyRwLock, @@ -3605,7 +3605,7 @@ mod _io { } #[pyattr] - #[pyclass(name = "BytesIO", base = "_BufferedIOBase")] + #[pyclass(name = "BytesIO", base = _BufferedIOBase)] #[derive(Debug, PyPayload)] struct BytesIO { buffer: PyRwLock, @@ -4241,7 +4241,7 @@ mod fileio { } #[pyattr] - #[pyclass(module = "io", name, base = "_RawIOBase")] + #[pyclass(module = "io", name, base = _RawIOBase)] #[derive(Debug, PyPayload)] pub(super) struct FileIO { fd: AtomicCell, From 9792001703ae653114693a662012bab2ea8a35ae Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Mon, 10 Nov 2025 02:48:25 +0200 Subject: [PATCH 0007/1459] Add newtype of `CodeUnits` (#6241) --- compiler/codegen/src/ir.rs | 6 +-- compiler/core/src/bytecode.rs | 73 ++++++++++++++++++++++++++++++++--- compiler/core/src/marshal.rs | 17 +------- vm/src/builtins/code.rs | 29 +++----------- 4 files changed, 77 insertions(+), 48 deletions(-) diff --git a/compiler/codegen/src/ir.rs b/compiler/codegen/src/ir.rs index 7cd173c821d..f08b37e8887 100644 --- a/compiler/codegen/src/ir.rs +++ b/compiler/codegen/src/ir.rs @@ -4,8 +4,8 @@ use crate::{IndexMap, IndexSet, error::InternalError}; use rustpython_compiler_core::{ OneIndexed, SourceLocation, bytecode::{ - CodeFlags, CodeObject, CodeUnit, ConstantData, InstrDisplayContext, Instruction, Label, - OpArg, PyCodeLocationInfoKind, + CodeFlags, CodeObject, CodeUnit, CodeUnits, ConstantData, InstrDisplayContext, Instruction, + Label, OpArg, PyCodeLocationInfoKind, }, }; @@ -214,7 +214,7 @@ impl CodeInfo { qualname: qualname.unwrap_or(obj_name), max_stackdepth, - instructions: instructions.into_boxed_slice(), + instructions: CodeUnits::from(instructions), locations: locations.into_boxed_slice(), constants: constants.into_iter().collect(), names: name_cache.into_iter().collect(), diff --git a/compiler/core/src/bytecode.rs b/compiler/core/src/bytecode.rs index 4ab666da99e..2cfb70e2782 100644 --- a/compiler/core/src/bytecode.rs +++ b/compiler/core/src/bytecode.rs @@ -1,13 +1,16 @@ //! Implement python as a virtual machine with bytecode. This module //! implements bytecode structure. -use crate::{OneIndexed, SourceLocation}; +use crate::{ + marshal::MarshalError, + {OneIndexed, SourceLocation}, +}; use bitflags::bitflags; use itertools::Itertools; use malachite_bigint::BigInt; use num_complex::Complex64; use rustpython_wtf8::{Wtf8, Wtf8Buf}; -use std::{collections::BTreeSet, fmt, hash, marker::PhantomData, mem}; +use std::{collections::BTreeSet, fmt, hash, marker::PhantomData, mem, ops::Deref}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[repr(i8)] @@ -195,7 +198,7 @@ impl ConstantBag for BasicBag { /// a code object. Also a module has a code object. #[derive(Clone)] pub struct CodeObject { - pub instructions: Box<[CodeUnit]>, + pub instructions: CodeUnits, pub locations: Box<[SourceLocation]>, pub flags: CodeFlags, /// Number of positional-only arguments @@ -257,6 +260,12 @@ impl OpArgByte { } } +impl From for OpArgByte { + fn from(raw: u8) -> Self { + Self(raw) + } +} + impl fmt::Debug for OpArgByte { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) @@ -808,14 +817,14 @@ impl From for u8 { } impl TryFrom for Instruction { - type Error = crate::marshal::MarshalError; + type Error = MarshalError; #[inline] - fn try_from(value: u8) -> Result { + fn try_from(value: u8) -> Result { if value <= u8::from(LAST_INSTRUCTION) { Ok(unsafe { std::mem::transmute::(value) }) } else { - Err(crate::marshal::MarshalError::InvalidBytecode) + Err(MarshalError::InvalidBytecode) } } } @@ -835,6 +844,58 @@ impl CodeUnit { } } +impl TryFrom<&[u8]> for CodeUnit { + type Error = MarshalError; + + fn try_from(value: &[u8]) -> Result { + match value.len() { + 2 => Ok(Self::new(value[0].try_into()?, value[1].into())), + _ => Err(Self::Error::InvalidBytecode), + } + } +} + +#[derive(Clone)] +pub struct CodeUnits(Box<[CodeUnit]>); + +impl TryFrom<&[u8]> for CodeUnits { + type Error = MarshalError; + + fn try_from(value: &[u8]) -> Result { + if !value.len().is_multiple_of(2) { + return Err(Self::Error::InvalidBytecode); + } + + value.chunks_exact(2).map(CodeUnit::try_from).collect() + } +} + +impl From<[CodeUnit; N]> for CodeUnits { + fn from(value: [CodeUnit; N]) -> Self { + Self(Box::from(value)) + } +} + +impl From> for CodeUnits { + fn from(value: Vec) -> Self { + Self(value.into_boxed_slice()) + } +} + +impl FromIterator for CodeUnits { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl Deref for CodeUnits { + type Target = [CodeUnit]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + use self::Instruction::*; bitflags! { diff --git a/compiler/core/src/marshal.rs b/compiler/core/src/marshal.rs index f803317772d..39e48071678 100644 --- a/compiler/core/src/marshal.rs +++ b/compiler/core/src/marshal.rs @@ -165,19 +165,6 @@ impl<'a> ReadBorrowed<'a> for &'a [u8] { } } -/// Parses bytecode bytes into CodeUnit instructions. -/// Each instruction is 2 bytes: opcode and argument. -pub fn parse_instructions_from_bytes(bytes: &[u8]) -> Result> { - bytes - .chunks_exact(2) - .map(|cu| { - let op = Instruction::try_from(cu[0])?; - let arg = OpArgByte(cu[1]); - Ok(CodeUnit { op, arg }) - }) - .collect() -} - pub struct Cursor { pub data: B, pub position: usize, @@ -197,8 +184,8 @@ pub fn deserialize_code( bag: Bag, ) -> Result> { let len = rdr.read_u32()?; - let instructions = rdr.read_slice(len * 2)?; - let instructions = parse_instructions_from_bytes(instructions)?; + let raw_instructions = rdr.read_slice(len * 2)?; + let instructions = CodeUnits::try_from(raw_instructions)?; let len = rdr.read_u32()?; let locations = (0..len) diff --git a/vm/src/builtins/code.rs b/vm/src/builtins/code.rs index 2a22993a9e7..f9f3429e303 100644 --- a/vm/src/builtins/code.rs +++ b/vm/src/builtins/code.rs @@ -1,12 +1,10 @@ -/*! Infamous code object. The python class `code` - -*/ +//! Infamous code object. The python class `code` use super::{PyBytesRef, PyStrRef, PyTupleRef, PyType, PyTypeRef}; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, builtins::PyStrInterned, - bytecode::{self, AsBag, BorrowedConstant, CodeFlags, CodeUnit, Constant, ConstantBag}, + bytecode::{self, AsBag, BorrowedConstant, CodeFlags, Constant, ConstantBag}, class::{PyClassImpl, StaticType}, convert::ToPyObject, frozen, @@ -15,11 +13,7 @@ use crate::{ }; use malachite_bigint::BigInt; use num_traits::Zero; -use rustpython_compiler_core::{ - OneIndexed, - bytecode::PyCodeLocationInfoKind, - marshal::{MarshalError, parse_instructions_from_bytes}, -}; +use rustpython_compiler_core::{OneIndexed, bytecode::CodeUnits, bytecode::PyCodeLocationInfoKind}; use std::{borrow::Borrow, fmt, ops::Deref}; /// State for iterating through code address ranges @@ -457,7 +451,7 @@ impl Constructor for PyCode { // Parse and validate bytecode from bytes let bytecode_bytes = args.co_code.as_bytes(); - let instructions = parse_bytecode(bytecode_bytes) + let instructions = CodeUnits::try_from(bytecode_bytes) .map_err(|e| vm.new_value_error(format!("invalid bytecode: {}", e)))?; // Convert constants @@ -925,7 +919,7 @@ impl PyCode { let instructions = match co_code { OptionalArg::Present(code_bytes) => { // Parse and validate bytecode from bytes - parse_bytecode(code_bytes.as_bytes()) + CodeUnits::try_from(code_bytes.as_bytes()) .map_err(|e| vm.new_value_error(format!("invalid bytecode: {}", e)))? } OptionalArg::Missing => self.code.instructions.clone(), @@ -1033,19 +1027,6 @@ impl ToPyObject for bytecode::CodeObject { } } -/// Validates and parses bytecode bytes into CodeUnit instructions. -/// Returns MarshalError if bytecode is invalid (odd length or contains invalid opcodes). -/// Note: Returning MarshalError is not necessary at this point because this is not a part of marshalling API. -/// However, we (temporarily) reuse MarshalError for simplicity. -fn parse_bytecode(bytecode_bytes: &[u8]) -> Result, MarshalError> { - // Bytecode must have even length (each instruction is 2 bytes) - if !bytecode_bytes.len().is_multiple_of(2) { - return Err(MarshalError::InvalidBytecode); - } - - parse_instructions_from_bytes(bytecode_bytes) -} - // Helper struct for reading linetable struct LineTableReader<'a> { data: &'a [u8], From c9ba9560b5820ce2cf750b809810b00ac7f75f46 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:58:08 +0900 Subject: [PATCH 0008/1459] new_buffer_error (#6243) --- Lib/test/test_binascii.py | 1 - Lib/test/test_ssl.py | 1 - vm/src/function/buffer.rs | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 02ca06a3c07..cf11ffce7f1 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -490,7 +490,6 @@ def test_base64_roundtrip(self, binary, newline): restored = binascii.a2b_base64(self.type2test(converted)) self.assertConversion(binary, converted, restored, newline=newline) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_c_contiguity(self): m = memoryview(bytearray(b'noncontig')) noncontig_writable = m[::-2] diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 55e6bb59143..fea3a2ce692 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1811,7 +1811,6 @@ def test_pending(self): bio.read() self.assertEqual(bio.pending, 0) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_buffer_types(self): bio = ssl.MemoryBIO() bio.write(b'foo') diff --git a/vm/src/function/buffer.rs b/vm/src/function/buffer.rs index 8a2a471ac94..e8f835e1dac 100644 --- a/vm/src/function/buffer.rs +++ b/vm/src/function/buffer.rs @@ -22,7 +22,7 @@ impl PyObject { buffer .as_contiguous() .map(|x| f(&x)) - .ok_or_else(|| vm.new_type_error("non-contiguous buffer is not a bytes-like object")) + .ok_or_else(|| vm.new_buffer_error("non-contiguous buffer is not a bytes-like object")) } pub fn try_rw_bytes_like( @@ -81,7 +81,7 @@ impl<'a> TryFromBorrowedObject<'a> for ArgBytesLike { if buffer.desc.is_contiguous() { Ok(Self(buffer)) } else { - Err(vm.new_type_error("non-contiguous buffer is not a bytes-like object")) + Err(vm.new_buffer_error("non-contiguous buffer is not a bytes-like object")) } } } @@ -121,7 +121,7 @@ impl<'a> TryFromBorrowedObject<'a> for ArgMemoryBuffer { fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult { let buffer = PyBuffer::try_from_borrowed_object(vm, obj)?; if !buffer.desc.is_contiguous() { - Err(vm.new_type_error("non-contiguous buffer is not a bytes-like object")) + Err(vm.new_buffer_error("non-contiguous buffer is not a bytes-like object")) } else if buffer.desc.readonly { Err(vm.new_type_error("buffer is not a read-write bytes-like object")) } else { From 2d4617236e4b7fc3ba88beac71d48a1263fa4e43 Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Mon, 10 Nov 2025 20:16:14 +0530 Subject: [PATCH 0009/1459] Update CI auto-formate (#6237) --------- Signed-off-by: Yash Suthar Co-authored-by: fanninpm --- .github/workflows/ci.yaml | 58 ++++++++++++++-- .github/workflows/pr-auto-commit.yaml | 99 +++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/pr-auto-commit.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 977f27f3762..c61d46c4dac 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -307,15 +307,13 @@ jobs: run: python -I whats_left.py lint: - name: Check Rust code with rustfmt and clippy + name: Check Rust code with clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy - - name: run rustfmt - run: cargo fmt --check + components: clippy - name: run clippy on wasm run: cargo clippy --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings - uses: actions/setup-python@v6 @@ -450,3 +448,55 @@ jobs: 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 + + auto_format_commit: + needs: [rust_tests, exotic_targets, snippets_cpython, lint, miri, wasm, wasm-wasi] + permissions: + contents: write + pull-requests: write + name: Auto-format code + runs-on: ubuntu-latest + if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} + concurrency: + group: fmt-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: ${{ github.head_ref || github.ref_name }} + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Run cargo fmt + run: | + echo "Running cargo fmt --all" + cargo fmt --all + + - name: Commit and push if changes + id: commit + 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 -u + git commit -m "Auto-format code [skip ci]" + git push + echo "formatted=true" >> $GITHUB_OUTPUT + else + echo "formatted=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR if formatting was applied + if: steps.commit.outputs.formatted == 'true' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + message: | + Code has been automatically formatted. + No action needed. + the changes were committed with `[skip ci]`. diff --git a/.github/workflows/pr-auto-commit.yaml b/.github/workflows/pr-auto-commit.yaml new file mode 100644 index 00000000000..8546c2abe51 --- /dev/null +++ b/.github/workflows/pr-auto-commit.yaml @@ -0,0 +1,99 @@ +name: PR Auto-format + +# This workflow triggers when a PR is opened/updated +on: + pull_request_target: + types: [opened, synchronize, reopened] + branches: + - main + - release + +concurrency: + group: pr-fmt-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + auto_format: + if: | + !contains(github.event.pull_request.labels.*.name, 'skip:ci') && + !contains(github.event.pull_request.head.sha, '[skip ci]') + permissions: + contents: write + pull-requests: write + checks: read + runs-on: ubuntu-latest + timeout-minutes: 60 + + steps: + - name: Checkout PR branch + uses: actions/checkout@v5 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + # Wait for all PR check runs to complete + - name: Wait for all checks to complete + uses: poseidon/wait-for-status-checks@v0.6.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + delay: 60 + interval: 30 + timeout: 7200 + + - name: CI completed successfully + run: echo "CI workflow completed successfully - proceeding with auto-format" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + + - name: Run cargo fmt + run: | + echo "Running cargo fmt --all on PR #${{ github.event.pull_request.number }}" + cargo fmt --all + + - name: Check for formatting changes + id: check_changes + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "has_changes=true" >> $GITHUB_OUTPUT + else + echo "has_changes=false" >> $GITHUB_OUTPUT + fi + + - name: Commit and push formatting changes + if: steps.check_changes.outputs.has_changes == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add -u + git commit -m "Auto-format code [skip ci]" + + git push origin HEAD:${{ github.event.pull_request.head.ref }} + + - name: Comment on PR + if: steps.check_changes.outputs.has_changes == 'true' + uses: marocchino/sticky-pull-request-comment@v2 + with: + number: ${{ github.event.pull_request.number }} + message: | + **Code has been automatically formatted** + + The code in this PR has been formatted using `cargo fmt`. + The changes have been committed with `[skip ci]` to avoid triggering another CI run. + + **Triggered by commit:** `${{ github.event.pull_request.head.sha }}` + **Last formatted:** ${{ github.event.pull_request.updated_at }} + + You may need to pull the latest changes before pushing again: + ```bash + git pull origin ${{ github.event.pull_request.head.ref }} + ``` + + - name: No formatting needed + if: steps.check_changes.outputs.has_changes == 'false' + run: echo "Code is already properly formatted" From 0f8c0bc8a8130807983e574bffd6886d44818628 Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Tue, 11 Nov 2025 04:05:08 +0530 Subject: [PATCH 0010/1459] remove old auto-formte implimentaion (#6251) Signed-off-by: Yash Suthar --- .github/workflows/ci.yaml | 52 --------------------------------------- 1 file changed, 52 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c61d46c4dac..2ce4b475773 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -448,55 +448,3 @@ jobs: 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 - - auto_format_commit: - needs: [rust_tests, exotic_targets, snippets_cpython, lint, miri, wasm, wasm-wasi] - permissions: - contents: write - pull-requests: write - name: Auto-format code - runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} - concurrency: - group: fmt-${{ github.ref }} - cancel-in-progress: true - - steps: - - name: Checkout code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - ref: ${{ github.head_ref || github.ref_name }} - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - - name: Run cargo fmt - run: | - echo "Running cargo fmt --all" - cargo fmt --all - - - name: Commit and push if changes - id: commit - 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 -u - git commit -m "Auto-format code [skip ci]" - git push - echo "formatted=true" >> $GITHUB_OUTPUT - else - echo "formatted=false" >> $GITHUB_OUTPUT - fi - - - name: Comment on PR if formatting was applied - if: steps.commit.outputs.formatted == 'true' && github.event_name == 'pull_request' - uses: marocchino/sticky-pull-request-comment@v2 - with: - message: | - Code has been automatically formatted. - No action needed. - the changes were committed with `[skip ci]`. From 456293023301e4d024cf20ed65275ca4766048a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 08:24:16 +0900 Subject: [PATCH 0011/1459] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2ce4b475773..02afeff5713 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -336,7 +336,7 @@ 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' incremental_files_only: true From 9ce85862ce13b42461cf32e720d2d2f4caf0c65b Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Fri, 14 Nov 2025 10:31:47 +0900 Subject: [PATCH 0012/1459] Add BaseException.add_note() and __notes__ attribute support (#6252) --- Lib/test/test_exceptions.py | 1 - vm/src/exceptions.rs | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 6148bd3a6fc..61f4156dc6d 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -601,7 +601,6 @@ def test_invalid_setstate(self): with self.assertRaisesRegex(TypeError, "state is not a dictionary"): e.__setstate__(42) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_notes(self): for e in [BaseException(1), Exception(2), ValueError(3)]: with self.subTest(e=e): diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 1a9c95a930e..3cf26c1d5e9 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -4,7 +4,7 @@ use crate::object::{Traverse, TraverseFn}; use crate::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::{ - PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, + PyList, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, traceback::{PyTraceback, PyTracebackRef}, }, class::{PyClassImpl, StaticType}, @@ -652,6 +652,29 @@ impl PyRef { Ok(self) } + #[pymethod] + fn add_note(self, note: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + let dict = self + .as_object() + .dict() + .ok_or_else(|| vm.new_attribute_error("Exception object has no __dict__"))?; + + let notes = if let Ok(notes) = dict.get_item("__notes__", vm) { + notes + } else { + let new_notes = vm.ctx.new_list(vec![]); + dict.set_item("__notes__", new_notes.clone().into(), vm)?; + new_notes.into() + }; + + let notes = notes + .downcast::() + .map_err(|_| vm.new_type_error("__notes__ must be a list"))?; + + notes.borrow_vec_mut().push(note.into()); + Ok(()) + } + #[pymethod] fn __reduce__(self, vm: &VirtualMachine) -> PyTupleRef { if let Some(dict) = self.as_object().dict().filter(|x| !x.is_empty()) { From db1adaa2c26f83f39f97ffbec6b03261f2792bd6 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:47:51 +0200 Subject: [PATCH 0013/1459] 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 --- .gitattributes | 1 + .github/workflows/ci.yaml | 4 +- .github/workflows/update-doc-db.yml | 95 + Cargo.lock | 73 +- Cargo.toml | 8 +- crates/doc/.gitignore | 1 + crates/doc/Cargo.toml | 17 + crates/doc/LICENSE | 277 + crates/doc/generate.py | 214 + crates/doc/src/data.inc.rs | 9007 +++++++++++++++++++++++++++ crates/doc/src/lib.rs | 12 + derive-impl/Cargo.toml | 2 +- derive-impl/src/lib.rs | 1 - derive-impl/src/pyclass.rs | 7 +- derive-impl/src/pymodule.rs | 16 +- 15 files changed, 9702 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/update-doc-db.yml create mode 100644 crates/doc/.gitignore create mode 100644 crates/doc/Cargo.toml create mode 100644 crates/doc/LICENSE create mode 100644 crates/doc/generate.py create mode 100644 crates/doc/src/data.inc.rs create mode 100644 crates/doc/src/lib.rs diff --git a/.gitattributes b/.gitattributes index f54bcd3b725..d1dd182a9b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,4 @@ 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 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 02afeff5713..9ab4610ed11 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -113,7 +113,7 @@ jobs: RUST_BACKTRACE: full name: Run rust tests runs-on: ${{ matrix.os }} - timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 30 }} + timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 35 }} strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] @@ -239,7 +239,7 @@ jobs: RUST_BACKTRACE: full name: Run snippets and cpython tests runs-on: ${{ matrix.os }} - timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 30 }} + timeout-minutes: ${{ contains(matrix.os, 'windows') && 45 || 35 }} strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] diff --git a/.github/workflows/update-doc-db.yml b/.github/workflows/update-doc-db.yml new file mode 100644 index 00000000000..dcef94f20d2 --- /dev/null +++ b/.github/workflows/update-doc-db.yml @@ -0,0 +1,95 @@ +name: Update doc DB + +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + python-version: + description: Target python version to generate doc db for + type: string + default: "3.13.9" + +defaults: + run: + shell: bash + working-directory: ./crates/rustpython-doc + +jobs: + generate: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - windows-latest + - macos-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + sparse-checkout: | + crates/rustpython-doc + + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: ${{ inputs.python-version }} + + - name: Generate docs + run: python ./generate.py + + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: doc-db-${{ inputs.python-version }}-${{ matrix.os }} + path: "crates/rustpython-doc/generated/*.json" + if-no-files-found: error + retention-days: 7 + overwrite: true + + merge: + runs-on: ubuntu-latest + needs: generate + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + sparse-checkout: | + crates/rustpython-doc + + - name: Download generated doc DBs + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + with: + pattern: "doc-db-${{ inputs.python-version }}-**" + path: crates/rustpython-doc/generated/ + merge-multiple: true + + - name: Transform JSON + run: | + # Merge all artifacts + jq -s "add" --sort-keys generated/*.json > generated/merged.json + + # Format merged json for the phf macro + jq -r 'to_entries[] | " \(.key | @json) => \(.value | @json),"' generated/merged.json > generated/raw_entries.txt + + OUTPUT_FILE='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: ${{ inputs.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 generated/raw_entries.txt >> $OUTPUT_FILE + echo '};' >> $OUTPUT_FILE + + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + with: + name: doc-db-${{ inputs.python-version }} + path: "crates/rustpython-doc/src/data.inc.rs" + if-no-files-found: error + retention-days: 7 + overwrite: true diff --git a/Cargo.lock b/Cargo.lock index 9903fd19334..e1b1d958d30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,6 +893,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fd-lock" version = "4.0.4" @@ -1825,8 +1831,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", ] [[package]] @@ -1835,8 +1851,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -1845,18 +1861,41 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", "syn", @@ -1871,6 +1910,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2489,10 +2537,9 @@ dependencies = [ [[package]] name = "rustpython-doc" -version = "0.3.0" -source = "git+https://github.com/RustPython/__doc__?tag=0.3.0#8b62ce5d796d68a091969c9fa5406276cb483f79" +version = "0.4.0" dependencies = [ - "once_cell", + "phf 0.13.1", ] [[package]] @@ -2587,7 +2634,7 @@ dependencies = [ "page_size", "parking_lot", "paste", - "phf", + "phf 0.11.3", "pymath", "rand_core 0.9.3", "rustix", @@ -3368,7 +3415,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" dependencies = [ - "phf", + "phf 0.11.3", "unicode_names2_generator 1.3.0", ] @@ -3378,7 +3425,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d189085656ca1203291e965444e7f6a2723fbdd1dd9f34f8482e79bafd8338a0" dependencies = [ - "phf", + "phf 0.11.3", "unicode_names2_generator 2.0.0", ] diff --git a/Cargo.toml b/Cargo.toml index 3cdc471dc3d..b01734ae7ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,10 @@ opt-level = 3 # https://github.com/rust-lang/rust/issues/92869 # lto = "thin" +# Doesn't change often +[profile.release.package.rustpython-doc] +codegen-units = 1 + [profile.bench] lto = "thin" codegen-units = 1 @@ -132,6 +136,7 @@ members = [ "derive-impl", "wtf8", "wasm/lib", + "crates/*", ] [workspace.package] @@ -156,13 +161,14 @@ 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-doc = { path = "crates/doc", version = "0.4.0" } ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.14.1" } ruff_python_ast = { git = "https://github.com/astral-sh/ruff.git", tag = "0.14.1" } ruff_text_size = { git = "https://github.com/astral-sh/ruff.git", tag = "0.14.1" } ruff_source_file = { git = "https://github.com/astral-sh/ruff.git", tag = "0.14.1" } +phf = { version = "0.13.1", default-features = false, features = ["macros"]} ahash = "0.8.12" ascii = "1.1" bitflags = "2.9.4" diff --git a/crates/doc/.gitignore b/crates/doc/.gitignore new file mode 100644 index 00000000000..9ab870da897 --- /dev/null +++ b/crates/doc/.gitignore @@ -0,0 +1 @@ +generated/ diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml new file mode 100644 index 00000000000..3435fabc8b5 --- /dev/null +++ b/crates/doc/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rustpython-doc" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +repository.workspace = true +license-file = "LICENSE" + +[dependencies] +phf = { workspace = true } + +[lib] +doctest = false # Crashes when true + +[lints] +workspace = true diff --git a/crates/doc/LICENSE b/crates/doc/LICENSE new file mode 100644 index 00000000000..20cf39097c6 --- /dev/null +++ b/crates/doc/LICENSE @@ -0,0 +1,277 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" +are retained in Python alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/crates/doc/generate.py b/crates/doc/generate.py new file mode 100644 index 00000000000..2f553ac0a19 --- /dev/null +++ b/crates/doc/generate.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python +import argparse +import inspect +import json +import os +import pathlib +import platform +import pydoc +import re +import sys +import types +import typing +import warnings +from importlib.machinery import EXTENSION_SUFFIXES, ExtensionFileLoader + +if typing.TYPE_CHECKING: + from collections.abc import Iterable + +OUTPUT_FILE = pathlib.Path(__file__).parent / "generated" / f"{sys.platform}.json" +OUTPUT_FILE.parent.mkdir(exist_ok=True) + +UNICODE_ESCAPE = re.compile(r"\\u([0-9]+)") + +IGNORED_MODULES = {"this", "antigravity"} +IGNORED_ATTRS = { + "__annotations__", + "__class__", + "__dict__", + "__dir__", + "__doc__", + "__file__", + "__name__", + "__qualname__", +} + + +type Parts = tuple[str, ...] + + +class DocEntry(typing.NamedTuple): + parts: Parts + raw_doc: str | None + + @property + def key(self) -> str: + return ".".join(self.parts) + + @property + def doc(self) -> str: + assert self.raw_doc is not None + + return re.sub(UNICODE_ESCAPE, r"\\u{\1}", inspect.cleandoc(self.raw_doc)) + + +def is_c_extension(module: types.ModuleType) -> bool: + """ + Check whether a module was written in C. + + Returns + ------- + bool + + Notes + ----- + Adapted from: https://stackoverflow.com/a/39304199 + """ + loader = getattr(module, "__loader__", None) + if isinstance(loader, ExtensionFileLoader): + return True + + try: + inspect.getsource(module) + except (OSError, TypeError): + return True + + try: + module_filename = inspect.getfile(module) + except TypeError: + return True + + module_filetype = os.path.splitext(module_filename)[1] + return module_filetype in EXTENSION_SUFFIXES + + +def is_child_of(obj: typing.Any, module: types.ModuleType) -> bool: + """ + Whether or not an object is a child of a module. + + Returns + ------- + bool + """ + return inspect.getmodule(obj) is module + + +def iter_modules() -> "Iterable[types.ModuleType]": + """ + Yields + ------ + :class:`types.Module` + Python modules. + """ + for module_name in sys.stdlib_module_names - IGNORED_MODULES: + try: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + module = __import__(module_name) + except ImportError: + warnings.warn(f"Could not import {module_name}", category=ImportWarning) + continue + + yield module + + +def iter_c_modules() -> "Iterable[types.ModuleType]": + """ + Yields + ------ + :class:`types.Module` + Modules that are written in C. (not pure python) + """ + yield from filter(is_c_extension, iter_modules()) + + +def traverse( + obj: typing.Any, module: types.ModuleType, parts: Parts = () +) -> "typing.Iterable[DocEntry]": + if inspect.ismodule(obj): + parts += (obj.__name__,) + + if any(f(obj) for f in (inspect.ismodule, inspect.isclass, inspect.isbuiltin)): + yield DocEntry(parts, pydoc._getowndoc(obj)) + + for name, attr in inspect.getmembers(obj): + if name in IGNORED_ATTRS: + continue + + if attr == obj: + continue + + if (module is obj) and (not is_child_of(attr, module)): + continue + + # Don't recurse into modules imported by our module. i.e. `ipaddress.py` imports `re` don't traverse `re` + if (not inspect.ismodule(obj)) and inspect.ismodule(attr): + continue + + new_parts = parts + (name,) + + attr_typ = type(attr) + is_type_or_builtin = any(attr_typ is x for x in (type, type(__builtins__))) + + if is_type_or_builtin: + yield from traverse(attr, module, new_parts) + continue + + is_callable = ( + callable(attr) + or not issubclass(attr_typ, type) + or attr_typ.__name__ in ("getset_descriptor", "member_descriptor") + ) + + is_func = any( + f(attr) + for f in (inspect.isfunction, inspect.ismethod, inspect.ismethoddescriptor) + ) + + if is_callable or is_func: + yield DocEntry(new_parts, pydoc._getowndoc(attr)) + + +def find_doc_entries() -> "Iterable[DocEntry]": + yield from ( + doc_entry + for module in iter_c_modules() + for doc_entry in traverse(module, module) + ) + yield from (doc_entry for doc_entry in traverse(__builtins__, __builtins__)) + + builtin_types = [ + type(None), + type(bytearray().__iter__()), + type(bytes().__iter__()), + type(dict().__iter__()), + type(dict().items()), + type(dict().items().__iter__()), + type(dict().values()), + type(dict().values().__iter__()), + type(lambda: ...), + type(list().__iter__()), + type(memoryview(b"").__iter__()), + type(range(0).__iter__()), + type(set().__iter__()), + type(str().__iter__()), + type(tuple().__iter__()), + ] + for typ in builtin_types: + parts = ("builtins", typ.__name__) + yield DocEntry(parts, pydoc._getowndoc(typ)) + yield from traverse(typ, __builtins__, parts) + + +def main(): + docs = { + entry.key: entry.doc + for entry in find_doc_entries() + if entry.raw_doc is not None + } + dumped = json.dumps(docs, sort_keys=True, indent=4) + OUTPUT_FILE.write_text(dumped) + + +if __name__ == "__main__": + main() diff --git a/crates/doc/src/data.inc.rs b/crates/doc/src/data.inc.rs new file mode 100644 index 00000000000..ee08b59f7ba --- /dev/null +++ b/crates/doc/src/data.inc.rs @@ -0,0 +1,9007 @@ +// This file was auto-generated by `.github/workflows/update-doc-db.yml`. +// CPython version: 3.13.9 +// spell-checker: disable + +pub static DB: phf::Map<&'static str, &'static str> = phf::phf_map! { + "_abc" => "Module contains faster C implementation of abc.ABCMeta", + "_abc._abc_init" => "Internal ABC helper for class set-up. Should be never used outside abc module.", + "_abc._abc_instancecheck" => "Internal ABC helper for instance checks. Should be never used outside abc module.", + "_abc._abc_register" => "Internal ABC helper for subclasss registration. Should be never used outside abc module.", + "_abc._abc_subclasscheck" => "Internal ABC helper for subclasss checks. Should be never used outside abc module.", + "_abc._get_dump" => "Internal ABC helper for cache and registry debugging.\n\nReturn shallow copies of registry, of both caches, and\nnegative cache version. Don't call this function directly,\ninstead use ABC._dump_registry() for a nice repr.", + "_abc._reset_caches" => "Internal ABC helper to reset both caches of a given class.\n\nShould be only used by refleak.py", + "_abc._reset_registry" => "Internal ABC helper to reset registry of a given class.\n\nShould be only used by refleak.py", + "_abc.get_cache_token" => "Returns the current ABC cache token.\n\nThe token is an opaque object (supporting equality testing) identifying the\ncurrent version of the ABC cache for virtual subclasses. The token changes\nwith every call to register() on any ABC.", + "_asyncio" => "Accelerator module for asyncio", + "_asyncio.Future" => "This class is *almost* compatible with concurrent.futures.Future.\n\nDifferences:\n\n- result() and exception() do not take a timeout argument and\n raise an exception when the future isn't done yet.\n\n- Callbacks registered with add_done_callback() are always called\n via the event loop's call_soon_threadsafe().\n\n- This class is not compatible with the wait() and as_completed()\n methods in the concurrent.futures package.", + "_asyncio.Future.__await__" => "Return an iterator to be used in await expression.", + "_asyncio.Future.__class_getitem__" => "See PEP 585", + "_asyncio.Future.__del__" => "Called when the instance is about to be destroyed.", + "_asyncio.Future.__delattr__" => "Implement delattr(self, name).", + "_asyncio.Future.__eq__" => "Return self==value.", + "_asyncio.Future.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_asyncio.Future.__ge__" => "Return self>=value.", + "_asyncio.Future.__getattribute__" => "Return getattr(self, name).", + "_asyncio.Future.__getstate__" => "Helper for pickle.", + "_asyncio.Future.__gt__" => "Return self>value.", + "_asyncio.Future.__hash__" => "Return hash(self).", + "_asyncio.Future.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_asyncio.Future.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_asyncio.Future.__iter__" => "Implement iter(self).", + "_asyncio.Future.__le__" => "Return self<=value.", + "_asyncio.Future.__lt__" => "Return self "Return self!=value.", + "_asyncio.Future.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_asyncio.Future.__reduce__" => "Helper for pickle.", + "_asyncio.Future.__reduce_ex__" => "Helper for pickle.", + "_asyncio.Future.__repr__" => "Return repr(self).", + "_asyncio.Future.__setattr__" => "Implement setattr(self, name, value).", + "_asyncio.Future.__sizeof__" => "Size of object in memory, in bytes.", + "_asyncio.Future.__str__" => "Return str(self).", + "_asyncio.Future.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_asyncio.Future._make_cancelled_error" => "Create the CancelledError to raise if the Future is cancelled.\n\nThis should only be called once when handling a cancellation since\nit erases the context exception value.", + "_asyncio.Future.add_done_callback" => "Add a callback to be run when the future becomes done.\n\nThe callback is called with a single argument - the future object. If\nthe future is already done when this is called, the callback is\nscheduled with call_soon.", + "_asyncio.Future.cancel" => "Cancel the future and schedule callbacks.\n\nIf the future is already done or cancelled, return False. Otherwise,\nchange the future's state to cancelled, schedule the callbacks and\nreturn True.", + "_asyncio.Future.cancelled" => "Return True if the future was cancelled.", + "_asyncio.Future.done" => "Return True if the future is done.\n\nDone means either that a result / exception are available, or that the\nfuture was cancelled.", + "_asyncio.Future.exception" => "Return the exception that was set on this future.\n\nThe exception (or None if no exception was set) is returned only if\nthe future is done. If the future has been cancelled, raises\nCancelledError. If the future isn't done yet, raises\nInvalidStateError.", + "_asyncio.Future.get_loop" => "Return the event loop the Future is bound to.", + "_asyncio.Future.remove_done_callback" => "Remove all instances of a callback from the \"call when done\" list.\n\nReturns the number of callbacks removed.", + "_asyncio.Future.result" => "Return the result this future represents.\n\nIf the future has been cancelled, raises CancelledError. If the\nfuture's result isn't yet available, raises InvalidStateError. If\nthe future is done and has an exception set, this exception is raised.", + "_asyncio.Future.set_exception" => "Mark the future done and set an exception.\n\nIf the future is already done when this method is called, raises\nInvalidStateError.", + "_asyncio.Future.set_result" => "Mark the future done and set its result.\n\nIf the future is already done when this method is called, raises\nInvalidStateError.", + "_asyncio.Task" => "A coroutine wrapped in a Future.", + "_asyncio.Task.__await__" => "Return an iterator to be used in await expression.", + "_asyncio.Task.__class_getitem__" => "See PEP 585", + "_asyncio.Task.__del__" => "Called when the instance is about to be destroyed.", + "_asyncio.Task.__delattr__" => "Implement delattr(self, name).", + "_asyncio.Task.__eq__" => "Return self==value.", + "_asyncio.Task.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_asyncio.Task.__ge__" => "Return self>=value.", + "_asyncio.Task.__getattribute__" => "Return getattr(self, name).", + "_asyncio.Task.__getstate__" => "Helper for pickle.", + "_asyncio.Task.__gt__" => "Return self>value.", + "_asyncio.Task.__hash__" => "Return hash(self).", + "_asyncio.Task.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_asyncio.Task.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_asyncio.Task.__iter__" => "Implement iter(self).", + "_asyncio.Task.__le__" => "Return self<=value.", + "_asyncio.Task.__lt__" => "Return self "Return self!=value.", + "_asyncio.Task.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_asyncio.Task.__reduce__" => "Helper for pickle.", + "_asyncio.Task.__reduce_ex__" => "Helper for pickle.", + "_asyncio.Task.__repr__" => "Return repr(self).", + "_asyncio.Task.__setattr__" => "Implement setattr(self, name, value).", + "_asyncio.Task.__sizeof__" => "Size of object in memory, in bytes.", + "_asyncio.Task.__str__" => "Return str(self).", + "_asyncio.Task.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_asyncio.Task._make_cancelled_error" => "Create the CancelledError to raise if the Task is cancelled.\n\nThis should only be called once when handling a cancellation since\nit erases the context exception value.", + "_asyncio.Task.add_done_callback" => "Add a callback to be run when the future becomes done.\n\nThe callback is called with a single argument - the future object. If\nthe future is already done when this is called, the callback is\nscheduled with call_soon.", + "_asyncio.Task.cancel" => "Request that this task cancel itself.\n\nThis arranges for a CancelledError to be thrown into the\nwrapped coroutine on the next cycle through the event loop.\nThe coroutine then has a chance to clean up or even deny\nthe request using try/except/finally.\n\nUnlike Future.cancel, this does not guarantee that the\ntask will be cancelled: the exception might be caught and\nacted upon, delaying cancellation of the task or preventing\ncancellation completely. The task may also return a value or\nraise a different exception.\n\nImmediately after this method is called, Task.cancelled() will\nnot return True (unless the task was already cancelled). A\ntask will be marked as cancelled when the wrapped coroutine\nterminates with a CancelledError exception (even if cancel()\nwas not called).\n\nThis also increases the task's count of cancellation requests.", + "_asyncio.Task.cancelled" => "Return True if the future was cancelled.", + "_asyncio.Task.cancelling" => "Return the count of the task's cancellation requests.\n\nThis count is incremented when .cancel() is called\nand may be decremented using .uncancel().", + "_asyncio.Task.done" => "Return True if the future is done.\n\nDone means either that a result / exception are available, or that the\nfuture was cancelled.", + "_asyncio.Task.exception" => "Return the exception that was set on this future.\n\nThe exception (or None if no exception was set) is returned only if\nthe future is done. If the future has been cancelled, raises\nCancelledError. If the future isn't done yet, raises\nInvalidStateError.", + "_asyncio.Task.get_loop" => "Return the event loop the Future is bound to.", + "_asyncio.Task.get_stack" => "Return the list of stack frames for this task's coroutine.\n\nIf the coroutine is not done, this returns the stack where it is\nsuspended. If the coroutine has completed successfully or was\ncancelled, this returns an empty list. If the coroutine was\nterminated by an exception, this returns the list of traceback\nframes.\n\nThe frames are always ordered from oldest to newest.\n\nThe optional limit gives the maximum number of frames to\nreturn; by default all available frames are returned. Its\nmeaning differs depending on whether a stack or a traceback is\nreturned: the newest frames of a stack are returned, but the\noldest frames of a traceback are returned. (This matches the\nbehavior of the traceback module.)\n\nFor reasons beyond our control, only one stack frame is\nreturned for a suspended coroutine.", + "_asyncio.Task.print_stack" => "Print the stack or traceback for this task's coroutine.\n\nThis produces output similar to that of the traceback module,\nfor the frames retrieved by get_stack(). The limit argument\nis passed to get_stack(). The file argument is an I/O stream\nto which the output is written; by default output is written\nto sys.stderr.", + "_asyncio.Task.remove_done_callback" => "Remove all instances of a callback from the \"call when done\" list.\n\nReturns the number of callbacks removed.", + "_asyncio.Task.result" => "Return the result this future represents.\n\nIf the future has been cancelled, raises CancelledError. If the\nfuture's result isn't yet available, raises InvalidStateError. If\nthe future is done and has an exception set, this exception is raised.", + "_asyncio.Task.uncancel" => "Decrement the task's count of cancellation requests.\n\nThis should be used by tasks that catch CancelledError\nand wish to continue indefinitely until they are cancelled again.\n\nReturns the remaining number of cancellation requests.", + "_asyncio._enter_task" => "Enter into task execution or resume suspended task.\n\nTask belongs to loop.\n\nReturns None.", + "_asyncio._get_running_loop" => "Return the running event loop or None.\n\nThis is a low-level function intended to be used by event loops.\nThis function is thread-specific.", + "_asyncio._leave_task" => "Leave task execution or suspend a task.\n\nTask belongs to loop.\n\nReturns None.", + "_asyncio._register_eager_task" => "Register a new task in asyncio as executed by loop.\n\nReturns None.", + "_asyncio._register_task" => "Register a new task in asyncio as executed by loop.\n\nReturns None.", + "_asyncio._set_running_loop" => "Set the running event loop.\n\nThis is a low-level function intended to be used by event loops.\nThis function is thread-specific.", + "_asyncio._swap_current_task" => "Temporarily swap in the supplied task and return the original one (or None).\n\nThis is intended for use during eager coroutine execution.", + "_asyncio._unregister_eager_task" => "Unregister a task.\n\nReturns None.", + "_asyncio._unregister_task" => "Unregister a task.\n\nReturns None.", + "_asyncio.current_task" => "Return a currently executed task.", + "_asyncio.get_event_loop" => "Return an asyncio event loop.\n\nWhen called from a coroutine or a callback (e.g. scheduled with\ncall_soon or similar API), this function will always return the\nrunning event loop.\n\nIf there is no running event loop set, the function will return\nthe result of `get_event_loop_policy().get_event_loop()` call.", + "_asyncio.get_running_loop" => "Return the running event loop. Raise a RuntimeError if there is none.\n\nThis function is thread-specific.", + "_bisect" => "Bisection algorithms.\n\nThis module provides support for maintaining a list in sorted order without\nhaving to sort the list after each insertion. For long lists of items with\nexpensive comparison operations, this can be an improvement over the more\ncommon approach.", + "_bisect.bisect_left" => "Return the index where to insert item x in list a, assuming a is sorted.\n\nThe return value i is such that all e in a[:i] have e < x, and all e in\na[i:] have e >= x. So if x already appears in the list, a.insert(i, x) will\ninsert just before the leftmost x already there.\n\nOptional args lo (default 0) and hi (default len(a)) bound the\nslice of a to be searched.\n\nA custom key function can be supplied to customize the sort order.", + "_bisect.bisect_right" => "Return the index where to insert item x in list a, assuming a is sorted.\n\nThe return value i is such that all e in a[:i] have e <= x, and all e in\na[i:] have e > x. So if x already appears in the list, a.insert(i, x) will\ninsert just after the rightmost x already there.\n\nOptional args lo (default 0) and hi (default len(a)) bound the\nslice of a to be searched.\n\nA custom key function can be supplied to customize the sort order.", + "_bisect.insort_left" => "Insert item x in list a, and keep it sorted assuming a is sorted.\n\nIf x is already in a, insert it to the left of the leftmost x.\n\nOptional args lo (default 0) and hi (default len(a)) bound the\nslice of a to be searched.\n\nA custom key function can be supplied to customize the sort order.", + "_bisect.insort_right" => "Insert item x in list a, and keep it sorted assuming a is sorted.\n\nIf x is already in a, insert it to the right of the rightmost x.\n\nOptional args lo (default 0) and hi (default len(a)) bound the\nslice of a to be searched.\n\nA custom key function can be supplied to customize the sort order.", + "_blake2" => "_blake2b provides BLAKE2b for hashlib", + "_blake2.blake2b" => "Return a new BLAKE2b hash object.", + "_blake2.blake2b.__delattr__" => "Implement delattr(self, name).", + "_blake2.blake2b.__eq__" => "Return self==value.", + "_blake2.blake2b.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_blake2.blake2b.__ge__" => "Return self>=value.", + "_blake2.blake2b.__getattribute__" => "Return getattr(self, name).", + "_blake2.blake2b.__getstate__" => "Helper for pickle.", + "_blake2.blake2b.__gt__" => "Return self>value.", + "_blake2.blake2b.__hash__" => "Return hash(self).", + "_blake2.blake2b.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_blake2.blake2b.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_blake2.blake2b.__le__" => "Return self<=value.", + "_blake2.blake2b.__lt__" => "Return self "Return self!=value.", + "_blake2.blake2b.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_blake2.blake2b.__reduce__" => "Helper for pickle.", + "_blake2.blake2b.__reduce_ex__" => "Helper for pickle.", + "_blake2.blake2b.__repr__" => "Return repr(self).", + "_blake2.blake2b.__setattr__" => "Implement setattr(self, name, value).", + "_blake2.blake2b.__sizeof__" => "Size of object in memory, in bytes.", + "_blake2.blake2b.__str__" => "Return str(self).", + "_blake2.blake2b.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_blake2.blake2b.copy" => "Return a copy of the hash object.", + "_blake2.blake2b.digest" => "Return the digest value as a bytes object.", + "_blake2.blake2b.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_blake2.blake2b.update" => "Update this hash object's state with the provided bytes-like object.", + "_blake2.blake2s" => "Return a new BLAKE2s hash object.", + "_blake2.blake2s.__delattr__" => "Implement delattr(self, name).", + "_blake2.blake2s.__eq__" => "Return self==value.", + "_blake2.blake2s.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_blake2.blake2s.__ge__" => "Return self>=value.", + "_blake2.blake2s.__getattribute__" => "Return getattr(self, name).", + "_blake2.blake2s.__getstate__" => "Helper for pickle.", + "_blake2.blake2s.__gt__" => "Return self>value.", + "_blake2.blake2s.__hash__" => "Return hash(self).", + "_blake2.blake2s.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_blake2.blake2s.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_blake2.blake2s.__le__" => "Return self<=value.", + "_blake2.blake2s.__lt__" => "Return self "Return self!=value.", + "_blake2.blake2s.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_blake2.blake2s.__reduce__" => "Helper for pickle.", + "_blake2.blake2s.__reduce_ex__" => "Helper for pickle.", + "_blake2.blake2s.__repr__" => "Return repr(self).", + "_blake2.blake2s.__setattr__" => "Implement setattr(self, name, value).", + "_blake2.blake2s.__sizeof__" => "Size of object in memory, in bytes.", + "_blake2.blake2s.__str__" => "Return str(self).", + "_blake2.blake2s.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_blake2.blake2s.copy" => "Return a copy of the hash object.", + "_blake2.blake2s.digest" => "Return the digest value as a bytes object.", + "_blake2.blake2s.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_blake2.blake2s.update" => "Update this hash object's state with the provided bytes-like object.", + "_bz2.BZ2Compressor" => "Create a compressor object for compressing data incrementally.\n\n compresslevel\n Compression level, as a number between 1 and 9.\n\nFor one-shot compression, use the compress() function instead.", + "_bz2.BZ2Compressor.__delattr__" => "Implement delattr(self, name).", + "_bz2.BZ2Compressor.__eq__" => "Return self==value.", + "_bz2.BZ2Compressor.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_bz2.BZ2Compressor.__ge__" => "Return self>=value.", + "_bz2.BZ2Compressor.__getattribute__" => "Return getattr(self, name).", + "_bz2.BZ2Compressor.__getstate__" => "Helper for pickle.", + "_bz2.BZ2Compressor.__gt__" => "Return self>value.", + "_bz2.BZ2Compressor.__hash__" => "Return hash(self).", + "_bz2.BZ2Compressor.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_bz2.BZ2Compressor.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_bz2.BZ2Compressor.__le__" => "Return self<=value.", + "_bz2.BZ2Compressor.__lt__" => "Return self "Return self!=value.", + "_bz2.BZ2Compressor.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_bz2.BZ2Compressor.__reduce__" => "Helper for pickle.", + "_bz2.BZ2Compressor.__reduce_ex__" => "Helper for pickle.", + "_bz2.BZ2Compressor.__repr__" => "Return repr(self).", + "_bz2.BZ2Compressor.__setattr__" => "Implement setattr(self, name, value).", + "_bz2.BZ2Compressor.__sizeof__" => "Size of object in memory, in bytes.", + "_bz2.BZ2Compressor.__str__" => "Return str(self).", + "_bz2.BZ2Compressor.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_bz2.BZ2Compressor.compress" => "Provide data to the compressor object.\n\nReturns a chunk of compressed data if possible, or b'' otherwise.\n\nWhen you have finished providing data to the compressor, call the\nflush() method to finish the compression process.", + "_bz2.BZ2Compressor.flush" => "Finish the compression process.\n\nReturns the compressed data left in internal buffers.\n\nThe compressor object may not be used after this method is called.", + "_bz2.BZ2Decompressor" => "Create a decompressor object for decompressing data incrementally.\n\nFor one-shot decompression, use the decompress() function instead.", + "_bz2.BZ2Decompressor.__delattr__" => "Implement delattr(self, name).", + "_bz2.BZ2Decompressor.__eq__" => "Return self==value.", + "_bz2.BZ2Decompressor.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_bz2.BZ2Decompressor.__ge__" => "Return self>=value.", + "_bz2.BZ2Decompressor.__getattribute__" => "Return getattr(self, name).", + "_bz2.BZ2Decompressor.__getstate__" => "Helper for pickle.", + "_bz2.BZ2Decompressor.__gt__" => "Return self>value.", + "_bz2.BZ2Decompressor.__hash__" => "Return hash(self).", + "_bz2.BZ2Decompressor.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_bz2.BZ2Decompressor.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_bz2.BZ2Decompressor.__le__" => "Return self<=value.", + "_bz2.BZ2Decompressor.__lt__" => "Return self "Return self!=value.", + "_bz2.BZ2Decompressor.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_bz2.BZ2Decompressor.__reduce__" => "Helper for pickle.", + "_bz2.BZ2Decompressor.__reduce_ex__" => "Helper for pickle.", + "_bz2.BZ2Decompressor.__repr__" => "Return repr(self).", + "_bz2.BZ2Decompressor.__setattr__" => "Implement setattr(self, name, value).", + "_bz2.BZ2Decompressor.__sizeof__" => "Size of object in memory, in bytes.", + "_bz2.BZ2Decompressor.__str__" => "Return str(self).", + "_bz2.BZ2Decompressor.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_bz2.BZ2Decompressor.decompress" => "Decompress *data*, returning uncompressed data as bytes.\n\nIf *max_length* is nonnegative, returns at most *max_length* bytes of\ndecompressed data. If this limit is reached and further output can be\nproduced, *self.needs_input* will be set to ``False``. In this case, the next\ncall to *decompress()* may provide *data* as b'' to obtain more of the output.\n\nIf all of the input data was decompressed and returned (either because this\nwas less than *max_length* bytes, or because *max_length* was negative),\n*self.needs_input* will be set to True.\n\nAttempting to decompress data after the end of stream is reached raises an\nEOFError. Any data found after the end of the stream is ignored and saved in\nthe unused_data attribute.", + "_bz2.BZ2Decompressor.eof" => "True if the end-of-stream marker has been reached.", + "_bz2.BZ2Decompressor.needs_input" => "True if more input is needed before more decompressed data can be produced.", + "_bz2.BZ2Decompressor.unused_data" => "Data found after the end of the compressed stream.", + "_codecs.decode" => "Decodes obj using the codec registered for encoding.\n\nDefault encoding is 'utf-8'. errors may be given to set a\ndifferent error handling scheme. Default is 'strict' meaning that encoding\nerrors raise a ValueError. Other possible values are 'ignore', 'replace'\nand 'backslashreplace' as well as any other name registered with\ncodecs.register_error that can handle ValueErrors.", + "_codecs.encode" => "Encodes obj using the codec registered for encoding.\n\nThe default encoding is 'utf-8'. errors may be given to set a\ndifferent error handling scheme. Default is 'strict' meaning that encoding\nerrors raise a ValueError. Other possible values are 'ignore', 'replace'\nand 'backslashreplace' as well as any other name registered with\ncodecs.register_error that can handle ValueErrors.", + "_codecs.lookup" => "Looks up a codec tuple in the Python codec registry and returns a CodecInfo object.", + "_codecs.lookup_error" => "lookup_error(errors) -> handler\n\nReturn the error handler for the specified error handling name or raise a\nLookupError, if no handler exists under this name.", + "_codecs.register" => "Register a codec search function.\n\nSearch functions are expected to take one argument, the encoding name in\nall lower case letters, and either return None, or a tuple of functions\n(encoder, decoder, stream_reader, stream_writer) (or a CodecInfo object).", + "_codecs.register_error" => "Register the specified error handler under the name errors.\n\nhandler must be a callable object, that will be called with an exception\ninstance containing information about the location of the encoding/decoding\nerror and must return a (replacement, new position) tuple.", + "_codecs.unregister" => "Unregister a codec search function and clear the registry's cache.\n\nIf the search function is not registered, do nothing.", + "_collections" => "High performance data structures.\n- deque: ordered collection accessible from endpoints only\n- defaultdict: dict subclass with a default value factory", + "_collections._count_elements" => "Count elements in the iterable, updating the mapping", + "_contextvars" => "Context Variables", + "_contextvars.Context.__contains__" => "Return bool(key in self).", + "_contextvars.Context.__delattr__" => "Implement delattr(self, name).", + "_contextvars.Context.__eq__" => "Return self==value.", + "_contextvars.Context.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_contextvars.Context.__ge__" => "Return self>=value.", + "_contextvars.Context.__getattribute__" => "Return getattr(self, name).", + "_contextvars.Context.__getitem__" => "Return self[key].", + "_contextvars.Context.__getstate__" => "Helper for pickle.", + "_contextvars.Context.__gt__" => "Return self>value.", + "_contextvars.Context.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_contextvars.Context.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_contextvars.Context.__iter__" => "Implement iter(self).", + "_contextvars.Context.__le__" => "Return self<=value.", + "_contextvars.Context.__len__" => "Return len(self).", + "_contextvars.Context.__lt__" => "Return self "Return self!=value.", + "_contextvars.Context.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_contextvars.Context.__reduce__" => "Helper for pickle.", + "_contextvars.Context.__reduce_ex__" => "Helper for pickle.", + "_contextvars.Context.__repr__" => "Return repr(self).", + "_contextvars.Context.__setattr__" => "Implement setattr(self, name, value).", + "_contextvars.Context.__sizeof__" => "Size of object in memory, in bytes.", + "_contextvars.Context.__str__" => "Return str(self).", + "_contextvars.Context.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_contextvars.Context.copy" => "Return a shallow copy of the context object.", + "_contextvars.Context.get" => "Return the value for `key` if `key` has the value in the context object.\n\nIf `key` does not exist, return `default`. If `default` is not given,\nreturn None.", + "_contextvars.Context.items" => "Return all variables and their values in the context object.\n\nThe result is returned as a list of 2-tuples (variable, value).", + "_contextvars.Context.keys" => "Return a list of all variables in the context object.", + "_contextvars.Context.values" => "Return a list of all variables' values in the context object.", + "_contextvars.ContextVar.__class_getitem__" => "See PEP 585", + "_contextvars.ContextVar.__delattr__" => "Implement delattr(self, name).", + "_contextvars.ContextVar.__eq__" => "Return self==value.", + "_contextvars.ContextVar.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_contextvars.ContextVar.__ge__" => "Return self>=value.", + "_contextvars.ContextVar.__getattribute__" => "Return getattr(self, name).", + "_contextvars.ContextVar.__getstate__" => "Helper for pickle.", + "_contextvars.ContextVar.__gt__" => "Return self>value.", + "_contextvars.ContextVar.__hash__" => "Return hash(self).", + "_contextvars.ContextVar.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_contextvars.ContextVar.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_contextvars.ContextVar.__le__" => "Return self<=value.", + "_contextvars.ContextVar.__lt__" => "Return self "Return self!=value.", + "_contextvars.ContextVar.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_contextvars.ContextVar.__reduce__" => "Helper for pickle.", + "_contextvars.ContextVar.__reduce_ex__" => "Helper for pickle.", + "_contextvars.ContextVar.__repr__" => "Return repr(self).", + "_contextvars.ContextVar.__setattr__" => "Implement setattr(self, name, value).", + "_contextvars.ContextVar.__sizeof__" => "Size of object in memory, in bytes.", + "_contextvars.ContextVar.__str__" => "Return str(self).", + "_contextvars.ContextVar.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_contextvars.ContextVar.get" => "Return a value for the context variable for the current context.\n\nIf there is no value for the variable in the current context, the method will:\n * return the value of the default argument of the method, if provided; or\n * return the default value for the context variable, if it was created\n with one; or\n * raise a LookupError.", + "_contextvars.ContextVar.reset" => "Reset the context variable.\n\nThe variable is reset to the value it had before the `ContextVar.set()` that\ncreated the token was used.", + "_contextvars.ContextVar.set" => "Call to set a new value for the context variable in the current context.\n\nThe required value argument is the new value for the context variable.\n\nReturns a Token object that can be used to restore the variable to its previous\nvalue via the `ContextVar.reset()` method.", + "_contextvars.Token.__class_getitem__" => "See PEP 585", + "_contextvars.Token.__delattr__" => "Implement delattr(self, name).", + "_contextvars.Token.__eq__" => "Return self==value.", + "_contextvars.Token.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_contextvars.Token.__ge__" => "Return self>=value.", + "_contextvars.Token.__getattribute__" => "Return getattr(self, name).", + "_contextvars.Token.__getstate__" => "Helper for pickle.", + "_contextvars.Token.__gt__" => "Return self>value.", + "_contextvars.Token.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_contextvars.Token.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_contextvars.Token.__le__" => "Return self<=value.", + "_contextvars.Token.__lt__" => "Return self "Return self!=value.", + "_contextvars.Token.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_contextvars.Token.__reduce__" => "Helper for pickle.", + "_contextvars.Token.__reduce_ex__" => "Helper for pickle.", + "_contextvars.Token.__repr__" => "Return repr(self).", + "_contextvars.Token.__setattr__" => "Implement setattr(self, name, value).", + "_contextvars.Token.__sizeof__" => "Size of object in memory, in bytes.", + "_contextvars.Token.__str__" => "Return str(self).", + "_contextvars.Token.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_csv" => "CSV parsing and writing.", + "_csv.Dialect" => "CSV dialect\n\nThe Dialect type records CSV parsing and generation options.", + "_csv.Dialect.__delattr__" => "Implement delattr(self, name).", + "_csv.Dialect.__eq__" => "Return self==value.", + "_csv.Dialect.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_csv.Dialect.__ge__" => "Return self>=value.", + "_csv.Dialect.__getattribute__" => "Return getattr(self, name).", + "_csv.Dialect.__getstate__" => "Helper for pickle.", + "_csv.Dialect.__gt__" => "Return self>value.", + "_csv.Dialect.__hash__" => "Return hash(self).", + "_csv.Dialect.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_csv.Dialect.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_csv.Dialect.__le__" => "Return self<=value.", + "_csv.Dialect.__lt__" => "Return self "Return self!=value.", + "_csv.Dialect.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_csv.Dialect.__reduce__" => "raises an exception to avoid pickling", + "_csv.Dialect.__reduce_ex__" => "raises an exception to avoid pickling", + "_csv.Dialect.__repr__" => "Return repr(self).", + "_csv.Dialect.__setattr__" => "Implement setattr(self, name, value).", + "_csv.Dialect.__sizeof__" => "Size of object in memory, in bytes.", + "_csv.Dialect.__str__" => "Return str(self).", + "_csv.Dialect.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_csv.Error.__cause__" => "exception cause", + "_csv.Error.__context__" => "exception context", + "_csv.Error.__delattr__" => "Implement delattr(self, name).", + "_csv.Error.__eq__" => "Return self==value.", + "_csv.Error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_csv.Error.__ge__" => "Return self>=value.", + "_csv.Error.__getattribute__" => "Return getattr(self, name).", + "_csv.Error.__getstate__" => "Helper for pickle.", + "_csv.Error.__gt__" => "Return self>value.", + "_csv.Error.__hash__" => "Return hash(self).", + "_csv.Error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_csv.Error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_csv.Error.__le__" => "Return self<=value.", + "_csv.Error.__lt__" => "Return self "Return self!=value.", + "_csv.Error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_csv.Error.__reduce_ex__" => "Helper for pickle.", + "_csv.Error.__repr__" => "Return repr(self).", + "_csv.Error.__setattr__" => "Implement setattr(self, name, value).", + "_csv.Error.__sizeof__" => "Size of object in memory, in bytes.", + "_csv.Error.__str__" => "Return str(self).", + "_csv.Error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_csv.Error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_csv.Error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_csv.Reader" => "CSV reader\n\nReader objects are responsible for reading and parsing tabular data\nin CSV format.", + "_csv.Reader.__delattr__" => "Implement delattr(self, name).", + "_csv.Reader.__eq__" => "Return self==value.", + "_csv.Reader.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_csv.Reader.__ge__" => "Return self>=value.", + "_csv.Reader.__getattribute__" => "Return getattr(self, name).", + "_csv.Reader.__getstate__" => "Helper for pickle.", + "_csv.Reader.__gt__" => "Return self>value.", + "_csv.Reader.__hash__" => "Return hash(self).", + "_csv.Reader.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_csv.Reader.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_csv.Reader.__iter__" => "Implement iter(self).", + "_csv.Reader.__le__" => "Return self<=value.", + "_csv.Reader.__lt__" => "Return self "Return self!=value.", + "_csv.Reader.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_csv.Reader.__next__" => "Implement next(self).", + "_csv.Reader.__reduce__" => "Helper for pickle.", + "_csv.Reader.__reduce_ex__" => "Helper for pickle.", + "_csv.Reader.__repr__" => "Return repr(self).", + "_csv.Reader.__setattr__" => "Implement setattr(self, name, value).", + "_csv.Reader.__sizeof__" => "Size of object in memory, in bytes.", + "_csv.Reader.__str__" => "Return str(self).", + "_csv.Reader.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_csv.Writer" => "CSV writer\n\nWriter objects are responsible for generating tabular data\nin CSV format from sequence input.", + "_csv.Writer.__delattr__" => "Implement delattr(self, name).", + "_csv.Writer.__eq__" => "Return self==value.", + "_csv.Writer.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_csv.Writer.__ge__" => "Return self>=value.", + "_csv.Writer.__getattribute__" => "Return getattr(self, name).", + "_csv.Writer.__getstate__" => "Helper for pickle.", + "_csv.Writer.__gt__" => "Return self>value.", + "_csv.Writer.__hash__" => "Return hash(self).", + "_csv.Writer.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_csv.Writer.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_csv.Writer.__le__" => "Return self<=value.", + "_csv.Writer.__lt__" => "Return self "Return self!=value.", + "_csv.Writer.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_csv.Writer.__reduce__" => "Helper for pickle.", + "_csv.Writer.__reduce_ex__" => "Helper for pickle.", + "_csv.Writer.__repr__" => "Return repr(self).", + "_csv.Writer.__setattr__" => "Implement setattr(self, name, value).", + "_csv.Writer.__sizeof__" => "Size of object in memory, in bytes.", + "_csv.Writer.__str__" => "Return str(self).", + "_csv.Writer.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_csv.Writer.writerow" => "Construct and write a CSV record from an iterable of fields.\n\nNon-string elements will be converted to string.", + "_csv.Writer.writerows" => "Construct and write a series of iterables to a csv file.\n\nNon-string elements will be converted to string.", + "_csv.field_size_limit" => "Sets an upper limit on parsed fields.\n\nReturns old limit. If limit is not given, no new limit is set and\nthe old limit is returned", + "_csv.get_dialect" => "Return the dialect instance associated with name.", + "_csv.list_dialects" => "Return a list of all known dialect names.", + "_csv.reader" => "Return a reader object that will process lines from the given iterable.\n\nThe \"iterable\" argument can be any object that returns a line\nof input for each iteration, such as a file object or a list. The\noptional \"dialect\" argument defines a CSV dialect. The function\nalso accepts optional keyword arguments which override settings\nprovided by the dialect.\n\nThe returned object is an iterator. Each iteration returns a row\nof the CSV file (which can span multiple input lines).", + "_csv.register_dialect" => "Create a mapping from a string name to a CVS dialect.\n\nThe optional \"dialect\" argument specifies the base dialect instance\nor the name of the registered dialect. The function also accepts\noptional keyword arguments which override settings provided by the\ndialect.", + "_csv.unregister_dialect" => "Delete the name/dialect mapping associated with a string name.", + "_csv.writer" => "Return a writer object that will write user data on the given file object.\n\nThe \"fileobj\" argument can be any object that supports the file API.\nThe optional \"dialect\" argument defines a CSV dialect. The function\nalso accepts optional keyword arguments which override settings\nprovided by the dialect.", + "_ctypes" => "Create and manipulate C compatible data types in Python.", + "_ctypes.Array" => "Abstract base class for arrays.\n\nThe recommended way to create concrete array types is by multiplying any\nctypes data type with a non-negative integer. Alternatively, you can subclass\nthis type and define _length_ and _type_ class variables. Array elements can\nbe read and written using standard subscript and slice accesses for slice\nreads, the resulting object is not itself an Array.", + "_ctypes.CFuncPtr" => "Function Pointer", + "_ctypes.COMError" => "Raised when a COM method call failed.", + "_ctypes.COMError.__cause__" => "exception cause", + "_ctypes.COMError.__context__" => "exception context", + "_ctypes.COMError.__delattr__" => "Implement delattr(self, name).", + "_ctypes.COMError.__eq__" => "Return self==value.", + "_ctypes.COMError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ctypes.COMError.__ge__" => "Return self>=value.", + "_ctypes.COMError.__getattribute__" => "Return getattr(self, name).", + "_ctypes.COMError.__getstate__" => "Helper for pickle.", + "_ctypes.COMError.__gt__" => "Return self>value.", + "_ctypes.COMError.__hash__" => "Return hash(self).", + "_ctypes.COMError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ctypes.COMError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ctypes.COMError.__le__" => "Return self<=value.", + "_ctypes.COMError.__lt__" => "Return self "Return self!=value.", + "_ctypes.COMError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ctypes.COMError.__reduce_ex__" => "Helper for pickle.", + "_ctypes.COMError.__repr__" => "Return repr(self).", + "_ctypes.COMError.__setattr__" => "Implement setattr(self, name, value).", + "_ctypes.COMError.__sizeof__" => "Size of object in memory, in bytes.", + "_ctypes.COMError.__str__" => "Return str(self).", + "_ctypes.COMError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ctypes.COMError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_ctypes.COMError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_ctypes.CopyComPointer" => "CopyComPointer(src, dst) -> HRESULT value", + "_ctypes.FormatError" => "FormatError([integer]) -> string\n\nConvert a win32 error code into a string. If the error code is not\ngiven, the return value of a call to GetLastError() is used.", + "_ctypes.FreeLibrary" => "FreeLibrary(handle) -> void\n\nFree the handle of an executable previously loaded by LoadLibrary.", + "_ctypes.LoadLibrary" => "LoadLibrary(name, load_flags) -> handle\n\nLoad an executable (usually a DLL), and return a handle to it.\nThe handle may be used to locate exported functions in this\nmodule. load_flags are as defined for LoadLibraryEx in the\nWindows API.", + "_ctypes.POINTER" => "Create and return a new ctypes pointer type.\n\n type\n A ctypes type.\n\nPointer types are cached and reused internally,\nso calling this function repeatedly is cheap.", + "_ctypes.Structure" => "Structure base class", + "_ctypes.Union" => "Union base class", + "_ctypes._Pointer" => "XXX to be provided", + "_ctypes._SimpleCData" => "XXX to be provided", + "_ctypes._dyld_shared_cache_contains_path" => "check if path is in the shared cache", + "_ctypes.addressof" => "addressof(C instance) -> integer\nReturn the address of the C instance internal buffer", + "_ctypes.alignment" => "alignment(C type) -> integer\nalignment(C instance) -> integer\nReturn the alignment requirements of a C instance", + "_ctypes.buffer_info" => "Return buffer interface information", + "_ctypes.byref" => "byref(C instance[, offset=0]) -> byref-object\nReturn a pointer lookalike to a C instance, only usable\nas function argument", + "_ctypes.dlclose" => "dlclose a library", + "_ctypes.dlopen" => "dlopen(name, flag={RTLD_GLOBAL|RTLD_LOCAL}) open a shared library", + "_ctypes.dlsym" => "find symbol in shared library", + "_ctypes.pointer" => "Create a new pointer instance, pointing to 'obj'.\n\nThe returned object is of the type POINTER(type(obj)). Note that if you\njust want to pass a pointer to an object to a foreign function call, you\nshould use byref(obj) which is much faster.", + "_ctypes.resize" => "Resize the memory buffer of a ctypes instance", + "_ctypes.sizeof" => "sizeof(C type) -> integer\nsizeof(C instance) -> integer\nReturn the size in bytes of a C instance", + "_curses.baudrate" => "Return the output speed of the terminal in bits per second.", + "_curses.beep" => "Emit a short attention sound.", + "_curses.can_change_color" => "Return True if the programmer can change the colors displayed by the terminal.", + "_curses.cbreak" => "Enter cbreak mode.\n\n flag\n If false, the effect is the same as calling nocbreak().\n\nIn cbreak mode (sometimes called \"rare\" mode) normal tty line buffering is\nturned off and characters are available to be read one by one. However,\nunlike raw mode, special characters (interrupt, quit, suspend, and flow\ncontrol) retain their effects on the tty driver and calling program.\nCalling first raw() then cbreak() leaves the terminal in cbreak mode.", + "_curses.color_content" => "Return the red, green, and blue (RGB) components of the specified color.\n\n color_number\n The number of the color (0 - (COLORS-1)).\n\nA 3-tuple is returned, containing the R, G, B values for the given color,\nwhich will be between 0 (no component) and 1000 (maximum amount of component).", + "_curses.color_pair" => "Return the attribute value for displaying text in the specified color.\n\n pair_number\n The number of the color pair.\n\nThis attribute value can be combined with A_STANDOUT, A_REVERSE, and the\nother A_* attributes. pair_number() is the counterpart to this function.", + "_curses.curs_set" => "Set the cursor state.\n\n visibility\n 0 for invisible, 1 for normal visible, or 2 for very visible.\n\nIf the terminal supports the visibility requested, the previous cursor\nstate is returned; otherwise, an exception is raised. On many terminals,\nthe \"visible\" mode is an underline cursor and the \"very visible\" mode is\na block cursor.", + "_curses.def_prog_mode" => "Save the current terminal mode as the \"program\" mode.\n\nThe \"program\" mode is the mode when the running program is using curses.\n\nSubsequent calls to reset_prog_mode() will restore this mode.", + "_curses.def_shell_mode" => "Save the current terminal mode as the \"shell\" mode.\n\nThe \"shell\" mode is the mode when the running program is not using curses.\n\nSubsequent calls to reset_shell_mode() will restore this mode.", + "_curses.delay_output" => "Insert a pause in output.\n\nms\n Duration in milliseconds.", + "_curses.doupdate" => "Update the physical screen to match the virtual screen.", + "_curses.echo" => "Enter echo mode.\n\n flag\n If false, the effect is the same as calling noecho().\n\nIn echo mode, each character input is echoed to the screen as it is entered.", + "_curses.endwin" => "De-initialize the library, and return terminal to normal status.", + "_curses.erasechar" => "Return the user's current erase character.", + "_curses.error.__cause__" => "exception cause", + "_curses.error.__context__" => "exception context", + "_curses.error.__delattr__" => "Implement delattr(self, name).", + "_curses.error.__eq__" => "Return self==value.", + "_curses.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_curses.error.__ge__" => "Return self>=value.", + "_curses.error.__getattribute__" => "Return getattr(self, name).", + "_curses.error.__getstate__" => "Helper for pickle.", + "_curses.error.__gt__" => "Return self>value.", + "_curses.error.__hash__" => "Return hash(self).", + "_curses.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_curses.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_curses.error.__le__" => "Return self<=value.", + "_curses.error.__lt__" => "Return self "Return self!=value.", + "_curses.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_curses.error.__reduce_ex__" => "Helper for pickle.", + "_curses.error.__repr__" => "Return repr(self).", + "_curses.error.__setattr__" => "Implement setattr(self, name, value).", + "_curses.error.__sizeof__" => "Size of object in memory, in bytes.", + "_curses.error.__str__" => "Return str(self).", + "_curses.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_curses.error.__weakref__" => "list of weak references to the object", + "_curses.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_curses.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_curses.flash" => "Flash the screen.\n\nThat is, change it to reverse-video and then change it back in a short interval.", + "_curses.flushinp" => "Flush all input buffers.\n\nThis throws away any typeahead that has been typed by the user and has not\nyet been processed by the program.", + "_curses.get_escdelay" => "Gets the curses ESCDELAY setting.\n\nGets the number of milliseconds to wait after reading an escape character,\nto distinguish between an individual escape character entered on the\nkeyboard from escape sequences sent by cursor and function keys.", + "_curses.get_tabsize" => "Gets the curses TABSIZE setting.\n\nGets the number of columns used by the curses library when converting a tab\ncharacter to spaces as it adds the tab to a window.", + "_curses.getmouse" => "Retrieve the queued mouse event.\n\nAfter getch() returns KEY_MOUSE to signal a mouse event, this function\nreturns a 5-tuple (id, x, y, z, bstate).", + "_curses.getsyx" => "Return the current coordinates of the virtual screen cursor.\n\nReturn a (y, x) tuple. If leaveok is currently true, return (-1, -1).", + "_curses.getwin" => "Read window related data stored in the file by an earlier putwin() call.\n\nThe routine then creates and initializes a new window using that data,\nreturning the new window object.", + "_curses.halfdelay" => "Enter half-delay mode.\n\n tenths\n Maximal blocking delay in tenths of seconds (1 - 255).\n\nUse nocbreak() to leave half-delay mode.", + "_curses.has_colors" => "Return True if the terminal can display colors; otherwise, return False.", + "_curses.has_extended_color_support" => "Return True if the module supports extended colors; otherwise, return False.\n\nExtended color support allows more than 256 color-pairs for terminals\nthat support more than 16 colors (e.g. xterm-256color).", + "_curses.has_ic" => "Return True if the terminal has insert- and delete-character capabilities.", + "_curses.has_il" => "Return True if the terminal has insert- and delete-line capabilities.", + "_curses.has_key" => "Return True if the current terminal type recognizes a key with that value.\n\nkey\n Key number.", + "_curses.init_color" => "Change the definition of a color.\n\n color_number\n The number of the color to be changed (0 - (COLORS-1)).\n r\n Red component (0 - 1000).\n g\n Green component (0 - 1000).\n b\n Blue component (0 - 1000).\n\nWhen init_color() is used, all occurrences of that color on the screen\nimmediately change to the new definition. This function is a no-op on\nmost terminals; it is active only if can_change_color() returns true.", + "_curses.init_pair" => "Change the definition of a color-pair.\n\n pair_number\n The number of the color-pair to be changed (1 - (COLOR_PAIRS-1)).\n fg\n Foreground color number (-1 - (COLORS-1)).\n bg\n Background color number (-1 - (COLORS-1)).\n\nIf the color-pair was previously initialized, the screen is refreshed and\nall occurrences of that color-pair are changed to the new definition.", + "_curses.initscr" => "Initialize the library.\n\nReturn a WindowObject which represents the whole screen.", + "_curses.is_term_resized" => "Return True if resize_term() would modify the window structure, False otherwise.\n\nnlines\n Height.\nncols\n Width.", + "_curses.isendwin" => "Return True if endwin() has been called.", + "_curses.keyname" => "Return the name of specified key.\n\nkey\n Key number.", + "_curses.killchar" => "Return the user's current line kill character.", + "_curses.longname" => "Return the terminfo long name field describing the current terminal.\n\nThe maximum length of a verbose description is 128 characters. It is defined\nonly after the call to initscr().", + "_curses.meta" => "Enable/disable meta keys.\n\nIf yes is True, allow 8-bit characters to be input. If yes is False,\nallow only 7-bit characters.", + "_curses.mouseinterval" => "Set and retrieve the maximum time between press and release in a click.\n\n interval\n Time in milliseconds.\n\nSet the maximum time that can elapse between press and release events in\norder for them to be recognized as a click, and return the previous interval\nvalue.", + "_curses.mousemask" => "Set the mouse events to be reported, and return a tuple (availmask, oldmask).\n\nReturn a tuple (availmask, oldmask). availmask indicates which of the\nspecified mouse events can be reported; on complete failure it returns 0.\noldmask is the previous value of the given window's mouse event mask.\nIf this function is never called, no mouse events are ever reported.", + "_curses.napms" => "Sleep for specified time.\n\nms\n Duration in milliseconds.", + "_curses.newpad" => "Create and return a pointer to a new pad data structure.\n\nnlines\n Height.\nncols\n Width.", + "_curses.newwin" => "newwin(nlines, ncols, [begin_y=0, begin_x=0])\nReturn a new window.\n\n nlines\n Height.\n ncols\n Width.\n begin_y\n Top side y-coordinate.\n begin_x\n Left side x-coordinate.\n\nBy default, the window will extend from the specified position to the lower\nright corner of the screen.", + "_curses.nl" => "Enter newline mode.\n\n flag\n If false, the effect is the same as calling nonl().\n\nThis mode translates the return key into newline on input, and translates\nnewline into return and line-feed on output. Newline mode is initially on.", + "_curses.nocbreak" => "Leave cbreak mode.\n\nReturn to normal \"cooked\" mode with line buffering.", + "_curses.noecho" => "Leave echo mode.\n\nEchoing of input characters is turned off.", + "_curses.nonl" => "Leave newline mode.\n\nDisable translation of return into newline on input, and disable low-level\ntranslation of newline into newline/return on output.", + "_curses.noqiflush" => "Disable queue flushing.\n\nWhen queue flushing is disabled, normal flush of input and output queues\nassociated with the INTR, QUIT and SUSP characters will not be done.", + "_curses.noraw" => "Leave raw mode.\n\nReturn to normal \"cooked\" mode with line buffering.", + "_curses.pair_content" => "Return a tuple (fg, bg) containing the colors for the requested color pair.\n\npair_number\n The number of the color pair (0 - (COLOR_PAIRS-1)).", + "_curses.pair_number" => "Return the number of the color-pair set by the specified attribute value.\n\ncolor_pair() is the counterpart to this function.", + "_curses.putp" => "Emit the value of a specified terminfo capability for the current terminal.\n\nNote that the output of putp() always goes to standard output.", + "_curses.qiflush" => "Enable queue flushing.\n\n flag\n If false, the effect is the same as calling noqiflush().\n\nIf queue flushing is enabled, all output in the display driver queue\nwill be flushed when the INTR, QUIT and SUSP characters are read.", + "_curses.raw" => "Enter raw mode.\n\n flag\n If false, the effect is the same as calling noraw().\n\nIn raw mode, normal line buffering and processing of interrupt, quit,\nsuspend, and flow control keys are turned off; characters are presented to\ncurses input functions one by one.", + "_curses.reset_prog_mode" => "Restore the terminal to \"program\" mode, as previously saved by def_prog_mode().", + "_curses.reset_shell_mode" => "Restore the terminal to \"shell\" mode, as previously saved by def_shell_mode().", + "_curses.resetty" => "Restore terminal mode.", + "_curses.resize_term" => "Backend function used by resizeterm(), performing most of the work.\n\n nlines\n Height.\n ncols\n Width.\n\nWhen resizing the windows, resize_term() blank-fills the areas that are\nextended. The calling application should fill in these areas with appropriate\ndata. The resize_term() function attempts to resize all windows. However,\ndue to the calling convention of pads, it is not possible to resize these\nwithout additional interaction with the application.", + "_curses.resizeterm" => "Resize the standard and current windows to the specified dimensions.\n\n nlines\n Height.\n ncols\n Width.\n\nAdjusts other bookkeeping data used by the curses library that record the\nwindow dimensions (in particular the SIGWINCH handler).", + "_curses.savetty" => "Save terminal mode.", + "_curses.set_escdelay" => "Sets the curses ESCDELAY setting.\n\n ms\n length of the delay in milliseconds.\n\nSets the number of milliseconds to wait after reading an escape character,\nto distinguish between an individual escape character entered on the\nkeyboard from escape sequences sent by cursor and function keys.", + "_curses.set_tabsize" => "Sets the curses TABSIZE setting.\n\n size\n rendered cell width of a tab character.\n\nSets the number of columns used by the curses library when converting a tab\ncharacter to spaces as it adds the tab to a window.", + "_curses.setsyx" => "Set the virtual screen cursor.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n\nIf y and x are both -1, then leaveok is set.", + "_curses.setupterm" => "Initialize the terminal.\n\nterm\n Terminal name.\n If omitted, the value of the TERM environment variable will be used.\nfd\n File descriptor to which any initialization sequences will be sent.\n If not supplied, the file descriptor for sys.stdout will be used.", + "_curses.start_color" => "Initializes eight basic colors and global variables COLORS and COLOR_PAIRS.\n\nMust be called if the programmer wants to use colors, and before any other\ncolor manipulation routine is called. It is good practice to call this\nroutine right after initscr().\n\nIt also restores the colors on the terminal to the values they had when the\nterminal was just turned on.", + "_curses.termattrs" => "Return a logical OR of all video attributes supported by the terminal.", + "_curses.termname" => "Return the value of the environment variable TERM, truncated to 14 characters.", + "_curses.tigetflag" => "Return the value of the Boolean capability.\n\n capname\n The terminfo capability name.\n\nThe value -1 is returned if capname is not a Boolean capability, or 0 if\nit is canceled or absent from the terminal description.", + "_curses.tigetnum" => "Return the value of the numeric capability.\n\n capname\n The terminfo capability name.\n\nThe value -2 is returned if capname is not a numeric capability, or -1 if\nit is canceled or absent from the terminal description.", + "_curses.tigetstr" => "Return the value of the string capability.\n\n capname\n The terminfo capability name.\n\nNone is returned if capname is not a string capability, or is canceled or\nabsent from the terminal description.", + "_curses.tparm" => "Instantiate the specified byte string with the supplied parameters.\n\nstr\n Parameterized byte string obtained from the terminfo database.", + "_curses.typeahead" => "Specify that the file descriptor fd be used for typeahead checking.\n\n fd\n File descriptor.\n\nIf fd is -1, then no typeahead checking is done.", + "_curses.unctrl" => "Return a string which is a printable representation of the character ch.\n\nControl characters are displayed as a caret followed by the character,\nfor example as ^C. Printing characters are left as they are.", + "_curses.unget_wch" => "Push ch so the next get_wch() will return it.", + "_curses.ungetch" => "Push ch so the next getch() will return it.", + "_curses.ungetmouse" => "Push a KEY_MOUSE event onto the input queue.\n\nThe following getmouse() will return the given state data.", + "_curses.use_default_colors" => "Allow use of default values for colors on terminals supporting this feature.\n\nUse this to support transparency in your application. The default color\nis assigned to the color number -1.", + "_curses.use_env" => "Use environment variables LINES and COLUMNS.\n\nIf used, this function should be called before initscr() or newterm() are\ncalled.\n\nWhen flag is False, the values of lines and columns specified in the terminfo\ndatabase will be used, even if environment variables LINES and COLUMNS (used\nby default) are set, or if curses is running in a window (in which case\ndefault behavior would be to use the window size if LINES and COLUMNS are\nnot set).", + "_curses.window.__delattr__" => "Implement delattr(self, name).", + "_curses.window.__eq__" => "Return self==value.", + "_curses.window.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_curses.window.__ge__" => "Return self>=value.", + "_curses.window.__getattribute__" => "Return getattr(self, name).", + "_curses.window.__getstate__" => "Helper for pickle.", + "_curses.window.__gt__" => "Return self>value.", + "_curses.window.__hash__" => "Return hash(self).", + "_curses.window.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_curses.window.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_curses.window.__le__" => "Return self<=value.", + "_curses.window.__lt__" => "Return self "Return self!=value.", + "_curses.window.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_curses.window.__reduce__" => "Helper for pickle.", + "_curses.window.__reduce_ex__" => "Helper for pickle.", + "_curses.window.__repr__" => "Return repr(self).", + "_curses.window.__setattr__" => "Implement setattr(self, name, value).", + "_curses.window.__sizeof__" => "Size of object in memory, in bytes.", + "_curses.window.__str__" => "Return str(self).", + "_curses.window.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_curses.window.addch" => "addch([y, x,] ch, [attr=_curses.A_NORMAL])\nPaint the character.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n ch\n Character to add.\n attr\n Attributes for the character.\n\nPaint character ch at (y, x) with attributes attr,\noverwriting any character previously painted at that location.\nBy default, the character position and attributes are the\ncurrent settings for the window object.", + "_curses.window.addnstr" => "addnstr([y, x,] str, n, [attr])\nPaint at most n characters of the string.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n str\n String to add.\n n\n Maximal number of characters.\n attr\n Attributes for characters.\n\nPaint at most n characters of the string str at (y, x) with\nattributes attr, overwriting anything previously on the display.\nBy default, the character position and attributes are the\ncurrent settings for the window object.", + "_curses.window.addstr" => "addstr([y, x,] str, [attr])\nPaint the string.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n str\n String to add.\n attr\n Attributes for characters.\n\nPaint the string str at (y, x) with attributes attr,\noverwriting anything previously on the display.\nBy default, the character position and attributes are the\ncurrent settings for the window object.", + "_curses.window.attroff" => "Remove attribute attr from the \"background\" set.", + "_curses.window.attron" => "Add attribute attr from the \"background\" set.", + "_curses.window.attrset" => "Set the \"background\" set of attributes.", + "_curses.window.bkgd" => "Set the background property of the window.\n\nch\n Background character.\nattr\n Background attributes.", + "_curses.window.bkgdset" => "Set the window's background.\n\nch\n Background character.\nattr\n Background attributes.", + "_curses.window.border" => "Draw a border around the edges of the window.\n\n ls\n Left side.\n rs\n Right side.\n ts\n Top side.\n bs\n Bottom side.\n tl\n Upper-left corner.\n tr\n Upper-right corner.\n bl\n Bottom-left corner.\n br\n Bottom-right corner.\n\nEach parameter specifies the character to use for a specific part of the\nborder. The characters can be specified as integers or as one-character\nstrings. A 0 value for any parameter will cause the default character to be\nused for that parameter.", + "_curses.window.box" => "box([verch=0, horch=0])\nDraw a border around the edges of the window.\n\n verch\n Left and right side.\n horch\n Top and bottom side.\n\nSimilar to border(), but both ls and rs are verch and both ts and bs are\nhorch. The default corner characters are always used by this function.", + "_curses.window.delch" => "delch([y, x])\nDelete any character at (y, x).\n\n y\n Y-coordinate.\n x\n X-coordinate.", + "_curses.window.derwin" => "derwin([nlines=0, ncols=0,] begin_y, begin_x)\nCreate a sub-window (window-relative coordinates).\n\n nlines\n Height.\n ncols\n Width.\n begin_y\n Top side y-coordinate.\n begin_x\n Left side x-coordinate.\n\nderwin() is the same as calling subwin(), except that begin_y and begin_x\nare relative to the origin of the window, rather than relative to the entire\nscreen.", + "_curses.window.echochar" => "Add character ch with attribute attr, and refresh.\n\nch\n Character to add.\nattr\n Attributes for the character.", + "_curses.window.enclose" => "Return True if the screen-relative coordinates are enclosed by the window.\n\ny\n Y-coordinate.\nx\n X-coordinate.", + "_curses.window.encoding" => "the typecode character used to create the array", + "_curses.window.get_wch" => "get_wch([y, x])\nGet a wide character from terminal keyboard.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n\nReturn a character for most keys, or an integer for function keys,\nkeypad keys, and other special keys.", + "_curses.window.getbkgd" => "Return the window's current background character/attribute pair.", + "_curses.window.getch" => "getch([y, x])\nGet a character code from terminal keyboard.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n\nThe integer returned does not have to be in ASCII range: function keys,\nkeypad keys and so on return numbers higher than 256. In no-delay mode, -1\nis returned if there is no input, else getch() waits until a key is pressed.", + "_curses.window.getkey" => "getkey([y, x])\nGet a character (string) from terminal keyboard.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n\nReturning a string instead of an integer, as getch() does. Function keys,\nkeypad keys and other special keys return a multibyte string containing the\nkey name. In no-delay mode, an exception is raised if there is no input.", + "_curses.window.hline" => "hline([y, x,] ch, n, [attr=_curses.A_NORMAL])\nDisplay a horizontal line.\n\n y\n Starting Y-coordinate.\n x\n Starting X-coordinate.\n ch\n Character to draw.\n n\n Line length.\n attr\n Attributes for the characters.", + "_curses.window.inch" => "inch([y, x])\nReturn the character at the given position in the window.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n\nThe bottom 8 bits are the character proper, and upper bits are the attributes.", + "_curses.window.insch" => "insch([y, x,] ch, [attr=_curses.A_NORMAL])\nInsert a character before the current or specified position.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n ch\n Character to insert.\n attr\n Attributes for the character.\n\nAll characters to the right of the cursor are shifted one position right, with\nthe rightmost characters on the line being lost.", + "_curses.window.insnstr" => "insnstr([y, x,] str, n, [attr])\nInsert at most n characters of the string.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n str\n String to insert.\n n\n Maximal number of characters.\n attr\n Attributes for characters.\n\nInsert a character string (as many characters as will fit on the line)\nbefore the character under the cursor, up to n characters. If n is zero\nor negative, the entire string is inserted. All characters to the right\nof the cursor are shifted right, with the rightmost characters on the line\nbeing lost. The cursor position does not change (after moving to y, x, if\nspecified).", + "_curses.window.insstr" => "insstr([y, x,] str, [attr])\nInsert the string before the current or specified position.\n\n y\n Y-coordinate.\n x\n X-coordinate.\n str\n String to insert.\n attr\n Attributes for characters.\n\nInsert a character string (as many characters as will fit on the line)\nbefore the character under the cursor. All characters to the right of\nthe cursor are shifted right, with the rightmost characters on the line\nbeing lost. The cursor position does not change (after moving to y, x,\nif specified).", + "_curses.window.is_linetouched" => "Return True if the specified line was modified, otherwise return False.\n\n line\n Line number.\n\nRaise a curses.error exception if line is not valid for the given window.", + "_curses.window.noutrefresh" => "noutrefresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])\nMark for refresh but wait.\n\nThis function updates the data structure representing the desired state of the\nwindow, but does not force an update of the physical screen. To accomplish\nthat, call doupdate().", + "_curses.window.overlay" => "overlay(destwin, [sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol])\nOverlay the window on top of destwin.\n\nThe windows need not be the same size, only the overlapping region is copied.\nThis copy is non-destructive, which means that the current background\ncharacter does not overwrite the old contents of destwin.\n\nTo get fine-grained control over the copied region, the second form of\noverlay() can be used. sminrow and smincol are the upper-left coordinates\nof the source window, and the other variables mark a rectangle in the\ndestination window.", + "_curses.window.overwrite" => "overwrite(destwin, [sminrow, smincol, dminrow, dmincol, dmaxrow,\n dmaxcol])\nOverwrite the window on top of destwin.\n\nThe windows need not be the same size, in which case only the overlapping\nregion is copied. This copy is destructive, which means that the current\nbackground character overwrites the old contents of destwin.\n\nTo get fine-grained control over the copied region, the second form of\noverwrite() can be used. sminrow and smincol are the upper-left coordinates\nof the source window, the other variables mark a rectangle in the destination\nwindow.", + "_curses.window.putwin" => "Write all data associated with the window into the provided file object.\n\nThis information can be later retrieved using the getwin() function.", + "_curses.window.redrawln" => "Mark the specified lines corrupted.\n\n beg\n Starting line number.\n num\n The number of lines.\n\nThey should be completely redrawn on the next refresh() call.", + "_curses.window.refresh" => "refresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])\nUpdate the display immediately.\n\nSynchronize actual screen with previous drawing/deleting methods.\nThe 6 optional arguments can only be specified when the window is a pad\ncreated with newpad(). The additional parameters are needed to indicate\nwhat part of the pad and screen are involved. pminrow and pmincol specify\nthe upper left-hand corner of the rectangle to be displayed in the pad.\nsminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to\nbe displayed on the screen. The lower right-hand corner of the rectangle to\nbe displayed in the pad is calculated from the screen coordinates, since the\nrectangles must be the same size. Both rectangles must be entirely contained\nwithin their respective structures. Negative values of pminrow, pmincol,\nsminrow, or smincol are treated as if they were zero.", + "_curses.window.scroll" => "scroll([lines=1])\nScroll the screen or scrolling region.\n\n lines\n Number of lines to scroll.\n\nScroll upward if the argument is positive and downward if it is negative.", + "_curses.window.setscrreg" => "Define a software scrolling region.\n\n top\n First line number.\n bottom\n Last line number.\n\nAll scrolling actions will take place in this region.", + "_curses.window.subpad" => "subwin([nlines=0, ncols=0,] begin_y, begin_x)\nCreate a sub-window (screen-relative coordinates).\n\n nlines\n Height.\n ncols\n Width.\n begin_y\n Top side y-coordinate.\n begin_x\n Left side x-coordinate.\n\nBy default, the sub-window will extend from the specified position to the\nlower right corner of the window.", + "_curses.window.subwin" => "subwin([nlines=0, ncols=0,] begin_y, begin_x)\nCreate a sub-window (screen-relative coordinates).\n\n nlines\n Height.\n ncols\n Width.\n begin_y\n Top side y-coordinate.\n begin_x\n Left side x-coordinate.\n\nBy default, the sub-window will extend from the specified position to the\nlower right corner of the window.", + "_curses.window.touchline" => "touchline(start, count, [changed=True])\nPretend count lines have been changed, starting with line start.\n\nIf changed is supplied, it specifies whether the affected lines are marked\nas having been changed (changed=True) or unchanged (changed=False).", + "_curses.window.vline" => "vline([y, x,] ch, n, [attr=_curses.A_NORMAL])\nDisplay a vertical line.\n\n y\n Starting Y-coordinate.\n x\n Starting X-coordinate.\n ch\n Character to draw.\n n\n Line length.\n attr\n Attributes for the character.", + "_curses_panel.bottom_panel" => "Return the bottom panel in the panel stack.", + "_curses_panel.error.__cause__" => "exception cause", + "_curses_panel.error.__context__" => "exception context", + "_curses_panel.error.__delattr__" => "Implement delattr(self, name).", + "_curses_panel.error.__eq__" => "Return self==value.", + "_curses_panel.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_curses_panel.error.__ge__" => "Return self>=value.", + "_curses_panel.error.__getattribute__" => "Return getattr(self, name).", + "_curses_panel.error.__getstate__" => "Helper for pickle.", + "_curses_panel.error.__gt__" => "Return self>value.", + "_curses_panel.error.__hash__" => "Return hash(self).", + "_curses_panel.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_curses_panel.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_curses_panel.error.__le__" => "Return self<=value.", + "_curses_panel.error.__lt__" => "Return self "Return self!=value.", + "_curses_panel.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_curses_panel.error.__reduce_ex__" => "Helper for pickle.", + "_curses_panel.error.__repr__" => "Return repr(self).", + "_curses_panel.error.__setattr__" => "Implement setattr(self, name, value).", + "_curses_panel.error.__sizeof__" => "Size of object in memory, in bytes.", + "_curses_panel.error.__str__" => "Return str(self).", + "_curses_panel.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_curses_panel.error.__weakref__" => "list of weak references to the object", + "_curses_panel.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_curses_panel.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_curses_panel.new_panel" => "Return a panel object, associating it with the given window win.", + "_curses_panel.panel.__delattr__" => "Implement delattr(self, name).", + "_curses_panel.panel.__eq__" => "Return self==value.", + "_curses_panel.panel.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_curses_panel.panel.__ge__" => "Return self>=value.", + "_curses_panel.panel.__getattribute__" => "Return getattr(self, name).", + "_curses_panel.panel.__getstate__" => "Helper for pickle.", + "_curses_panel.panel.__gt__" => "Return self>value.", + "_curses_panel.panel.__hash__" => "Return hash(self).", + "_curses_panel.panel.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_curses_panel.panel.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_curses_panel.panel.__le__" => "Return self<=value.", + "_curses_panel.panel.__lt__" => "Return self "Return self!=value.", + "_curses_panel.panel.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_curses_panel.panel.__reduce__" => "Helper for pickle.", + "_curses_panel.panel.__reduce_ex__" => "Helper for pickle.", + "_curses_panel.panel.__repr__" => "Return repr(self).", + "_curses_panel.panel.__setattr__" => "Implement setattr(self, name, value).", + "_curses_panel.panel.__sizeof__" => "Size of object in memory, in bytes.", + "_curses_panel.panel.__str__" => "Return str(self).", + "_curses_panel.panel.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_curses_panel.panel.above" => "Return the panel above the current panel.", + "_curses_panel.panel.below" => "Return the panel below the current panel.", + "_curses_panel.panel.bottom" => "Push the panel to the bottom of the stack.", + "_curses_panel.panel.hidden" => "Return True if the panel is hidden (not visible), False otherwise.", + "_curses_panel.panel.hide" => "Hide the panel.\n\nThis does not delete the object, it just makes the window on screen invisible.", + "_curses_panel.panel.move" => "Move the panel to the screen coordinates (y, x).", + "_curses_panel.panel.replace" => "Change the window associated with the panel to the window win.", + "_curses_panel.panel.set_userptr" => "Set the panel's user pointer to obj.", + "_curses_panel.panel.show" => "Display the panel (which might have been hidden).", + "_curses_panel.panel.top" => "Push panel to the top of the stack.", + "_curses_panel.panel.userptr" => "Return the user pointer for the panel.", + "_curses_panel.panel.window" => "Return the window object associated with the panel.", + "_curses_panel.top_panel" => "Return the top panel in the panel stack.", + "_curses_panel.update_panels" => "Updates the virtual screen after changes in the panel stack.\n\nThis does not call curses.doupdate(), so you'll have to do this yourself.", + "_datetime" => "Fast implementation of the datetime type.", + "_dbm.error.__cause__" => "exception cause", + "_dbm.error.__context__" => "exception context", + "_dbm.error.__delattr__" => "Implement delattr(self, name).", + "_dbm.error.__eq__" => "Return self==value.", + "_dbm.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_dbm.error.__ge__" => "Return self>=value.", + "_dbm.error.__getattribute__" => "Return getattr(self, name).", + "_dbm.error.__getstate__" => "Helper for pickle.", + "_dbm.error.__gt__" => "Return self>value.", + "_dbm.error.__hash__" => "Return hash(self).", + "_dbm.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_dbm.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_dbm.error.__le__" => "Return self<=value.", + "_dbm.error.__lt__" => "Return self "Return self!=value.", + "_dbm.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_dbm.error.__reduce_ex__" => "Helper for pickle.", + "_dbm.error.__repr__" => "Return repr(self).", + "_dbm.error.__setattr__" => "Implement setattr(self, name, value).", + "_dbm.error.__sizeof__" => "Size of object in memory, in bytes.", + "_dbm.error.__str__" => "Return str(self).", + "_dbm.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_dbm.error.__weakref__" => "list of weak references to the object", + "_dbm.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_dbm.error.errno" => "POSIX exception code", + "_dbm.error.filename" => "exception filename", + "_dbm.error.filename2" => "second exception filename", + "_dbm.error.strerror" => "exception strerror", + "_dbm.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_dbm.open" => "Return a database object.\n\nfilename\n The filename to open.\nflags\n How to open the file. \"r\" for reading, \"w\" for writing, etc.\nmode\n If creating a new file, the mode bits for the new file\n (e.g. os.O_RDWR).", + "_decimal" => "C decimal arithmetic module", + "_decimal.getcontext" => "Get the current default context.", + "_decimal.localcontext" => "Return a context manager that will set the default context to a copy of ctx\non entry to the with-statement and restore the previous default context when\nexiting the with-statement. If no context is specified, a copy of the current\ndefault context is used.", + "_decimal.setcontext" => "Set a new default context.", + "_elementtree._set_factories" => "Change the factories used to create comments and processing instructions.\n\nFor internal use only.", + "_functools" => "Tools that operate on functions.", + "_functools.cmp_to_key" => "Convert a cmp= function into a key= function.\n\nmycmp\n Function that compares two objects.", + "_functools.reduce" => "reduce(function, iterable[, initial], /) -> value\n\nApply a function of two arguments cumulatively to the items of an iterable, from left to right.\n\nThis effectively reduces the iterable to a single value. If initial is present,\nit is placed before the items of the iterable in the calculation, and serves as\na default when the iterable is empty.\n\nFor example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])\ncalculates ((((1 + 2) + 3) + 4) + 5).", + "_gdbm" => "This module provides an interface to the GNU DBM (GDBM) library.\n\nThis module is quite similar to the dbm module, but uses GDBM instead to\nprovide some additional functionality. Please note that the file formats\ncreated by GDBM and dbm are incompatible.\n\nGDBM objects behave like mappings (dictionaries), except that keys and\nvalues are always immutable bytes-like objects or strings. Printing\na GDBM object doesn't print the keys and values, and the items() and\nvalues() methods are not supported.", + "_gdbm.error.__cause__" => "exception cause", + "_gdbm.error.__context__" => "exception context", + "_gdbm.error.__delattr__" => "Implement delattr(self, name).", + "_gdbm.error.__eq__" => "Return self==value.", + "_gdbm.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_gdbm.error.__ge__" => "Return self>=value.", + "_gdbm.error.__getattribute__" => "Return getattr(self, name).", + "_gdbm.error.__getstate__" => "Helper for pickle.", + "_gdbm.error.__gt__" => "Return self>value.", + "_gdbm.error.__hash__" => "Return hash(self).", + "_gdbm.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_gdbm.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_gdbm.error.__le__" => "Return self<=value.", + "_gdbm.error.__lt__" => "Return self "Return self!=value.", + "_gdbm.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_gdbm.error.__reduce_ex__" => "Helper for pickle.", + "_gdbm.error.__repr__" => "Return repr(self).", + "_gdbm.error.__setattr__" => "Implement setattr(self, name, value).", + "_gdbm.error.__sizeof__" => "Size of object in memory, in bytes.", + "_gdbm.error.__str__" => "Return str(self).", + "_gdbm.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_gdbm.error.__weakref__" => "list of weak references to the object", + "_gdbm.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_gdbm.error.errno" => "POSIX exception code", + "_gdbm.error.filename" => "exception filename", + "_gdbm.error.filename2" => "second exception filename", + "_gdbm.error.strerror" => "exception strerror", + "_gdbm.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_gdbm.open" => "Open a dbm database and return a dbm object.\n\nThe filename argument is the name of the database file.\n\nThe optional flags argument can be 'r' (to open an existing database\nfor reading only -- default), 'w' (to open an existing database for\nreading and writing), 'c' (which creates the database if it doesn't\nexist), or 'n' (which always creates a new empty database).\n\nSome versions of gdbm support additional flags which must be\nappended to one of the flags described above. The module constant\n'open_flags' is a string of valid additional flags. The 'f' flag\nopens the database in fast mode; altered data will not automatically\nbe written to the disk after every change. This results in faster\nwrites to the database, but may result in an inconsistent database\nif the program crashes while the database is still open. Use the\nsync() method to force any unwritten data to be written to the disk.\nThe 's' flag causes all database operations to be synchronized to\ndisk. The 'u' flag disables locking of the database file.\n\nThe optional mode argument is the Unix mode of the file, used only\nwhen the database has to be created. It defaults to octal 0o666.", + "_hashlib" => "OpenSSL interface for hashlib module", + "_hashlib.HASH" => "A hash is an object used to calculate a checksum of a string of information.\n\nMethods:\n\nupdate() -- updates the current digest with an additional string\ndigest() -- return the current digest value\nhexdigest() -- return the current digest as a string of hexadecimal digits\ncopy() -- return a copy of the current hash object\n\nAttributes:\n\nname -- the hash algorithm being used by this object\ndigest_size -- number of bytes in this hashes output", + "_hashlib.HASH.__delattr__" => "Implement delattr(self, name).", + "_hashlib.HASH.__eq__" => "Return self==value.", + "_hashlib.HASH.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_hashlib.HASH.__ge__" => "Return self>=value.", + "_hashlib.HASH.__getattribute__" => "Return getattr(self, name).", + "_hashlib.HASH.__getstate__" => "Helper for pickle.", + "_hashlib.HASH.__gt__" => "Return self>value.", + "_hashlib.HASH.__hash__" => "Return hash(self).", + "_hashlib.HASH.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_hashlib.HASH.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_hashlib.HASH.__le__" => "Return self<=value.", + "_hashlib.HASH.__lt__" => "Return self "Return self!=value.", + "_hashlib.HASH.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_hashlib.HASH.__reduce__" => "Helper for pickle.", + "_hashlib.HASH.__reduce_ex__" => "Helper for pickle.", + "_hashlib.HASH.__repr__" => "Return repr(self).", + "_hashlib.HASH.__setattr__" => "Implement setattr(self, name, value).", + "_hashlib.HASH.__sizeof__" => "Size of object in memory, in bytes.", + "_hashlib.HASH.__str__" => "Return str(self).", + "_hashlib.HASH.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_hashlib.HASH.copy" => "Return a copy of the hash object.", + "_hashlib.HASH.digest" => "Return the digest value as a bytes object.", + "_hashlib.HASH.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_hashlib.HASH.update" => "Update this hash object's state with the provided string.", + "_hashlib.HASHXOF" => "A hash is an object used to calculate a checksum of a string of information.\n\nMethods:\n\nupdate() -- updates the current digest with an additional string\ndigest(length) -- return the current digest value\nhexdigest(length) -- return the current digest as a string of hexadecimal digits\ncopy() -- return a copy of the current hash object\n\nAttributes:\n\nname -- the hash algorithm being used by this object\ndigest_size -- number of bytes in this hashes output", + "_hashlib.HASHXOF.__delattr__" => "Implement delattr(self, name).", + "_hashlib.HASHXOF.__eq__" => "Return self==value.", + "_hashlib.HASHXOF.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_hashlib.HASHXOF.__ge__" => "Return self>=value.", + "_hashlib.HASHXOF.__getattribute__" => "Return getattr(self, name).", + "_hashlib.HASHXOF.__getstate__" => "Helper for pickle.", + "_hashlib.HASHXOF.__gt__" => "Return self>value.", + "_hashlib.HASHXOF.__hash__" => "Return hash(self).", + "_hashlib.HASHXOF.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_hashlib.HASHXOF.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_hashlib.HASHXOF.__le__" => "Return self<=value.", + "_hashlib.HASHXOF.__lt__" => "Return self "Return self!=value.", + "_hashlib.HASHXOF.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_hashlib.HASHXOF.__reduce__" => "Helper for pickle.", + "_hashlib.HASHXOF.__reduce_ex__" => "Helper for pickle.", + "_hashlib.HASHXOF.__repr__" => "Return repr(self).", + "_hashlib.HASHXOF.__setattr__" => "Implement setattr(self, name, value).", + "_hashlib.HASHXOF.__sizeof__" => "Size of object in memory, in bytes.", + "_hashlib.HASHXOF.__str__" => "Return str(self).", + "_hashlib.HASHXOF.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_hashlib.HASHXOF.copy" => "Return a copy of the hash object.", + "_hashlib.HASHXOF.digest" => "Return the digest value as a bytes object.", + "_hashlib.HASHXOF.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_hashlib.HASHXOF.update" => "Update this hash object's state with the provided string.", + "_hashlib.HMAC" => "The object used to calculate HMAC of a message.\n\nMethods:\n\nupdate() -- updates the current digest with an additional string\ndigest() -- return the current digest value\nhexdigest() -- return the current digest as a string of hexadecimal digits\ncopy() -- return a copy of the current hash object\n\nAttributes:\n\nname -- the name, including the hash algorithm used by this object\ndigest_size -- number of bytes in digest() output", + "_hashlib.HMAC.__delattr__" => "Implement delattr(self, name).", + "_hashlib.HMAC.__eq__" => "Return self==value.", + "_hashlib.HMAC.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_hashlib.HMAC.__ge__" => "Return self>=value.", + "_hashlib.HMAC.__getattribute__" => "Return getattr(self, name).", + "_hashlib.HMAC.__getstate__" => "Helper for pickle.", + "_hashlib.HMAC.__gt__" => "Return self>value.", + "_hashlib.HMAC.__hash__" => "Return hash(self).", + "_hashlib.HMAC.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_hashlib.HMAC.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_hashlib.HMAC.__le__" => "Return self<=value.", + "_hashlib.HMAC.__lt__" => "Return self "Return self!=value.", + "_hashlib.HMAC.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_hashlib.HMAC.__reduce__" => "Helper for pickle.", + "_hashlib.HMAC.__reduce_ex__" => "Helper for pickle.", + "_hashlib.HMAC.__repr__" => "Return repr(self).", + "_hashlib.HMAC.__setattr__" => "Implement setattr(self, name, value).", + "_hashlib.HMAC.__sizeof__" => "Size of object in memory, in bytes.", + "_hashlib.HMAC.__str__" => "Return str(self).", + "_hashlib.HMAC.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_hashlib.HMAC.copy" => "Return a copy (\"clone\") of the HMAC object.", + "_hashlib.HMAC.digest" => "Return the digest of the bytes passed to the update() method so far.", + "_hashlib.HMAC.hexdigest" => "Return hexadecimal digest of the bytes passed to the update() method so far.\n\nThis may be used to exchange the value safely in email or other non-binary\nenvironments.", + "_hashlib.HMAC.update" => "Update the HMAC object with msg.", + "_hashlib.UnsupportedDigestmodError.__cause__" => "exception cause", + "_hashlib.UnsupportedDigestmodError.__context__" => "exception context", + "_hashlib.UnsupportedDigestmodError.__delattr__" => "Implement delattr(self, name).", + "_hashlib.UnsupportedDigestmodError.__eq__" => "Return self==value.", + "_hashlib.UnsupportedDigestmodError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_hashlib.UnsupportedDigestmodError.__ge__" => "Return self>=value.", + "_hashlib.UnsupportedDigestmodError.__getattribute__" => "Return getattr(self, name).", + "_hashlib.UnsupportedDigestmodError.__getstate__" => "Helper for pickle.", + "_hashlib.UnsupportedDigestmodError.__gt__" => "Return self>value.", + "_hashlib.UnsupportedDigestmodError.__hash__" => "Return hash(self).", + "_hashlib.UnsupportedDigestmodError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_hashlib.UnsupportedDigestmodError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_hashlib.UnsupportedDigestmodError.__le__" => "Return self<=value.", + "_hashlib.UnsupportedDigestmodError.__lt__" => "Return self "Return self!=value.", + "_hashlib.UnsupportedDigestmodError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_hashlib.UnsupportedDigestmodError.__reduce_ex__" => "Helper for pickle.", + "_hashlib.UnsupportedDigestmodError.__repr__" => "Return repr(self).", + "_hashlib.UnsupportedDigestmodError.__setattr__" => "Implement setattr(self, name, value).", + "_hashlib.UnsupportedDigestmodError.__sizeof__" => "Size of object in memory, in bytes.", + "_hashlib.UnsupportedDigestmodError.__str__" => "Return str(self).", + "_hashlib.UnsupportedDigestmodError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_hashlib.UnsupportedDigestmodError.__weakref__" => "list of weak references to the object", + "_hashlib.UnsupportedDigestmodError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_hashlib.UnsupportedDigestmodError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_hashlib.compare_digest" => "Return 'a == b'.\n\nThis function uses an approach designed to prevent\ntiming analysis, making it appropriate for cryptography.\n\na and b must both be of the same type: either str (ASCII only),\nor any bytes-like object.\n\nNote: If a and b are of different lengths, or if an error occurs,\na timing attack could theoretically reveal information about the\ntypes and lengths of a and b--but not their values.", + "_hashlib.get_fips_mode" => "Determine the OpenSSL FIPS mode of operation.\n\nFor OpenSSL 3.0.0 and newer it returns the state of the default provider\nin the default OSSL context. It's not quite the same as FIPS_mode() but good\nenough for unittests.\n\nEffectively any non-zero return value indicates FIPS mode;\nvalues other than 1 may have additional significance.", + "_hashlib.hmac_digest" => "Single-shot HMAC.", + "_hashlib.hmac_new" => "Return a new hmac object.", + "_hashlib.new" => "Return a new hash object using the named algorithm.\n\nAn optional string argument may be provided and will be\nautomatically hashed.\n\nThe MD5 and SHA1 algorithms are always supported.", + "_hashlib.openssl_md5" => "Returns a md5 hash object; optionally initialized with a string", + "_hashlib.openssl_sha1" => "Returns a sha1 hash object; optionally initialized with a string", + "_hashlib.openssl_sha224" => "Returns a sha224 hash object; optionally initialized with a string", + "_hashlib.openssl_sha256" => "Returns a sha256 hash object; optionally initialized with a string", + "_hashlib.openssl_sha384" => "Returns a sha384 hash object; optionally initialized with a string", + "_hashlib.openssl_sha3_224" => "Returns a sha3-224 hash object; optionally initialized with a string", + "_hashlib.openssl_sha3_256" => "Returns a sha3-256 hash object; optionally initialized with a string", + "_hashlib.openssl_sha3_384" => "Returns a sha3-384 hash object; optionally initialized with a string", + "_hashlib.openssl_sha3_512" => "Returns a sha3-512 hash object; optionally initialized with a string", + "_hashlib.openssl_sha512" => "Returns a sha512 hash object; optionally initialized with a string", + "_hashlib.openssl_shake_128" => "Returns a shake-128 variable hash object; optionally initialized with a string", + "_hashlib.openssl_shake_256" => "Returns a shake-256 variable hash object; optionally initialized with a string", + "_hashlib.pbkdf2_hmac" => "Password based key derivation function 2 (PKCS #5 v2.0) with HMAC as pseudorandom function.", + "_hashlib.scrypt" => "scrypt password-based key derivation function.", + "_heapq" => "Heap queue algorithm (a.k.a. priority queue).\n\nHeaps are arrays for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for\nall k, counting elements from 0. For the sake of comparison,\nnon-existing elements are considered to be infinite. The interesting\nproperty of a heap is that a[0] is always its smallest element.\n\nUsage:\n\nheap = [] # creates an empty heap\nheappush(heap, item) # pushes a new item on the heap\nitem = heappop(heap) # pops the smallest item from the heap\nitem = heap[0] # smallest item on the heap without popping it\nheapify(x) # transforms list into a heap, in-place, in linear time\nitem = heapreplace(heap, item) # pops and returns smallest item, and adds\n # new item; the heap size is unchanged\n\nOur API differs from textbook heap algorithms as follows:\n\n- We use 0-based indexing. This makes the relationship between the\n index for a node and the indexes for its children slightly less\n obvious, but is more suitable since Python uses 0-based indexing.\n\n- Our heappop() method returns the smallest item, not the largest.\n\nThese two make it possible to view the heap as a regular Python list\nwithout surprises: heap[0] is the smallest item, and heap.sort()\nmaintains the heap invariant!", + "_heapq._heapify_max" => "Maxheap variant of heapify.", + "_heapq._heappop_max" => "Maxheap variant of heappop.", + "_heapq._heapreplace_max" => "Maxheap variant of heapreplace.", + "_heapq.heapify" => "Transform list into a heap, in-place, in O(len(heap)) time.", + "_heapq.heappop" => "Pop the smallest item off the heap, maintaining the heap invariant.", + "_heapq.heappush" => "Push item onto heap, maintaining the heap invariant.", + "_heapq.heappushpop" => "Push item on the heap, then pop and return the smallest item from the heap.\n\nThe combined action runs more efficiently than heappush() followed by\na separate call to heappop().", + "_heapq.heapreplace" => "Pop and return the current smallest value, and add the new item.\n\nThis is more efficient than heappop() followed by heappush(), and can be\nmore appropriate when using a fixed-size heap. Note that the value\nreturned may be larger than item! That constrains reasonable uses of\nthis routine unless written as part of a conditional replacement:\n\n if item > heap[0]:\n item = heapreplace(heap, item)", + "_imp" => "(Extremely) low-level import machinery bits as used by importlib.", + "_imp._fix_co_filename" => "Changes code.co_filename to specify the passed-in file path.\n\ncode\n Code object to change.\npath\n File path to use.", + "_imp._frozen_module_names" => "Returns the list of available frozen modules.", + "_imp._override_frozen_modules_for_tests" => "(internal-only) Override PyConfig.use_frozen_modules.\n\n(-1: \"off\", 1: \"on\", 0: no override)\nSee frozen_modules() in Lib/test/support/import_helper.py.", + "_imp._override_multi_interp_extensions_check" => "(internal-only) Override PyInterpreterConfig.check_multi_interp_extensions.\n\n(-1: \"never\", 1: \"always\", 0: no override)", + "_imp.acquire_lock" => "Acquires the interpreter's import lock for the current thread.\n\nThis lock should be used by import hooks to ensure thread-safety when importing\nmodules. On platforms without threads, this function does nothing.", + "_imp.create_builtin" => "Create an extension module.", + "_imp.create_dynamic" => "Create an extension module.", + "_imp.exec_builtin" => "Initialize a built-in module.", + "_imp.exec_dynamic" => "Initialize an extension module.", + "_imp.extension_suffixes" => "Returns the list of file suffixes used to identify extension modules.", + "_imp.find_frozen" => "Return info about the corresponding frozen module (if there is one) or None.\n\nThe returned info (a 2-tuple):\n\n * data the raw marshalled bytes\n * is_package whether or not it is a package\n * origname the originally frozen module's name, or None if not\n a stdlib module (this will usually be the same as\n the module's current name)", + "_imp.get_frozen_object" => "Create a code object for a frozen module.", + "_imp.init_frozen" => "Initializes a frozen module.", + "_imp.is_builtin" => "Returns True if the module name corresponds to a built-in module.", + "_imp.is_frozen" => "Returns True if the module name corresponds to a frozen module.", + "_imp.is_frozen_package" => "Returns True if the module name is of a frozen package.", + "_imp.lock_held" => "Return True if the import lock is currently held, else False.\n\nOn platforms without threads, return False.", + "_imp.release_lock" => "Release the interpreter's import lock.\n\nOn platforms without threads, this function does nothing.", + "_interpchannels" => "This module provides primitive operations to manage Python interpreters.\nThe 'interpreters' module provides a more convenient interface.", + "_interpchannels.ChannelClosedError.__cause__" => "exception cause", + "_interpchannels.ChannelClosedError.__context__" => "exception context", + "_interpchannels.ChannelClosedError.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelClosedError.__eq__" => "Return self==value.", + "_interpchannels.ChannelClosedError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelClosedError.__ge__" => "Return self>=value.", + "_interpchannels.ChannelClosedError.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelClosedError.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelClosedError.__gt__" => "Return self>value.", + "_interpchannels.ChannelClosedError.__hash__" => "Return hash(self).", + "_interpchannels.ChannelClosedError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelClosedError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelClosedError.__le__" => "Return self<=value.", + "_interpchannels.ChannelClosedError.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelClosedError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelClosedError.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelClosedError.__repr__" => "Return repr(self).", + "_interpchannels.ChannelClosedError.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelClosedError.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelClosedError.__str__" => "Return str(self).", + "_interpchannels.ChannelClosedError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelClosedError.__weakref__" => "list of weak references to the object", + "_interpchannels.ChannelClosedError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_interpchannels.ChannelClosedError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_interpchannels.ChannelEmptyError.__cause__" => "exception cause", + "_interpchannels.ChannelEmptyError.__context__" => "exception context", + "_interpchannels.ChannelEmptyError.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelEmptyError.__eq__" => "Return self==value.", + "_interpchannels.ChannelEmptyError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelEmptyError.__ge__" => "Return self>=value.", + "_interpchannels.ChannelEmptyError.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelEmptyError.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelEmptyError.__gt__" => "Return self>value.", + "_interpchannels.ChannelEmptyError.__hash__" => "Return hash(self).", + "_interpchannels.ChannelEmptyError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelEmptyError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelEmptyError.__le__" => "Return self<=value.", + "_interpchannels.ChannelEmptyError.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelEmptyError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelEmptyError.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelEmptyError.__repr__" => "Return repr(self).", + "_interpchannels.ChannelEmptyError.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelEmptyError.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelEmptyError.__str__" => "Return str(self).", + "_interpchannels.ChannelEmptyError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelEmptyError.__weakref__" => "list of weak references to the object", + "_interpchannels.ChannelEmptyError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_interpchannels.ChannelEmptyError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_interpchannels.ChannelError.__cause__" => "exception cause", + "_interpchannels.ChannelError.__context__" => "exception context", + "_interpchannels.ChannelError.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelError.__eq__" => "Return self==value.", + "_interpchannels.ChannelError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelError.__ge__" => "Return self>=value.", + "_interpchannels.ChannelError.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelError.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelError.__gt__" => "Return self>value.", + "_interpchannels.ChannelError.__hash__" => "Return hash(self).", + "_interpchannels.ChannelError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelError.__le__" => "Return self<=value.", + "_interpchannels.ChannelError.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelError.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelError.__repr__" => "Return repr(self).", + "_interpchannels.ChannelError.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelError.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelError.__str__" => "Return str(self).", + "_interpchannels.ChannelError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelError.__weakref__" => "list of weak references to the object", + "_interpchannels.ChannelError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_interpchannels.ChannelError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_interpchannels.ChannelID" => "A channel ID identifies a channel and may be used as an int.", + "_interpchannels.ChannelID.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelID.__eq__" => "Return self==value.", + "_interpchannels.ChannelID.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelID.__ge__" => "Return self>=value.", + "_interpchannels.ChannelID.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelID.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelID.__gt__" => "Return self>value.", + "_interpchannels.ChannelID.__hash__" => "Return hash(self).", + "_interpchannels.ChannelID.__index__" => "Return self converted to an integer, if self is suitable for use as an index into a list.", + "_interpchannels.ChannelID.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelID.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelID.__int__" => "int(self)", + "_interpchannels.ChannelID.__le__" => "Return self<=value.", + "_interpchannels.ChannelID.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelID.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelID.__reduce__" => "Helper for pickle.", + "_interpchannels.ChannelID.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelID.__repr__" => "Return repr(self).", + "_interpchannels.ChannelID.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelID.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelID.__str__" => "Return str(self).", + "_interpchannels.ChannelID.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelID.end" => "'send', 'recv', or 'both'", + "_interpchannels.ChannelID.recv" => "the 'recv' end of the channel", + "_interpchannels.ChannelID.send" => "the 'send' end of the channel", + "_interpchannels.ChannelInfo" => "ChannelInfo\n\nA named tuple of a channel's state.", + "_interpchannels.ChannelInfo.__add__" => "Return self+value.", + "_interpchannels.ChannelInfo.__class_getitem__" => "See PEP 585", + "_interpchannels.ChannelInfo.__contains__" => "Return bool(key in self).", + "_interpchannels.ChannelInfo.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelInfo.__eq__" => "Return self==value.", + "_interpchannels.ChannelInfo.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelInfo.__ge__" => "Return self>=value.", + "_interpchannels.ChannelInfo.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelInfo.__getitem__" => "Return self[key].", + "_interpchannels.ChannelInfo.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelInfo.__gt__" => "Return self>value.", + "_interpchannels.ChannelInfo.__hash__" => "Return hash(self).", + "_interpchannels.ChannelInfo.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelInfo.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelInfo.__iter__" => "Implement iter(self).", + "_interpchannels.ChannelInfo.__le__" => "Return self<=value.", + "_interpchannels.ChannelInfo.__len__" => "Return len(self).", + "_interpchannels.ChannelInfo.__lt__" => "Return self "Return self*value.", + "_interpchannels.ChannelInfo.__ne__" => "Return self!=value.", + "_interpchannels.ChannelInfo.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelInfo.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelInfo.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "_interpchannels.ChannelInfo.__repr__" => "Return repr(self).", + "_interpchannels.ChannelInfo.__rmul__" => "Return value*self.", + "_interpchannels.ChannelInfo.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelInfo.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelInfo.__str__" => "Return str(self).", + "_interpchannels.ChannelInfo.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelInfo.closed" => "both ends are closed", + "_interpchannels.ChannelInfo.closing" => "send is closed, recv is non-empty", + "_interpchannels.ChannelInfo.count" => "queued objects", + "_interpchannels.ChannelInfo.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "_interpchannels.ChannelInfo.num_interp_both" => "interpreters bound to both ends", + "_interpchannels.ChannelInfo.num_interp_both_recv_released" => "interpreters bound to both ends and released_from_the recv end", + "_interpchannels.ChannelInfo.num_interp_both_released" => "interpreters bound to both ends and released_from_both", + "_interpchannels.ChannelInfo.num_interp_both_send_released" => "interpreters bound to both ends and released_from_the send end", + "_interpchannels.ChannelInfo.num_interp_recv" => "interpreters bound to the send end", + "_interpchannels.ChannelInfo.num_interp_recv_released" => "interpreters bound to the send end and released", + "_interpchannels.ChannelInfo.num_interp_send" => "interpreters bound to the send end", + "_interpchannels.ChannelInfo.num_interp_send_released" => "interpreters bound to the send end and released", + "_interpchannels.ChannelInfo.open" => "both ends are open", + "_interpchannels.ChannelInfo.recv_associated" => "current interpreter is bound to the recv end", + "_interpchannels.ChannelInfo.recv_released" => "current interpreter *was* bound to the recv end", + "_interpchannels.ChannelInfo.send_associated" => "current interpreter is bound to the send end", + "_interpchannels.ChannelInfo.send_released" => "current interpreter *was* bound to the send end", + "_interpchannels.ChannelNotEmptyError.__cause__" => "exception cause", + "_interpchannels.ChannelNotEmptyError.__context__" => "exception context", + "_interpchannels.ChannelNotEmptyError.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelNotEmptyError.__eq__" => "Return self==value.", + "_interpchannels.ChannelNotEmptyError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelNotEmptyError.__ge__" => "Return self>=value.", + "_interpchannels.ChannelNotEmptyError.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelNotEmptyError.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelNotEmptyError.__gt__" => "Return self>value.", + "_interpchannels.ChannelNotEmptyError.__hash__" => "Return hash(self).", + "_interpchannels.ChannelNotEmptyError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelNotEmptyError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelNotEmptyError.__le__" => "Return self<=value.", + "_interpchannels.ChannelNotEmptyError.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelNotEmptyError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelNotEmptyError.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelNotEmptyError.__repr__" => "Return repr(self).", + "_interpchannels.ChannelNotEmptyError.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelNotEmptyError.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelNotEmptyError.__str__" => "Return str(self).", + "_interpchannels.ChannelNotEmptyError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelNotEmptyError.__weakref__" => "list of weak references to the object", + "_interpchannels.ChannelNotEmptyError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_interpchannels.ChannelNotEmptyError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_interpchannels.ChannelNotFoundError.__cause__" => "exception cause", + "_interpchannels.ChannelNotFoundError.__context__" => "exception context", + "_interpchannels.ChannelNotFoundError.__delattr__" => "Implement delattr(self, name).", + "_interpchannels.ChannelNotFoundError.__eq__" => "Return self==value.", + "_interpchannels.ChannelNotFoundError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpchannels.ChannelNotFoundError.__ge__" => "Return self>=value.", + "_interpchannels.ChannelNotFoundError.__getattribute__" => "Return getattr(self, name).", + "_interpchannels.ChannelNotFoundError.__getstate__" => "Helper for pickle.", + "_interpchannels.ChannelNotFoundError.__gt__" => "Return self>value.", + "_interpchannels.ChannelNotFoundError.__hash__" => "Return hash(self).", + "_interpchannels.ChannelNotFoundError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpchannels.ChannelNotFoundError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpchannels.ChannelNotFoundError.__le__" => "Return self<=value.", + "_interpchannels.ChannelNotFoundError.__lt__" => "Return self "Return self!=value.", + "_interpchannels.ChannelNotFoundError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpchannels.ChannelNotFoundError.__reduce_ex__" => "Helper for pickle.", + "_interpchannels.ChannelNotFoundError.__repr__" => "Return repr(self).", + "_interpchannels.ChannelNotFoundError.__setattr__" => "Implement setattr(self, name, value).", + "_interpchannels.ChannelNotFoundError.__sizeof__" => "Size of object in memory, in bytes.", + "_interpchannels.ChannelNotFoundError.__str__" => "Return str(self).", + "_interpchannels.ChannelNotFoundError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpchannels.ChannelNotFoundError.__weakref__" => "list of weak references to the object", + "_interpchannels.ChannelNotFoundError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_interpchannels.ChannelNotFoundError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_interpchannels.close" => "channel_close(cid, *, send=None, recv=None, force=False)\n\nClose the channel for all interpreters.\n\nIf the channel is empty then the keyword args are ignored and both\nends are immediately closed. Otherwise, if 'force' is True then\nall queued items are released and both ends are immediately\nclosed.\n\nIf the channel is not empty *and* 'force' is False then following\nhappens:\n\n * recv is True (regardless of send):\n - raise ChannelNotEmptyError\n * recv is None and send is None:\n - raise ChannelNotEmptyError\n * send is True and recv is not True:\n - fully close the 'send' end\n - close the 'recv' end to interpreters not already receiving\n - fully close it once empty\n\nClosing an already closed channel results in a ChannelClosedError.\n\nOnce the channel's ID has no more ref counts in any interpreter\nthe channel will be destroyed.", + "_interpchannels.create" => "channel_create(unboundop) -> cid\n\nCreate a new cross-interpreter channel and return a unique generated ID.", + "_interpchannels.destroy" => "channel_destroy(cid)\n\nClose and finalize the channel. Afterward attempts to use the channel\nwill behave as though it never existed.", + "_interpchannels.get_channel_defaults" => "get_channel_defaults(cid)\n\nReturn the channel's default values, set when it was created.", + "_interpchannels.get_count" => "get_count(cid)\n\nReturn the number of items in the channel.", + "_interpchannels.get_info" => "get_info(cid)\n\nReturn details about the channel.", + "_interpchannels.list_all" => "channel_list_all() -> [cid]\n\nReturn the list of all IDs for active channels.", + "_interpchannels.list_interpreters" => "channel_list_interpreters(cid, *, send) -> [id]\n\nReturn the list of all interpreter IDs associated with an end of the channel.\n\nThe 'send' argument should be a boolean indicating whether to use the send or\nreceive end.", + "_interpchannels.recv" => "channel_recv(cid, [default]) -> (obj, unboundop)\n\nReturn a new object from the data at the front of the channel's queue.\n\nIf there is nothing to receive then raise ChannelEmptyError, unless\na default value is provided. In that case return it.", + "_interpchannels.release" => "channel_release(cid, *, send=None, recv=None, force=True)\n\nClose the channel for the current interpreter. 'send' and 'recv'\n(bool) may be used to indicate the ends to close. By default both\nends are closed. Closing an already closed end is a noop.", + "_interpchannels.send" => "channel_send(cid, obj, *, blocking=True, timeout=None)\n\nAdd the object's data to the channel's queue.\nBy default this waits for the object to be received.", + "_interpchannels.send_buffer" => "channel_send_buffer(cid, obj, *, blocking=True, timeout=None)\n\nAdd the object's buffer to the channel's queue.\nBy default this waits for the object to be received.", + "_interpqueues" => "This module provides primitive operations to manage Python interpreters.\nThe 'interpreters' module provides a more convenient interface.", + "_interpqueues.bind" => "bind(qid)\n\nTake a reference to the identified queue.\nThe queue is not destroyed until there are no references left.", + "_interpqueues.create" => "create(maxsize, fmt, unboundop) -> qid\n\nCreate a new cross-interpreter queue and return its unique generated ID.\nIt is a new reference as though bind() had been called on the queue.\n\nThe caller is responsible for calling destroy() for the new queue\nbefore the runtime is finalized.", + "_interpqueues.destroy" => "destroy(qid)\n\nClear and destroy the queue. Afterward attempts to use the queue\nwill behave as though it never existed.", + "_interpqueues.get" => "get(qid) -> (obj, fmt)\n\nReturn a new object from the data at the front of the queue.\nThe object's format is also returned.\n\nIf there is nothing to receive then raise QueueEmpty.", + "_interpqueues.get_count" => "get_count(qid)\n\nReturn the number of items in the queue.", + "_interpqueues.get_maxsize" => "get_maxsize(qid)\n\nReturn the maximum number of items in the queue.", + "_interpqueues.get_queue_defaults" => "get_queue_defaults(qid)\n\nReturn the queue's default values, set when it was created.", + "_interpqueues.is_full" => "is_full(qid)\n\nReturn true if the queue has a maxsize and has reached it.", + "_interpqueues.list_all" => "list_all() -> [(qid, fmt)]\n\nReturn the list of IDs for all queues.\nEach corresponding default format is also included.", + "_interpqueues.put" => "put(qid, obj, fmt)\n\nAdd the object's data to the queue.", + "_interpqueues.release" => "release(qid)\n\nRelease a reference to the queue.\nThe queue is destroyed once there are no references left.", + "_interpreters" => "This module provides primitive operations to manage Python interpreters.\nThe 'interpreters' module provides a more convenient interface.", + "_interpreters.CrossInterpreterBufferView.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "_interpreters.CrossInterpreterBufferView.__delattr__" => "Implement delattr(self, name).", + "_interpreters.CrossInterpreterBufferView.__eq__" => "Return self==value.", + "_interpreters.CrossInterpreterBufferView.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_interpreters.CrossInterpreterBufferView.__ge__" => "Return self>=value.", + "_interpreters.CrossInterpreterBufferView.__getattribute__" => "Return getattr(self, name).", + "_interpreters.CrossInterpreterBufferView.__getstate__" => "Helper for pickle.", + "_interpreters.CrossInterpreterBufferView.__gt__" => "Return self>value.", + "_interpreters.CrossInterpreterBufferView.__hash__" => "Return hash(self).", + "_interpreters.CrossInterpreterBufferView.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_interpreters.CrossInterpreterBufferView.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_interpreters.CrossInterpreterBufferView.__le__" => "Return self<=value.", + "_interpreters.CrossInterpreterBufferView.__lt__" => "Return self "Return self!=value.", + "_interpreters.CrossInterpreterBufferView.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_interpreters.CrossInterpreterBufferView.__reduce__" => "Helper for pickle.", + "_interpreters.CrossInterpreterBufferView.__reduce_ex__" => "Helper for pickle.", + "_interpreters.CrossInterpreterBufferView.__repr__" => "Return repr(self).", + "_interpreters.CrossInterpreterBufferView.__setattr__" => "Implement setattr(self, name, value).", + "_interpreters.CrossInterpreterBufferView.__sizeof__" => "Size of object in memory, in bytes.", + "_interpreters.CrossInterpreterBufferView.__str__" => "Return str(self).", + "_interpreters.CrossInterpreterBufferView.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_interpreters.call" => "call(id, callable, args=None, kwargs=None, *, restrict=False)\n\nCall the provided object in the identified interpreter.\nPass the given args and kwargs, if possible.\n\n\"callable\" may be a plain function with no free vars that takes\nno arguments.\n\nThe function's code object is used and all its state\nis ignored, including its __globals__ dict.", + "_interpreters.capture_exception" => "capture_exception(exc=None) -> types.SimpleNamespace\n\nReturn a snapshot of an exception. If \"exc\" is None\nthen the current exception, if any, is used (but not cleared).\n\nThe returned snapshot is the same as what _interpreters.exec() returns.", + "_interpreters.create" => "create([config], *, reqrefs=False) -> ID\n\nCreate a new interpreter and return a unique generated ID.\n\nThe caller is responsible for destroying the interpreter before exiting,\ntypically by using _interpreters.destroy(). This can be managed \nautomatically by passing \"reqrefs=True\" and then using _incref() and\n_decref()` appropriately.\n\n\"config\" must be a valid interpreter config or the name of a\npredefined config (\"isolated\" or \"legacy\"). The default\nis \"isolated\".", + "_interpreters.destroy" => "destroy(id, *, restrict=False)\n\nDestroy the identified interpreter.\n\nAttempting to destroy the current interpreter raises InterpreterError.\nSo does an unrecognized ID.", + "_interpreters.exec" => "exec(id, code, shared=None, *, restrict=False)\n\nExecute the provided code in the identified interpreter.\nThis is equivalent to running the builtin exec() under the target\ninterpreter, using the __dict__ of its __main__ module as both\nglobals and locals.\n\n\"code\" may be a string containing the text of a Python script.\n\nFunctions (and code objects) are also supported, with some restrictions.\nThe code/function must not take any arguments or be a closure\n(i.e. have cell vars). Methods and other callables are not supported.\n\nIf a function is provided, its code object is used and all its state\nis ignored, including its __globals__ dict.", + "_interpreters.get_config" => "get_config(id, *, restrict=False) -> types.SimpleNamespace\n\nReturn a representation of the config used to initialize the interpreter.", + "_interpreters.get_current" => "get_current() -> (ID, whence)\n\nReturn the ID of current interpreter.", + "_interpreters.get_main" => "get_main() -> (ID, whence)\n\nReturn the ID of main interpreter.", + "_interpreters.is_running" => "is_running(id, *, restrict=False) -> bool\n\nReturn whether or not the identified interpreter is running.", + "_interpreters.is_shareable" => "is_shareable(obj) -> bool\n\nReturn True if the object's data may be shared between interpreters and\nFalse otherwise.", + "_interpreters.list_all" => "list_all() -> [(ID, whence)]\n\nReturn a list containing the ID of every existing interpreter.", + "_interpreters.new_config" => "new_config(name='isolated', /, **overrides) -> type.SimpleNamespace\n\nReturn a representation of a new PyInterpreterConfig.\n\nThe name determines the initial values of the config. Supported named\nconfigs are: default, isolated, legacy, and empty.\n\nAny keyword arguments are set on the corresponding config fields,\noverriding the initial values.", + "_interpreters.run_func" => "run_func(id, func, shared=None, *, restrict=False)\n\nExecute the body of the provided function in the identified interpreter.\nCode objects are also supported. In both cases, closures and args\nare not supported. Methods and other callables are not supported either.\n\n(See _interpreters.exec().", + "_interpreters.run_string" => "run_string(id, script, shared=None, *, restrict=False)\n\nExecute the provided string in the identified interpreter.\n\n(See _interpreters.exec().", + "_interpreters.set___main___attrs" => "set___main___attrs(id, ns, *, restrict=False)\n\nBind the given attributes in the interpreter's __main__ module.", + "_interpreters.whence" => "whence(id) -> int\n\nReturn an identifier for where the interpreter was created.", + "_io" => "The io module provides the Python interfaces to stream handling. The\nbuiltin open function is defined in this module.\n\nAt the top of the I/O hierarchy is the abstract base class IOBase. It\ndefines the basic interface to a stream. Note, however, that there is no\nseparation between reading and writing to streams; implementations are\nallowed to raise an OSError if they do not support a given operation.\n\nExtending IOBase is RawIOBase which deals simply with the reading and\nwriting of raw bytes to a stream. FileIO subclasses RawIOBase to provide\nan interface to OS files.\n\nBufferedIOBase deals with buffering on a raw byte stream (RawIOBase). Its\nsubclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer\nstreams that are readable, writable, and both respectively.\nBufferedRandom provides a buffered interface to random access\nstreams. BytesIO is a simple stream of in-memory bytes.\n\nAnother IOBase subclass, TextIOBase, deals with the encoding and decoding\nof streams into text. TextIOWrapper, which extends it, is a buffered text\ninterface to a buffered raw stream (`BufferedIOBase`). Finally, StringIO\nis an in-memory stream for text.\n\nArgument names are not part of the specification, and only the arguments\nof open() are intended to be used as keyword arguments.\n\ndata:\n\nDEFAULT_BUFFER_SIZE\n\n An int containing the default buffer size used by the module's buffered\n I/O classes. open() uses the file's blksize (as obtained by os.stat) if\n possible.", + "_io.BufferedRWPair" => "A buffered reader and writer object together.\n\nA buffered reader object and buffered writer object put together to\nform a sequential IO object that can read and write. This is typically\nused with a socket or two-way pipe.\n\nreader and writer are RawIOBase objects that are readable and\nwriteable respectively. If the buffer_size is omitted it defaults to\nDEFAULT_BUFFER_SIZE.", + "_io.BufferedRWPair.__del__" => "Called when the instance is about to be destroyed.", + "_io.BufferedRWPair.__delattr__" => "Implement delattr(self, name).", + "_io.BufferedRWPair.__eq__" => "Return self==value.", + "_io.BufferedRWPair.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.BufferedRWPair.__ge__" => "Return self>=value.", + "_io.BufferedRWPair.__getattribute__" => "Return getattr(self, name).", + "_io.BufferedRWPair.__getstate__" => "Helper for pickle.", + "_io.BufferedRWPair.__gt__" => "Return self>value.", + "_io.BufferedRWPair.__hash__" => "Return hash(self).", + "_io.BufferedRWPair.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.BufferedRWPair.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.BufferedRWPair.__iter__" => "Implement iter(self).", + "_io.BufferedRWPair.__le__" => "Return self<=value.", + "_io.BufferedRWPair.__lt__" => "Return self "Return self!=value.", + "_io.BufferedRWPair.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.BufferedRWPair.__next__" => "Implement next(self).", + "_io.BufferedRWPair.__reduce__" => "Helper for pickle.", + "_io.BufferedRWPair.__reduce_ex__" => "Helper for pickle.", + "_io.BufferedRWPair.__repr__" => "Return repr(self).", + "_io.BufferedRWPair.__setattr__" => "Implement setattr(self, name, value).", + "_io.BufferedRWPair.__sizeof__" => "Size of object in memory, in bytes.", + "_io.BufferedRWPair.__str__" => "Return str(self).", + "_io.BufferedRWPair.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.BufferedRWPair.detach" => "Disconnect this buffer from its underlying raw stream and return it.\n\nAfter the raw stream has been detached, the buffer is in an unusable\nstate.", + "_io.BufferedRWPair.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io.BufferedRWPair.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io.BufferedRWPair.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.BufferedRWPair.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io.BufferedRWPair.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io.BufferedRWPair.tell" => "Return current stream position.", + "_io.BufferedRWPair.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io.BufferedRWPair.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.BufferedRandom" => "A buffered interface to random access streams.\n\nThe constructor creates a reader and writer for a seekable stream,\nraw, given in the first argument. If the buffer_size is omitted it\ndefaults to DEFAULT_BUFFER_SIZE.", + "_io.BufferedRandom.__del__" => "Called when the instance is about to be destroyed.", + "_io.BufferedRandom.__delattr__" => "Implement delattr(self, name).", + "_io.BufferedRandom.__eq__" => "Return self==value.", + "_io.BufferedRandom.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.BufferedRandom.__ge__" => "Return self>=value.", + "_io.BufferedRandom.__getattribute__" => "Return getattr(self, name).", + "_io.BufferedRandom.__gt__" => "Return self>value.", + "_io.BufferedRandom.__hash__" => "Return hash(self).", + "_io.BufferedRandom.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.BufferedRandom.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.BufferedRandom.__iter__" => "Implement iter(self).", + "_io.BufferedRandom.__le__" => "Return self<=value.", + "_io.BufferedRandom.__lt__" => "Return self "Return self!=value.", + "_io.BufferedRandom.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.BufferedRandom.__next__" => "Implement next(self).", + "_io.BufferedRandom.__reduce__" => "Helper for pickle.", + "_io.BufferedRandom.__reduce_ex__" => "Helper for pickle.", + "_io.BufferedRandom.__repr__" => "Return repr(self).", + "_io.BufferedRandom.__setattr__" => "Implement setattr(self, name, value).", + "_io.BufferedRandom.__str__" => "Return str(self).", + "_io.BufferedRandom.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.BufferedRandom.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.BufferedRandom.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.BufferedReader" => "Create a new buffered reader using the given readable raw IO object.", + "_io.BufferedReader.__del__" => "Called when the instance is about to be destroyed.", + "_io.BufferedReader.__delattr__" => "Implement delattr(self, name).", + "_io.BufferedReader.__eq__" => "Return self==value.", + "_io.BufferedReader.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.BufferedReader.__ge__" => "Return self>=value.", + "_io.BufferedReader.__getattribute__" => "Return getattr(self, name).", + "_io.BufferedReader.__gt__" => "Return self>value.", + "_io.BufferedReader.__hash__" => "Return hash(self).", + "_io.BufferedReader.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.BufferedReader.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.BufferedReader.__iter__" => "Implement iter(self).", + "_io.BufferedReader.__le__" => "Return self<=value.", + "_io.BufferedReader.__lt__" => "Return self "Return self!=value.", + "_io.BufferedReader.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.BufferedReader.__next__" => "Implement next(self).", + "_io.BufferedReader.__reduce__" => "Helper for pickle.", + "_io.BufferedReader.__reduce_ex__" => "Helper for pickle.", + "_io.BufferedReader.__repr__" => "Return repr(self).", + "_io.BufferedReader.__setattr__" => "Implement setattr(self, name, value).", + "_io.BufferedReader.__str__" => "Return str(self).", + "_io.BufferedReader.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.BufferedReader.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.BufferedReader.writable" => "Return whether object was opened for writing.\n\nIf False, write() will raise OSError.", + "_io.BufferedReader.write" => "Write buffer b to the IO stream.\n\nReturn the number of bytes written, which is always\nthe length of b in bytes.\n\nRaise BlockingIOError if the buffer is full and the\nunderlying raw stream cannot accept more data at the moment.", + "_io.BufferedReader.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.BufferedWriter" => "A buffer for a writeable sequential RawIO object.\n\nThe constructor creates a BufferedWriter for the given writeable raw\nstream. If the buffer_size is not given, it defaults to\nDEFAULT_BUFFER_SIZE.", + "_io.BufferedWriter.__del__" => "Called when the instance is about to be destroyed.", + "_io.BufferedWriter.__delattr__" => "Implement delattr(self, name).", + "_io.BufferedWriter.__eq__" => "Return self==value.", + "_io.BufferedWriter.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.BufferedWriter.__ge__" => "Return self>=value.", + "_io.BufferedWriter.__getattribute__" => "Return getattr(self, name).", + "_io.BufferedWriter.__gt__" => "Return self>value.", + "_io.BufferedWriter.__hash__" => "Return hash(self).", + "_io.BufferedWriter.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.BufferedWriter.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.BufferedWriter.__iter__" => "Implement iter(self).", + "_io.BufferedWriter.__le__" => "Return self<=value.", + "_io.BufferedWriter.__lt__" => "Return self "Return self!=value.", + "_io.BufferedWriter.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.BufferedWriter.__next__" => "Implement next(self).", + "_io.BufferedWriter.__reduce__" => "Helper for pickle.", + "_io.BufferedWriter.__reduce_ex__" => "Helper for pickle.", + "_io.BufferedWriter.__repr__" => "Return repr(self).", + "_io.BufferedWriter.__setattr__" => "Implement setattr(self, name, value).", + "_io.BufferedWriter.__str__" => "Return str(self).", + "_io.BufferedWriter.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.BufferedWriter.read" => "Read and return up to n bytes.\n\nIf the size argument is omitted, None, or negative, read and\nreturn all data until EOF.\n\nIf the size argument is positive, and the underlying raw stream is\nnot 'interactive', multiple raw reads may be issued to satisfy\nthe byte count (unless EOF is reached first).\nHowever, for interactive raw streams (as well as sockets and pipes),\nat most one raw read will be issued, and a short result does not\nimply that EOF is imminent.\n\nReturn an empty bytes object on EOF.\n\nReturn None if the underlying raw stream was open in non-blocking\nmode and no data is available at the moment.", + "_io.BufferedWriter.read1" => "Read and return up to size bytes, with at most one read() call to the underlying raw stream.\n\nReturn an empty bytes object on EOF.\nA short result does not imply that EOF is imminent.", + "_io.BufferedWriter.readable" => "Return whether object was opened for reading.\n\nIf False, read() will raise OSError.", + "_io.BufferedWriter.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io.BufferedWriter.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.BufferedWriter.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.BytesIO" => "Buffered I/O implementation using an in-memory bytes buffer.", + "_io.BytesIO.__del__" => "Called when the instance is about to be destroyed.", + "_io.BytesIO.__delattr__" => "Implement delattr(self, name).", + "_io.BytesIO.__eq__" => "Return self==value.", + "_io.BytesIO.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.BytesIO.__ge__" => "Return self>=value.", + "_io.BytesIO.__getattribute__" => "Return getattr(self, name).", + "_io.BytesIO.__gt__" => "Return self>value.", + "_io.BytesIO.__hash__" => "Return hash(self).", + "_io.BytesIO.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.BytesIO.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.BytesIO.__iter__" => "Implement iter(self).", + "_io.BytesIO.__le__" => "Return self<=value.", + "_io.BytesIO.__lt__" => "Return self "Return self!=value.", + "_io.BytesIO.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.BytesIO.__next__" => "Implement next(self).", + "_io.BytesIO.__reduce__" => "Helper for pickle.", + "_io.BytesIO.__reduce_ex__" => "Helper for pickle.", + "_io.BytesIO.__repr__" => "Return repr(self).", + "_io.BytesIO.__setattr__" => "Implement setattr(self, name, value).", + "_io.BytesIO.__str__" => "Return str(self).", + "_io.BytesIO.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.BytesIO.close" => "Disable all I/O operations.", + "_io.BytesIO.closed" => "True if the file is closed.", + "_io.BytesIO.detach" => "Disconnect this buffer from its underlying raw stream and return it.\n\nAfter the raw stream has been detached, the buffer is in an unusable\nstate.", + "_io.BytesIO.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io.BytesIO.flush" => "Does nothing.", + "_io.BytesIO.getbuffer" => "Get a read-write view over the contents of the BytesIO object.", + "_io.BytesIO.getvalue" => "Retrieve the entire contents of the BytesIO object.", + "_io.BytesIO.isatty" => "Always returns False.\n\nBytesIO objects are not connected to a TTY-like device.", + "_io.BytesIO.read" => "Read at most size bytes, returned as a bytes object.\n\nIf the size argument is negative, read until EOF is reached.\nReturn an empty bytes object at EOF.", + "_io.BytesIO.read1" => "Read at most size bytes, returned as a bytes object.\n\nIf the size argument is negative or omitted, read until EOF is reached.\nReturn an empty bytes object at EOF.", + "_io.BytesIO.readable" => "Returns True if the IO object can be read.", + "_io.BytesIO.readinto" => "Read bytes into buffer.\n\nReturns number of bytes read (0 for EOF), or None if the object\nis set not to block and has no data to read.", + "_io.BytesIO.readline" => "Next line from the file, as a bytes object.\n\nRetain newline. A non-negative size argument limits the maximum\nnumber of bytes to return (an incomplete line may be returned then).\nReturn an empty bytes object at EOF.", + "_io.BytesIO.readlines" => "List of bytes objects, each a line from the file.\n\nCall readline() repeatedly and return a list of the lines so read.\nThe optional size argument, if given, is an approximate bound on the\ntotal number of bytes in the lines returned.", + "_io.BytesIO.seek" => "Change stream position.\n\nSeek to byte offset pos relative to position indicated by whence:\n 0 Start of stream (the default). pos should be >= 0;\n 1 Current position - pos may be negative;\n 2 End of stream - pos usually negative.\nReturns the new absolute position.", + "_io.BytesIO.seekable" => "Returns True if the IO object can be seeked.", + "_io.BytesIO.tell" => "Current file position, an integer.", + "_io.BytesIO.truncate" => "Truncate the file to at most size bytes.\n\nSize defaults to the current file position, as returned by tell().\nThe current file position is unchanged. Returns the new size.", + "_io.BytesIO.writable" => "Returns True if the IO object can be written.", + "_io.BytesIO.write" => "Write bytes to file.\n\nReturn the number of bytes written.", + "_io.BytesIO.writelines" => "Write lines to the file.\n\nNote that newlines are not added. lines can be any iterable object\nproducing bytes-like objects. This is equivalent to calling write() for\neach element.", + "_io.FileIO" => "Open a file.\n\nThe mode can be 'r' (default), 'w', 'x' or 'a' for reading,\nwriting, exclusive creation or appending. The file will be created if it\ndoesn't exist when opened for writing or appending; it will be truncated\nwhen opened for writing. A FileExistsError will be raised if it already\nexists when opened for creating. Opening a file for creating implies\nwriting so this mode behaves in a similar way to 'w'.Add a '+' to the mode\nto allow simultaneous reading and writing. A custom opener can be used by\npassing a callable as *opener*. The underlying file descriptor for the file\nobject is then obtained by calling opener with (*name*, *flags*).\n*opener* must return an open file descriptor (passing os.open as *opener*\nresults in functionality similar to passing None).", + "_io.FileIO.__del__" => "Called when the instance is about to be destroyed.", + "_io.FileIO.__delattr__" => "Implement delattr(self, name).", + "_io.FileIO.__eq__" => "Return self==value.", + "_io.FileIO.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.FileIO.__ge__" => "Return self>=value.", + "_io.FileIO.__getattribute__" => "Return getattr(self, name).", + "_io.FileIO.__gt__" => "Return self>value.", + "_io.FileIO.__hash__" => "Return hash(self).", + "_io.FileIO.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.FileIO.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.FileIO.__iter__" => "Implement iter(self).", + "_io.FileIO.__le__" => "Return self<=value.", + "_io.FileIO.__lt__" => "Return self "Return self!=value.", + "_io.FileIO.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.FileIO.__next__" => "Implement next(self).", + "_io.FileIO.__reduce__" => "Helper for pickle.", + "_io.FileIO.__reduce_ex__" => "Helper for pickle.", + "_io.FileIO.__repr__" => "Return repr(self).", + "_io.FileIO.__setattr__" => "Implement setattr(self, name, value).", + "_io.FileIO.__sizeof__" => "Size of object in memory, in bytes.", + "_io.FileIO.__str__" => "Return str(self).", + "_io.FileIO.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.FileIO.close" => "Close the file.\n\nA closed file cannot be used for further I/O operations. close() may be\ncalled more than once without error.", + "_io.FileIO.closed" => "True if the file is closed", + "_io.FileIO.closefd" => "True if the file descriptor will be closed by close().", + "_io.FileIO.fileno" => "Return the underlying file descriptor (an integer).", + "_io.FileIO.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io.FileIO.isatty" => "True if the file is connected to a TTY device.", + "_io.FileIO.mode" => "String giving the file mode", + "_io.FileIO.read" => "Read at most size bytes, returned as bytes.\n\nOnly makes one system call, so less data may be returned than requested.\nIn non-blocking mode, returns None if no data is available.\nReturn an empty bytes object at EOF.", + "_io.FileIO.readable" => "True if file was opened in a read mode.", + "_io.FileIO.readall" => "Read all data from the file, returned as bytes.\n\nIn non-blocking mode, returns as much as is immediately available,\nor None if no data is available. Return an empty bytes object at EOF.", + "_io.FileIO.readinto" => "Same as RawIOBase.readinto().", + "_io.FileIO.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io.FileIO.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.FileIO.seek" => "Move to new file position and return the file position.\n\nArgument offset is a byte count. Optional argument whence defaults to\nSEEK_SET or 0 (offset from start of file, offset should be >= 0); other values\nare SEEK_CUR or 1 (move relative to current position, positive or negative),\nand SEEK_END or 2 (move relative to end of file, usually negative, although\nmany platforms allow seeking beyond the end of a file).\n\nNote that not all file objects are seekable.", + "_io.FileIO.seekable" => "True if file supports random-access.", + "_io.FileIO.tell" => "Current file position.\n\nCan raise OSError for non seekable files.", + "_io.FileIO.truncate" => "Truncate the file to at most size bytes and return the truncated size.\n\nSize defaults to the current file position, as returned by tell().\nThe current file position is changed to the value of size.", + "_io.FileIO.writable" => "True if file was opened in a write mode.", + "_io.FileIO.write" => "Write buffer b to file, return number of bytes written.\n\nOnly makes one system call, so not all of the data may be written.\nThe number of bytes actually written is returned. In non-blocking mode,\nreturns None if the write would block.", + "_io.FileIO.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.IncrementalNewlineDecoder" => "Codec used when reading a file in universal newlines mode.\n\nIt wraps another incremental decoder, translating \\r\\n and \\r into \\n.\nIt also records the types of newlines encountered. When used with\ntranslate=False, it ensures that the newline sequence is returned in\none piece. When used with decoder=None, it expects unicode strings as\ndecode input and translates newlines without first invoking an external\ndecoder.", + "_io.IncrementalNewlineDecoder.__delattr__" => "Implement delattr(self, name).", + "_io.IncrementalNewlineDecoder.__eq__" => "Return self==value.", + "_io.IncrementalNewlineDecoder.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.IncrementalNewlineDecoder.__ge__" => "Return self>=value.", + "_io.IncrementalNewlineDecoder.__getattribute__" => "Return getattr(self, name).", + "_io.IncrementalNewlineDecoder.__getstate__" => "Helper for pickle.", + "_io.IncrementalNewlineDecoder.__gt__" => "Return self>value.", + "_io.IncrementalNewlineDecoder.__hash__" => "Return hash(self).", + "_io.IncrementalNewlineDecoder.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.IncrementalNewlineDecoder.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.IncrementalNewlineDecoder.__le__" => "Return self<=value.", + "_io.IncrementalNewlineDecoder.__lt__" => "Return self "Return self!=value.", + "_io.IncrementalNewlineDecoder.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.IncrementalNewlineDecoder.__reduce__" => "Helper for pickle.", + "_io.IncrementalNewlineDecoder.__reduce_ex__" => "Helper for pickle.", + "_io.IncrementalNewlineDecoder.__repr__" => "Return repr(self).", + "_io.IncrementalNewlineDecoder.__setattr__" => "Implement setattr(self, name, value).", + "_io.IncrementalNewlineDecoder.__sizeof__" => "Size of object in memory, in bytes.", + "_io.IncrementalNewlineDecoder.__str__" => "Return str(self).", + "_io.IncrementalNewlineDecoder.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.StringIO" => "Text I/O implementation using an in-memory buffer.\n\nThe initial_value argument sets the value of object. The newline\nargument is like the one of TextIOWrapper's constructor.", + "_io.StringIO.__del__" => "Called when the instance is about to be destroyed.", + "_io.StringIO.__delattr__" => "Implement delattr(self, name).", + "_io.StringIO.__eq__" => "Return self==value.", + "_io.StringIO.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.StringIO.__ge__" => "Return self>=value.", + "_io.StringIO.__getattribute__" => "Return getattr(self, name).", + "_io.StringIO.__gt__" => "Return self>value.", + "_io.StringIO.__hash__" => "Return hash(self).", + "_io.StringIO.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.StringIO.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.StringIO.__iter__" => "Implement iter(self).", + "_io.StringIO.__le__" => "Return self<=value.", + "_io.StringIO.__lt__" => "Return self "Return self!=value.", + "_io.StringIO.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.StringIO.__next__" => "Implement next(self).", + "_io.StringIO.__reduce__" => "Helper for pickle.", + "_io.StringIO.__reduce_ex__" => "Helper for pickle.", + "_io.StringIO.__repr__" => "Return repr(self).", + "_io.StringIO.__setattr__" => "Implement setattr(self, name, value).", + "_io.StringIO.__sizeof__" => "Size of object in memory, in bytes.", + "_io.StringIO.__str__" => "Return str(self).", + "_io.StringIO.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.StringIO.close" => "Close the IO object.\n\nAttempting any further operation after the object is closed\nwill raise a ValueError.\n\nThis method has no effect if the file is already closed.", + "_io.StringIO.detach" => "Separate the underlying buffer from the TextIOBase and return it.\n\nAfter the underlying buffer has been detached, the TextIO is in an unusable state.", + "_io.StringIO.encoding" => "Encoding of the text stream.\n\nSubclasses should override.", + "_io.StringIO.errors" => "The error setting of the decoder or encoder.\n\nSubclasses should override.", + "_io.StringIO.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io.StringIO.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io.StringIO.getvalue" => "Retrieve the entire contents of the object.", + "_io.StringIO.isatty" => "Return whether this is an 'interactive' stream.\n\nReturn False if it can't be determined.", + "_io.StringIO.read" => "Read at most size characters, returned as a string.\n\nIf the argument is negative or omitted, read until EOF\nis reached. Return an empty string at EOF.", + "_io.StringIO.readable" => "Returns True if the IO object can be read.", + "_io.StringIO.readline" => "Read until newline or EOF.\n\nReturns an empty string if EOF is hit immediately.", + "_io.StringIO.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.StringIO.seek" => "Change stream position.\n\nSeek to character offset pos relative to position indicated by whence:\n 0 Start of stream (the default). pos should be >= 0;\n 1 Current position - pos must be 0;\n 2 End of stream - pos must be 0.\nReturns the new absolute position.", + "_io.StringIO.seekable" => "Returns True if the IO object can be seeked.", + "_io.StringIO.tell" => "Tell the current file position.", + "_io.StringIO.truncate" => "Truncate size to pos.\n\nThe pos argument defaults to the current file position, as\nreturned by tell(). The current file position is unchanged.\nReturns the new absolute position.", + "_io.StringIO.writable" => "Returns True if the IO object can be written.", + "_io.StringIO.write" => "Write string to file.\n\nReturns the number of characters written, which is always equal to\nthe length of the string.", + "_io.StringIO.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.TextIOWrapper" => "Character and line based layer over a BufferedIOBase object, buffer.\n\nencoding gives the name of the encoding that the stream will be\ndecoded or encoded with. It defaults to locale.getencoding().\n\nerrors determines the strictness of encoding and decoding (see\nhelp(codecs.Codec) or the documentation for codecs.register) and\ndefaults to \"strict\".\n\nnewline controls how line endings are handled. It can be None, '',\n'\\n', '\\r', and '\\r\\n'. It works as follows:\n\n* On input, if newline is None, universal newlines mode is\n enabled. Lines in the input can end in '\\n', '\\r', or '\\r\\n', and\n these are translated into '\\n' before being returned to the\n caller. If it is '', universal newline mode is enabled, but line\n endings are returned to the caller untranslated. If it has any of\n the other legal values, input lines are only terminated by the given\n string, and the line ending is returned to the caller untranslated.\n\n* On output, if newline is None, any '\\n' characters written are\n translated to the system default line separator, os.linesep. If\n newline is '' or '\\n', no translation takes place. If newline is any\n of the other legal values, any '\\n' characters written are translated\n to the given string.\n\nIf line_buffering is True, a call to flush is implied when a call to\nwrite contains a newline character.", + "_io.TextIOWrapper.__del__" => "Called when the instance is about to be destroyed.", + "_io.TextIOWrapper.__delattr__" => "Implement delattr(self, name).", + "_io.TextIOWrapper.__eq__" => "Return self==value.", + "_io.TextIOWrapper.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io.TextIOWrapper.__ge__" => "Return self>=value.", + "_io.TextIOWrapper.__getattribute__" => "Return getattr(self, name).", + "_io.TextIOWrapper.__gt__" => "Return self>value.", + "_io.TextIOWrapper.__hash__" => "Return hash(self).", + "_io.TextIOWrapper.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io.TextIOWrapper.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io.TextIOWrapper.__iter__" => "Implement iter(self).", + "_io.TextIOWrapper.__le__" => "Return self<=value.", + "_io.TextIOWrapper.__lt__" => "Return self "Return self!=value.", + "_io.TextIOWrapper.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io.TextIOWrapper.__next__" => "Implement next(self).", + "_io.TextIOWrapper.__reduce__" => "Helper for pickle.", + "_io.TextIOWrapper.__reduce_ex__" => "Helper for pickle.", + "_io.TextIOWrapper.__repr__" => "Return repr(self).", + "_io.TextIOWrapper.__setattr__" => "Implement setattr(self, name, value).", + "_io.TextIOWrapper.__sizeof__" => "Size of object in memory, in bytes.", + "_io.TextIOWrapper.__str__" => "Return str(self).", + "_io.TextIOWrapper.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io.TextIOWrapper.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io.TextIOWrapper.reconfigure" => "Reconfigure the text stream with new parameters.\n\nThis also does an implicit stream flush.", + "_io.TextIOWrapper.seek" => "Set the stream position, and return the new stream position.\n\n cookie\n Zero or an opaque number returned by tell().\n whence\n The relative position to seek from.\n\nFour operations are supported, given by the following argument\ncombinations:\n\n- seek(0, SEEK_SET): Rewind to the start of the stream.\n- seek(cookie, SEEK_SET): Restore a previous position;\n 'cookie' must be a number returned by tell().\n- seek(0, SEEK_END): Fast-forward to the end of the stream.\n- seek(0, SEEK_CUR): Leave the current stream position unchanged.\n\nAny other argument combinations are invalid,\nand may raise exceptions.", + "_io.TextIOWrapper.tell" => "Return the stream position as an opaque number.\n\nThe return value of tell() can be given as input to seek(), to restore a\nprevious stream position.", + "_io.TextIOWrapper.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io._BufferedIOBase" => "Base class for buffered IO objects.\n\nThe main difference with RawIOBase is that the read() method\nsupports omitting the size argument, and does not have a default\nimplementation that defers to readinto().\n\nIn addition, read(), readinto() and write() may raise\nBlockingIOError if the underlying raw stream is in non-blocking\nmode and not ready; unlike their raw counterparts, they will never\nreturn None.\n\nA typical implementation should not inherit from a RawIOBase\nimplementation, but wrap one.", + "_io._BufferedIOBase.__del__" => "Called when the instance is about to be destroyed.", + "_io._BufferedIOBase.__delattr__" => "Implement delattr(self, name).", + "_io._BufferedIOBase.__eq__" => "Return self==value.", + "_io._BufferedIOBase.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._BufferedIOBase.__ge__" => "Return self>=value.", + "_io._BufferedIOBase.__getattribute__" => "Return getattr(self, name).", + "_io._BufferedIOBase.__getstate__" => "Helper for pickle.", + "_io._BufferedIOBase.__gt__" => "Return self>value.", + "_io._BufferedIOBase.__hash__" => "Return hash(self).", + "_io._BufferedIOBase.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._BufferedIOBase.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._BufferedIOBase.__iter__" => "Implement iter(self).", + "_io._BufferedIOBase.__le__" => "Return self<=value.", + "_io._BufferedIOBase.__lt__" => "Return self "Return self!=value.", + "_io._BufferedIOBase.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._BufferedIOBase.__next__" => "Implement next(self).", + "_io._BufferedIOBase.__reduce__" => "Helper for pickle.", + "_io._BufferedIOBase.__reduce_ex__" => "Helper for pickle.", + "_io._BufferedIOBase.__repr__" => "Return repr(self).", + "_io._BufferedIOBase.__setattr__" => "Implement setattr(self, name, value).", + "_io._BufferedIOBase.__sizeof__" => "Size of object in memory, in bytes.", + "_io._BufferedIOBase.__str__" => "Return str(self).", + "_io._BufferedIOBase.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._BufferedIOBase.close" => "Flush and close the IO object.\n\nThis method has no effect if the file is already closed.", + "_io._BufferedIOBase.detach" => "Disconnect this buffer from its underlying raw stream and return it.\n\nAfter the raw stream has been detached, the buffer is in an unusable\nstate.", + "_io._BufferedIOBase.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io._BufferedIOBase.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io._BufferedIOBase.isatty" => "Return whether this is an 'interactive' stream.\n\nReturn False if it can't be determined.", + "_io._BufferedIOBase.read" => "Read and return up to n bytes.\n\nIf the size argument is omitted, None, or negative, read and\nreturn all data until EOF.\n\nIf the size argument is positive, and the underlying raw stream is\nnot 'interactive', multiple raw reads may be issued to satisfy\nthe byte count (unless EOF is reached first).\nHowever, for interactive raw streams (as well as sockets and pipes),\nat most one raw read will be issued, and a short result does not\nimply that EOF is imminent.\n\nReturn an empty bytes object on EOF.\n\nReturn None if the underlying raw stream was open in non-blocking\nmode and no data is available at the moment.", + "_io._BufferedIOBase.read1" => "Read and return up to size bytes, with at most one read() call to the underlying raw stream.\n\nReturn an empty bytes object on EOF.\nA short result does not imply that EOF is imminent.", + "_io._BufferedIOBase.readable" => "Return whether object was opened for reading.\n\nIf False, read() will raise OSError.", + "_io._BufferedIOBase.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io._BufferedIOBase.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io._BufferedIOBase.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io._BufferedIOBase.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io._BufferedIOBase.tell" => "Return current stream position.", + "_io._BufferedIOBase.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io._BufferedIOBase.writable" => "Return whether object was opened for writing.\n\nIf False, write() will raise OSError.", + "_io._BufferedIOBase.write" => "Write buffer b to the IO stream.\n\nReturn the number of bytes written, which is always\nthe length of b in bytes.\n\nRaise BlockingIOError if the buffer is full and the\nunderlying raw stream cannot accept more data at the moment.", + "_io._BufferedIOBase.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io._BytesIOBuffer.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "_io._BytesIOBuffer.__delattr__" => "Implement delattr(self, name).", + "_io._BytesIOBuffer.__eq__" => "Return self==value.", + "_io._BytesIOBuffer.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._BytesIOBuffer.__ge__" => "Return self>=value.", + "_io._BytesIOBuffer.__getattribute__" => "Return getattr(self, name).", + "_io._BytesIOBuffer.__getstate__" => "Helper for pickle.", + "_io._BytesIOBuffer.__gt__" => "Return self>value.", + "_io._BytesIOBuffer.__hash__" => "Return hash(self).", + "_io._BytesIOBuffer.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._BytesIOBuffer.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._BytesIOBuffer.__le__" => "Return self<=value.", + "_io._BytesIOBuffer.__lt__" => "Return self "Return self!=value.", + "_io._BytesIOBuffer.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._BytesIOBuffer.__reduce__" => "Helper for pickle.", + "_io._BytesIOBuffer.__reduce_ex__" => "Helper for pickle.", + "_io._BytesIOBuffer.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "_io._BytesIOBuffer.__repr__" => "Return repr(self).", + "_io._BytesIOBuffer.__setattr__" => "Implement setattr(self, name, value).", + "_io._BytesIOBuffer.__sizeof__" => "Size of object in memory, in bytes.", + "_io._BytesIOBuffer.__str__" => "Return str(self).", + "_io._BytesIOBuffer.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._IOBase" => "The abstract base class for all I/O classes.\n\nThis class provides dummy implementations for many methods that\nderived classes can override selectively; the default implementations\nrepresent a file that cannot be read, written or seeked.\n\nEven though IOBase does not declare read, readinto, or write because\ntheir signatures will vary, implementations and clients should\nconsider those methods part of the interface. Also, implementations\nmay raise UnsupportedOperation when operations they do not support are\ncalled.\n\nThe basic type used for binary data read from or written to a file is\nbytes. Other bytes-like objects are accepted as method arguments too.\nIn some cases (such as readinto), a writable object is required. Text\nI/O classes work with str data.\n\nNote that calling any method (except additional calls to close(),\nwhich are ignored) on a closed stream should raise a ValueError.\n\nIOBase (and its subclasses) support the iterator protocol, meaning\nthat an IOBase object can be iterated over yielding the lines in a\nstream.\n\nIOBase also supports the :keyword:`with` statement. In this example,\nfp is closed after the suite of the with statement is complete:\n\nwith open('spam.txt', 'r') as fp:\n fp.write('Spam and eggs!')", + "_io._IOBase.__del__" => "Called when the instance is about to be destroyed.", + "_io._IOBase.__delattr__" => "Implement delattr(self, name).", + "_io._IOBase.__eq__" => "Return self==value.", + "_io._IOBase.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._IOBase.__ge__" => "Return self>=value.", + "_io._IOBase.__getattribute__" => "Return getattr(self, name).", + "_io._IOBase.__getstate__" => "Helper for pickle.", + "_io._IOBase.__gt__" => "Return self>value.", + "_io._IOBase.__hash__" => "Return hash(self).", + "_io._IOBase.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._IOBase.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._IOBase.__iter__" => "Implement iter(self).", + "_io._IOBase.__le__" => "Return self<=value.", + "_io._IOBase.__lt__" => "Return self "Return self!=value.", + "_io._IOBase.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._IOBase.__next__" => "Implement next(self).", + "_io._IOBase.__reduce__" => "Helper for pickle.", + "_io._IOBase.__reduce_ex__" => "Helper for pickle.", + "_io._IOBase.__repr__" => "Return repr(self).", + "_io._IOBase.__setattr__" => "Implement setattr(self, name, value).", + "_io._IOBase.__sizeof__" => "Size of object in memory, in bytes.", + "_io._IOBase.__str__" => "Return str(self).", + "_io._IOBase.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._IOBase.close" => "Flush and close the IO object.\n\nThis method has no effect if the file is already closed.", + "_io._IOBase.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io._IOBase.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io._IOBase.isatty" => "Return whether this is an 'interactive' stream.\n\nReturn False if it can't be determined.", + "_io._IOBase.readable" => "Return whether object was opened for reading.\n\nIf False, read() will raise OSError.", + "_io._IOBase.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io._IOBase.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io._IOBase.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io._IOBase.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io._IOBase.tell" => "Return current stream position.", + "_io._IOBase.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io._IOBase.writable" => "Return whether object was opened for writing.\n\nIf False, write() will raise OSError.", + "_io._IOBase.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io._RawIOBase" => "Base class for raw binary I/O.", + "_io._RawIOBase.__del__" => "Called when the instance is about to be destroyed.", + "_io._RawIOBase.__delattr__" => "Implement delattr(self, name).", + "_io._RawIOBase.__eq__" => "Return self==value.", + "_io._RawIOBase.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._RawIOBase.__ge__" => "Return self>=value.", + "_io._RawIOBase.__getattribute__" => "Return getattr(self, name).", + "_io._RawIOBase.__getstate__" => "Helper for pickle.", + "_io._RawIOBase.__gt__" => "Return self>value.", + "_io._RawIOBase.__hash__" => "Return hash(self).", + "_io._RawIOBase.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._RawIOBase.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._RawIOBase.__iter__" => "Implement iter(self).", + "_io._RawIOBase.__le__" => "Return self<=value.", + "_io._RawIOBase.__lt__" => "Return self "Return self!=value.", + "_io._RawIOBase.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._RawIOBase.__next__" => "Implement next(self).", + "_io._RawIOBase.__reduce__" => "Helper for pickle.", + "_io._RawIOBase.__reduce_ex__" => "Helper for pickle.", + "_io._RawIOBase.__repr__" => "Return repr(self).", + "_io._RawIOBase.__setattr__" => "Implement setattr(self, name, value).", + "_io._RawIOBase.__sizeof__" => "Size of object in memory, in bytes.", + "_io._RawIOBase.__str__" => "Return str(self).", + "_io._RawIOBase.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._RawIOBase.close" => "Flush and close the IO object.\n\nThis method has no effect if the file is already closed.", + "_io._RawIOBase.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io._RawIOBase.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io._RawIOBase.isatty" => "Return whether this is an 'interactive' stream.\n\nReturn False if it can't be determined.", + "_io._RawIOBase.readable" => "Return whether object was opened for reading.\n\nIf False, read() will raise OSError.", + "_io._RawIOBase.readall" => "Read until EOF, using multiple read() call.", + "_io._RawIOBase.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io._RawIOBase.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io._RawIOBase.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io._RawIOBase.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io._RawIOBase.tell" => "Return current stream position.", + "_io._RawIOBase.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io._RawIOBase.writable" => "Return whether object was opened for writing.\n\nIf False, write() will raise OSError.", + "_io._RawIOBase.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io._TextIOBase" => "Base class for text I/O.\n\nThis class provides a character and line based interface to stream\nI/O. There is no readinto method because Python's character strings\nare immutable.", + "_io._TextIOBase.__del__" => "Called when the instance is about to be destroyed.", + "_io._TextIOBase.__delattr__" => "Implement delattr(self, name).", + "_io._TextIOBase.__eq__" => "Return self==value.", + "_io._TextIOBase.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._TextIOBase.__ge__" => "Return self>=value.", + "_io._TextIOBase.__getattribute__" => "Return getattr(self, name).", + "_io._TextIOBase.__getstate__" => "Helper for pickle.", + "_io._TextIOBase.__gt__" => "Return self>value.", + "_io._TextIOBase.__hash__" => "Return hash(self).", + "_io._TextIOBase.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._TextIOBase.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._TextIOBase.__iter__" => "Implement iter(self).", + "_io._TextIOBase.__le__" => "Return self<=value.", + "_io._TextIOBase.__lt__" => "Return self "Return self!=value.", + "_io._TextIOBase.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._TextIOBase.__next__" => "Implement next(self).", + "_io._TextIOBase.__reduce__" => "Helper for pickle.", + "_io._TextIOBase.__reduce_ex__" => "Helper for pickle.", + "_io._TextIOBase.__repr__" => "Return repr(self).", + "_io._TextIOBase.__setattr__" => "Implement setattr(self, name, value).", + "_io._TextIOBase.__sizeof__" => "Size of object in memory, in bytes.", + "_io._TextIOBase.__str__" => "Return str(self).", + "_io._TextIOBase.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._TextIOBase.close" => "Flush and close the IO object.\n\nThis method has no effect if the file is already closed.", + "_io._TextIOBase.detach" => "Separate the underlying buffer from the TextIOBase and return it.\n\nAfter the underlying buffer has been detached, the TextIO is in an unusable state.", + "_io._TextIOBase.encoding" => "Encoding of the text stream.\n\nSubclasses should override.", + "_io._TextIOBase.errors" => "The error setting of the decoder or encoder.\n\nSubclasses should override.", + "_io._TextIOBase.fileno" => "Return underlying file descriptor if one exists.\n\nRaise OSError if the IO object does not use a file descriptor.", + "_io._TextIOBase.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io._TextIOBase.isatty" => "Return whether this is an 'interactive' stream.\n\nReturn False if it can't be determined.", + "_io._TextIOBase.newlines" => "Line endings translated so far.\n\nOnly line endings translated during reading are considered.\n\nSubclasses should override.", + "_io._TextIOBase.read" => "Read at most size characters from stream.\n\nRead from underlying buffer until we have size characters or we hit EOF.\nIf size is negative or omitted, read until EOF.", + "_io._TextIOBase.readable" => "Return whether object was opened for reading.\n\nIf False, read() will raise OSError.", + "_io._TextIOBase.readline" => "Read until newline or EOF.\n\nReturn an empty string if EOF is hit immediately.\nIf size is specified, at most size characters will be read.", + "_io._TextIOBase.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io._TextIOBase.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io._TextIOBase.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io._TextIOBase.tell" => "Return current stream position.", + "_io._TextIOBase.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io._TextIOBase.writable" => "Return whether object was opened for writing.\n\nIf False, write() will raise OSError.", + "_io._TextIOBase.write" => "Write string s to stream.\n\nReturn the number of characters written\n(which is always equal to the length of the string).", + "_io._TextIOBase.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io._WindowsConsoleIO" => "Open a console buffer by file descriptor.\n\nThe mode can be 'rb' (default), or 'wb' for reading or writing bytes. All\nother mode characters will be ignored. Mode 'b' will be assumed if it is\nomitted. The *opener* parameter is always ignored.", + "_io._WindowsConsoleIO.__del__" => "Called when the instance is about to be destroyed.", + "_io._WindowsConsoleIO.__delattr__" => "Implement delattr(self, name).", + "_io._WindowsConsoleIO.__eq__" => "Return self==value.", + "_io._WindowsConsoleIO.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_io._WindowsConsoleIO.__ge__" => "Return self>=value.", + "_io._WindowsConsoleIO.__getattribute__" => "Return getattr(self, name).", + "_io._WindowsConsoleIO.__getstate__" => "Helper for pickle.", + "_io._WindowsConsoleIO.__gt__" => "Return self>value.", + "_io._WindowsConsoleIO.__hash__" => "Return hash(self).", + "_io._WindowsConsoleIO.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_io._WindowsConsoleIO.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_io._WindowsConsoleIO.__iter__" => "Implement iter(self).", + "_io._WindowsConsoleIO.__le__" => "Return self<=value.", + "_io._WindowsConsoleIO.__lt__" => "Return self "Return self!=value.", + "_io._WindowsConsoleIO.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_io._WindowsConsoleIO.__next__" => "Implement next(self).", + "_io._WindowsConsoleIO.__reduce__" => "Helper for pickle.", + "_io._WindowsConsoleIO.__reduce_ex__" => "Helper for pickle.", + "_io._WindowsConsoleIO.__repr__" => "Return repr(self).", + "_io._WindowsConsoleIO.__setattr__" => "Implement setattr(self, name, value).", + "_io._WindowsConsoleIO.__sizeof__" => "Size of object in memory, in bytes.", + "_io._WindowsConsoleIO.__str__" => "Return str(self).", + "_io._WindowsConsoleIO.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_io._WindowsConsoleIO.close" => "Close the console object.\n\nA closed console object cannot be used for further I/O operations.\nclose() may be called more than once without error.", + "_io._WindowsConsoleIO.closed" => "True if the file is closed", + "_io._WindowsConsoleIO.closefd" => "True if the file descriptor will be closed by close().", + "_io._WindowsConsoleIO.fileno" => "Return the underlying file descriptor (an integer).", + "_io._WindowsConsoleIO.flush" => "Flush write buffers, if applicable.\n\nThis is not implemented for read-only and non-blocking streams.", + "_io._WindowsConsoleIO.isatty" => "Always True.", + "_io._WindowsConsoleIO.mode" => "String giving the file mode", + "_io._WindowsConsoleIO.read" => "Read at most size bytes, returned as bytes.\n\nOnly makes one system call when size is a positive integer,\nso less data may be returned than requested.\nReturn an empty bytes object at EOF.", + "_io._WindowsConsoleIO.readable" => "True if console is an input buffer.", + "_io._WindowsConsoleIO.readall" => "Read all data from the console, returned as bytes.\n\nReturn an empty bytes object at EOF.", + "_io._WindowsConsoleIO.readinto" => "Same as RawIOBase.readinto().", + "_io._WindowsConsoleIO.readline" => "Read and return a line from the stream.\n\nIf size is specified, at most size bytes will be read.\n\nThe line terminator is always b'\\n' for binary files; for text\nfiles, the newlines argument to open can be used to select the line\nterminator(s) recognized.", + "_io._WindowsConsoleIO.readlines" => "Return a list of lines from the stream.\n\nhint can be specified to control the number of lines read: no more\nlines will be read if the total size (in bytes/characters) of all\nlines so far exceeds hint.", + "_io._WindowsConsoleIO.seek" => "Change the stream position to the given byte offset.\n\n offset\n The stream position, relative to 'whence'.\n whence\n The relative position to seek from.\n\nThe offset is interpreted relative to the position indicated by whence.\nValues for whence are:\n\n* os.SEEK_SET or 0 -- start of stream (the default); offset should be zero or positive\n* os.SEEK_CUR or 1 -- current stream position; offset may be negative\n* os.SEEK_END or 2 -- end of stream; offset is usually negative\n\nReturn the new absolute position.", + "_io._WindowsConsoleIO.seekable" => "Return whether object supports random access.\n\nIf False, seek(), tell() and truncate() will raise OSError.\nThis method may need to do a test seek().", + "_io._WindowsConsoleIO.tell" => "Return current stream position.", + "_io._WindowsConsoleIO.truncate" => "Truncate file to size bytes.\n\nFile pointer is left unchanged. Size defaults to the current IO position\nas reported by tell(). Return the new size.", + "_io._WindowsConsoleIO.writable" => "True if console is an output buffer.", + "_io._WindowsConsoleIO.write" => "Write buffer b to file, return number of bytes written.\n\nOnly makes one system call, so not all of the data may be written.\nThe number of bytes actually written is returned.", + "_io._WindowsConsoleIO.writelines" => "Write a list of lines to stream.\n\nLine separators are not added, so it is usual for each of the\nlines provided to have a line separator at the end.", + "_io.open" => "Open file and return a stream. Raise OSError upon failure.\n\nfile is either a text or byte string giving the name (and the path\nif the file isn't in the current working directory) of the file to\nbe opened or an integer file descriptor of the file to be\nwrapped. (If a file descriptor is given, it is closed when the\nreturned I/O object is closed, unless closefd is set to False.)\n\nmode is an optional string that specifies the mode in which the file\nis opened. It defaults to 'r' which means open for reading in text\nmode. Other common values are 'w' for writing (truncating the file if\nit already exists), 'x' for creating and writing to a new file, and\n'a' for appending (which on some Unix systems, means that all writes\nappend to the end of the file regardless of the current seek position).\nIn text mode, if encoding is not specified the encoding used is platform\ndependent: locale.getencoding() is called to get the current locale encoding.\n(For reading and writing raw bytes use binary mode and leave encoding\nunspecified.) The available modes are:\n\n========= ===============================================================\nCharacter Meaning\n--------- ---------------------------------------------------------------\n'r' open for reading (default)\n'w' open for writing, truncating the file first\n'x' create a new file and open it for writing\n'a' open for writing, appending to the end of the file if it exists\n'b' binary mode\n't' text mode (default)\n'+' open a disk file for updating (reading and writing)\n========= ===============================================================\n\nThe default mode is 'rt' (open for reading text). For binary random\naccess, the mode 'w+b' opens and truncates the file to 0 bytes, while\n'r+b' opens the file without truncation. The 'x' mode implies 'w' and\nraises an `FileExistsError` if the file already exists.\n\nPython distinguishes between files opened in binary and text modes,\neven when the underlying operating system doesn't. Files opened in\nbinary mode (appending 'b' to the mode argument) return contents as\nbytes objects without any decoding. In text mode (the default, or when\n't' is appended to the mode argument), the contents of the file are\nreturned as strings, the bytes having been first decoded using a\nplatform-dependent encoding or using the specified encoding if given.\n\nbuffering is an optional integer used to set the buffering policy.\nPass 0 to switch buffering off (only allowed in binary mode), 1 to select\nline buffering (only usable in text mode), and an integer > 1 to indicate\nthe size of a fixed-size chunk buffer. When no buffering argument is\ngiven, the default buffering policy works as follows:\n\n* Binary files are buffered in fixed-size chunks; the size of the buffer\n is chosen using a heuristic trying to determine the underlying device's\n \"block size\" and falling back on `io.DEFAULT_BUFFER_SIZE`.\n On many systems, the buffer will typically be 4096 or 8192 bytes long.\n\n* \"Interactive\" text files (files for which isatty() returns True)\n use line buffering. Other text files use the policy described above\n for binary files.\n\nencoding is the name of the encoding used to decode or encode the\nfile. This should only be used in text mode. The default encoding is\nplatform dependent, but any encoding supported by Python can be\npassed. See the codecs module for the list of supported encodings.\n\nerrors is an optional string that specifies how encoding errors are to\nbe handled---this argument should not be used in binary mode. Pass\n'strict' to raise a ValueError exception if there is an encoding error\n(the default of None has the same effect), or pass 'ignore' to ignore\nerrors. (Note that ignoring encoding errors can lead to data loss.)\nSee the documentation for codecs.register or run 'help(codecs.Codec)'\nfor a list of the permitted encoding error strings.\n\nnewline controls how universal newlines works (it only applies to text\nmode). It can be None, '', '\\n', '\\r', and '\\r\\n'. It works as\nfollows:\n\n* On input, if newline is None, universal newlines mode is\n enabled. Lines in the input can end in '\\n', '\\r', or '\\r\\n', and\n these are translated into '\\n' before being returned to the\n caller. If it is '', universal newline mode is enabled, but line\n endings are returned to the caller untranslated. If it has any of\n the other legal values, input lines are only terminated by the given\n string, and the line ending is returned to the caller untranslated.\n\n* On output, if newline is None, any '\\n' characters written are\n translated to the system default line separator, os.linesep. If\n newline is '' or '\\n', no translation takes place. If newline is any\n of the other legal values, any '\\n' characters written are translated\n to the given string.\n\nIf closefd is False, the underlying file descriptor will be kept open\nwhen the file is closed. This does not work when a file name is given\nand must be True in that case.\n\nA custom opener can be used by passing a callable as *opener*. The\nunderlying file descriptor for the file object is then obtained by\ncalling *opener* with (*file*, *flags*). *opener* must return an open\nfile descriptor (passing os.open as *opener* results in functionality\nsimilar to passing None).\n\nopen() returns a file object whose type depends on the mode, and\nthrough which the standard file operations such as reading and writing\nare performed. When open() is used to open a file in a text mode ('w',\n'r', 'wt', 'rt', etc.), it returns a TextIOWrapper. When used to open\na file in a binary mode, the returned class varies: in read binary\nmode, it returns a BufferedReader; in write binary and append binary\nmodes, it returns a BufferedWriter, and in read/write mode, it returns\na BufferedRandom.\n\nIt is also possible to use a string or bytearray as a file for both\nreading and writing. For strings StringIO can be used like a file\nopened in a text mode, and for bytes a BytesIO can be used like a file\nopened in a binary mode.", + "_io.open_code" => "Opens the provided file with the intent to import the contents.\n\nThis may perform extra validation beyond open(), but is otherwise interchangeable\nwith calling open(path, 'rb').", + "_io.text_encoding" => "A helper function to choose the text encoding.\n\nWhen encoding is not None, this function returns it.\nOtherwise, this function returns the default text encoding\n(i.e. \"locale\" or \"utf-8\" depends on UTF-8 mode).\n\nThis function emits an EncodingWarning if encoding is None and\nsys.flags.warn_default_encoding is true.\n\nThis can be used in APIs with an encoding=None parameter.\nHowever, please consider using encoding=\"utf-8\" for new APIs.", + "_json" => "json speedups", + "_json.encode_basestring" => "encode_basestring(string) -> string\n\nReturn a JSON representation of a Python string", + "_json.encode_basestring_ascii" => "encode_basestring_ascii(string) -> string\n\nReturn an ASCII-only JSON representation of a Python string", + "_json.make_encoder" => "Encoder(markers, default, encoder, indent, key_separator, item_separator, sort_keys, skipkeys, allow_nan)", + "_json.make_encoder.__call__" => "Call self as a function.", + "_json.make_encoder.__delattr__" => "Implement delattr(self, name).", + "_json.make_encoder.__eq__" => "Return self==value.", + "_json.make_encoder.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_json.make_encoder.__ge__" => "Return self>=value.", + "_json.make_encoder.__getattribute__" => "Return getattr(self, name).", + "_json.make_encoder.__getstate__" => "Helper for pickle.", + "_json.make_encoder.__gt__" => "Return self>value.", + "_json.make_encoder.__hash__" => "Return hash(self).", + "_json.make_encoder.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_json.make_encoder.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_json.make_encoder.__le__" => "Return self<=value.", + "_json.make_encoder.__lt__" => "Return self "Return self!=value.", + "_json.make_encoder.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_json.make_encoder.__reduce__" => "Helper for pickle.", + "_json.make_encoder.__reduce_ex__" => "Helper for pickle.", + "_json.make_encoder.__repr__" => "Return repr(self).", + "_json.make_encoder.__setattr__" => "Implement setattr(self, name, value).", + "_json.make_encoder.__sizeof__" => "Size of object in memory, in bytes.", + "_json.make_encoder.__str__" => "Return str(self).", + "_json.make_encoder.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_json.make_encoder.default" => "default", + "_json.make_encoder.encoder" => "encoder", + "_json.make_encoder.indent" => "indent", + "_json.make_encoder.item_separator" => "item_separator", + "_json.make_encoder.key_separator" => "key_separator", + "_json.make_encoder.markers" => "markers", + "_json.make_encoder.skipkeys" => "skipkeys", + "_json.make_encoder.sort_keys" => "sort_keys", + "_json.make_scanner" => "JSON scanner object", + "_json.make_scanner.__call__" => "Call self as a function.", + "_json.make_scanner.__delattr__" => "Implement delattr(self, name).", + "_json.make_scanner.__eq__" => "Return self==value.", + "_json.make_scanner.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_json.make_scanner.__ge__" => "Return self>=value.", + "_json.make_scanner.__getattribute__" => "Return getattr(self, name).", + "_json.make_scanner.__getstate__" => "Helper for pickle.", + "_json.make_scanner.__gt__" => "Return self>value.", + "_json.make_scanner.__hash__" => "Return hash(self).", + "_json.make_scanner.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_json.make_scanner.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_json.make_scanner.__le__" => "Return self<=value.", + "_json.make_scanner.__lt__" => "Return self "Return self!=value.", + "_json.make_scanner.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_json.make_scanner.__reduce__" => "Helper for pickle.", + "_json.make_scanner.__reduce_ex__" => "Helper for pickle.", + "_json.make_scanner.__repr__" => "Return repr(self).", + "_json.make_scanner.__setattr__" => "Implement setattr(self, name, value).", + "_json.make_scanner.__sizeof__" => "Size of object in memory, in bytes.", + "_json.make_scanner.__str__" => "Return str(self).", + "_json.make_scanner.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_json.make_scanner.object_hook" => "object_hook", + "_json.make_scanner.parse_constant" => "parse_constant", + "_json.make_scanner.parse_float" => "parse_float", + "_json.make_scanner.parse_int" => "parse_int", + "_json.make_scanner.strict" => "strict", + "_json.scanstring" => "scanstring(string, end, strict=True) -> (string, end)\n\nScan the string s for a JSON string. End is the index of the\ncharacter in s after the quote that started the JSON string.\nUnescapes all valid JSON string escape sequences and raises ValueError\non attempt to decode an invalid string. If strict is False then literal\ncontrol characters are allowed in the string.\n\nReturns a tuple of the decoded string and the index of the character in s\nafter the end quote.", + "_locale" => "Support for POSIX locales.", + "_locale.bind_textdomain_codeset" => "Bind the C library's domain to codeset.", + "_locale.bindtextdomain" => "Bind the C library's domain to dir.", + "_locale.dcgettext" => "Return translation of msg in domain and category.", + "_locale.dgettext" => "dgettext(domain, msg) -> string\n\nReturn translation of msg in domain.", + "_locale.getencoding" => "Get the current locale encoding.", + "_locale.gettext" => "gettext(msg) -> string\n\nReturn translation of msg.", + "_locale.localeconv" => "Returns numeric and monetary locale-specific parameters.", + "_locale.nl_langinfo" => "Return the value for the locale information associated with key.", + "_locale.setlocale" => "Activates/queries locale processing.", + "_locale.strcoll" => "Compares two strings according to the locale.", + "_locale.strxfrm" => "Return a string that can be used as a key for locale-aware comparisons.", + "_locale.textdomain" => "Set the C library's textdmain to domain, returning the new domain.", + "_lsprof" => "Fast profiler", + "_lsprof.Profiler" => "Profiler(timer=None, timeunit=None, subcalls=True, builtins=True)\n\nBuilds a profiler object using the specified timer function.\nThe default timer is a fast built-in one based on real time.\nFor custom timer functions returning integers, timeunit can\nbe a float specifying a scale (i.e. how long each integer unit\nis, in seconds).", + "_lsprof.Profiler.__delattr__" => "Implement delattr(self, name).", + "_lsprof.Profiler.__eq__" => "Return self==value.", + "_lsprof.Profiler.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lsprof.Profiler.__ge__" => "Return self>=value.", + "_lsprof.Profiler.__getattribute__" => "Return getattr(self, name).", + "_lsprof.Profiler.__getstate__" => "Helper for pickle.", + "_lsprof.Profiler.__gt__" => "Return self>value.", + "_lsprof.Profiler.__hash__" => "Return hash(self).", + "_lsprof.Profiler.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lsprof.Profiler.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lsprof.Profiler.__le__" => "Return self<=value.", + "_lsprof.Profiler.__lt__" => "Return self "Return self!=value.", + "_lsprof.Profiler.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lsprof.Profiler.__reduce__" => "Helper for pickle.", + "_lsprof.Profiler.__reduce_ex__" => "Helper for pickle.", + "_lsprof.Profiler.__repr__" => "Return repr(self).", + "_lsprof.Profiler.__setattr__" => "Implement setattr(self, name, value).", + "_lsprof.Profiler.__sizeof__" => "Size of object in memory, in bytes.", + "_lsprof.Profiler.__str__" => "Return str(self).", + "_lsprof.Profiler.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lsprof.Profiler.clear" => "clear()\n\nClear all profiling information collected so far.", + "_lsprof.Profiler.disable" => "disable()\n\nStop collecting profiling information.", + "_lsprof.Profiler.enable" => "enable(subcalls=True, builtins=True)\n\nStart collecting profiling information.\nIf 'subcalls' is True, also records for each function\nstatistics separated according to its current caller.\nIf 'builtins' is True, records the time spent in\nbuilt-in functions separately from their caller.", + "_lsprof.Profiler.getstats" => "list of profiler_entry objects.\n\ngetstats() -> list of profiler_entry objects\n\nReturn all information collected by the profiler.\nEach profiler_entry is a tuple-like object with the\nfollowing attributes:\n\n code code object\n callcount how many times this was called\n reccallcount how many times called recursively\n totaltime total time in this entry\n inlinetime inline time in this entry (not in subcalls)\n calls details of the calls\n\nThe calls attribute is either None or a list of\nprofiler_subentry objects:\n\n code called code object\n callcount how many times this is called\n reccallcount how many times this is called recursively\n totaltime total time spent in this call\n inlinetime inline time (not in further subcalls)", + "_lsprof.profiler_entry.__add__" => "Return self+value.", + "_lsprof.profiler_entry.__class_getitem__" => "See PEP 585", + "_lsprof.profiler_entry.__contains__" => "Return bool(key in self).", + "_lsprof.profiler_entry.__delattr__" => "Implement delattr(self, name).", + "_lsprof.profiler_entry.__eq__" => "Return self==value.", + "_lsprof.profiler_entry.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lsprof.profiler_entry.__ge__" => "Return self>=value.", + "_lsprof.profiler_entry.__getattribute__" => "Return getattr(self, name).", + "_lsprof.profiler_entry.__getitem__" => "Return self[key].", + "_lsprof.profiler_entry.__getstate__" => "Helper for pickle.", + "_lsprof.profiler_entry.__gt__" => "Return self>value.", + "_lsprof.profiler_entry.__hash__" => "Return hash(self).", + "_lsprof.profiler_entry.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lsprof.profiler_entry.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lsprof.profiler_entry.__iter__" => "Implement iter(self).", + "_lsprof.profiler_entry.__le__" => "Return self<=value.", + "_lsprof.profiler_entry.__len__" => "Return len(self).", + "_lsprof.profiler_entry.__lt__" => "Return self "Return self*value.", + "_lsprof.profiler_entry.__ne__" => "Return self!=value.", + "_lsprof.profiler_entry.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lsprof.profiler_entry.__reduce_ex__" => "Helper for pickle.", + "_lsprof.profiler_entry.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "_lsprof.profiler_entry.__repr__" => "Return repr(self).", + "_lsprof.profiler_entry.__rmul__" => "Return value*self.", + "_lsprof.profiler_entry.__setattr__" => "Implement setattr(self, name, value).", + "_lsprof.profiler_entry.__sizeof__" => "Size of object in memory, in bytes.", + "_lsprof.profiler_entry.__str__" => "Return str(self).", + "_lsprof.profiler_entry.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lsprof.profiler_entry.callcount" => "how many times this was called", + "_lsprof.profiler_entry.calls" => "details of the calls", + "_lsprof.profiler_entry.code" => "code object or built-in function name", + "_lsprof.profiler_entry.count" => "Return number of occurrences of value.", + "_lsprof.profiler_entry.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "_lsprof.profiler_entry.inlinetime" => "inline time in this entry (not in subcalls)", + "_lsprof.profiler_entry.reccallcount" => "how many times called recursively", + "_lsprof.profiler_entry.totaltime" => "total time in this entry", + "_lsprof.profiler_subentry.__add__" => "Return self+value.", + "_lsprof.profiler_subentry.__class_getitem__" => "See PEP 585", + "_lsprof.profiler_subentry.__contains__" => "Return bool(key in self).", + "_lsprof.profiler_subentry.__delattr__" => "Implement delattr(self, name).", + "_lsprof.profiler_subentry.__eq__" => "Return self==value.", + "_lsprof.profiler_subentry.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lsprof.profiler_subentry.__ge__" => "Return self>=value.", + "_lsprof.profiler_subentry.__getattribute__" => "Return getattr(self, name).", + "_lsprof.profiler_subentry.__getitem__" => "Return self[key].", + "_lsprof.profiler_subentry.__getstate__" => "Helper for pickle.", + "_lsprof.profiler_subentry.__gt__" => "Return self>value.", + "_lsprof.profiler_subentry.__hash__" => "Return hash(self).", + "_lsprof.profiler_subentry.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lsprof.profiler_subentry.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lsprof.profiler_subentry.__iter__" => "Implement iter(self).", + "_lsprof.profiler_subentry.__le__" => "Return self<=value.", + "_lsprof.profiler_subentry.__len__" => "Return len(self).", + "_lsprof.profiler_subentry.__lt__" => "Return self "Return self*value.", + "_lsprof.profiler_subentry.__ne__" => "Return self!=value.", + "_lsprof.profiler_subentry.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lsprof.profiler_subentry.__reduce_ex__" => "Helper for pickle.", + "_lsprof.profiler_subentry.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "_lsprof.profiler_subentry.__repr__" => "Return repr(self).", + "_lsprof.profiler_subentry.__rmul__" => "Return value*self.", + "_lsprof.profiler_subentry.__setattr__" => "Implement setattr(self, name, value).", + "_lsprof.profiler_subentry.__sizeof__" => "Size of object in memory, in bytes.", + "_lsprof.profiler_subentry.__str__" => "Return str(self).", + "_lsprof.profiler_subentry.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lsprof.profiler_subentry.callcount" => "how many times this is called", + "_lsprof.profiler_subentry.code" => "called code object or built-in function name", + "_lsprof.profiler_subentry.count" => "Return number of occurrences of value.", + "_lsprof.profiler_subentry.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "_lsprof.profiler_subentry.inlinetime" => "inline time (not in further subcalls)", + "_lsprof.profiler_subentry.reccallcount" => "how many times this is called recursively", + "_lsprof.profiler_subentry.totaltime" => "total time spent in this call", + "_lzma.LZMACompressor" => "LZMACompressor(format=FORMAT_XZ, check=-1, preset=None, filters=None)\n\nCreate a compressor object for compressing data incrementally.\n\nformat specifies the container format to use for the output. This can\nbe FORMAT_XZ (default), FORMAT_ALONE, or FORMAT_RAW.\n\ncheck specifies the integrity check to use. For FORMAT_XZ, the default\nis CHECK_CRC64. FORMAT_ALONE and FORMAT_RAW do not support integrity\nchecks; for these formats, check must be omitted, or be CHECK_NONE.\n\nThe settings used by the compressor can be specified either as a\npreset compression level (with the 'preset' argument), or in detail\nas a custom filter chain (with the 'filters' argument). For FORMAT_XZ\nand FORMAT_ALONE, the default is to use the PRESET_DEFAULT preset\nlevel. For FORMAT_RAW, the caller must always specify a filter chain;\nthe raw compressor does not support preset compression levels.\n\npreset (if provided) should be an integer in the range 0-9, optionally\nOR-ed with the constant PRESET_EXTREME.\n\nfilters (if provided) should be a sequence of dicts. Each dict should\nhave an entry for \"id\" indicating the ID of the filter, plus\nadditional entries for options to the filter.\n\nFor one-shot compression, use the compress() function instead.", + "_lzma.LZMACompressor.__delattr__" => "Implement delattr(self, name).", + "_lzma.LZMACompressor.__eq__" => "Return self==value.", + "_lzma.LZMACompressor.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lzma.LZMACompressor.__ge__" => "Return self>=value.", + "_lzma.LZMACompressor.__getattribute__" => "Return getattr(self, name).", + "_lzma.LZMACompressor.__getstate__" => "Helper for pickle.", + "_lzma.LZMACompressor.__gt__" => "Return self>value.", + "_lzma.LZMACompressor.__hash__" => "Return hash(self).", + "_lzma.LZMACompressor.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lzma.LZMACompressor.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lzma.LZMACompressor.__le__" => "Return self<=value.", + "_lzma.LZMACompressor.__lt__" => "Return self "Return self!=value.", + "_lzma.LZMACompressor.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lzma.LZMACompressor.__reduce__" => "Helper for pickle.", + "_lzma.LZMACompressor.__reduce_ex__" => "Helper for pickle.", + "_lzma.LZMACompressor.__repr__" => "Return repr(self).", + "_lzma.LZMACompressor.__setattr__" => "Implement setattr(self, name, value).", + "_lzma.LZMACompressor.__sizeof__" => "Size of object in memory, in bytes.", + "_lzma.LZMACompressor.__str__" => "Return str(self).", + "_lzma.LZMACompressor.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lzma.LZMACompressor.compress" => "Provide data to the compressor object.\n\nReturns a chunk of compressed data if possible, or b'' otherwise.\n\nWhen you have finished providing data to the compressor, call the\nflush() method to finish the compression process.", + "_lzma.LZMACompressor.flush" => "Finish the compression process.\n\nReturns the compressed data left in internal buffers.\n\nThe compressor object may not be used after this method is called.", + "_lzma.LZMADecompressor" => "Create a decompressor object for decompressing data incrementally.\n\n format\n Specifies the container format of the input stream. If this is\n FORMAT_AUTO (the default), the decompressor will automatically detect\n whether the input is FORMAT_XZ or FORMAT_ALONE. Streams created with\n FORMAT_RAW cannot be autodetected.\n memlimit\n Limit the amount of memory used by the decompressor. This will cause\n decompression to fail if the input cannot be decompressed within the\n given limit.\n filters\n A custom filter chain. This argument is required for FORMAT_RAW, and\n not accepted with any other format. When provided, this should be a\n sequence of dicts, each indicating the ID and options for a single\n filter.\n\nFor one-shot decompression, use the decompress() function instead.", + "_lzma.LZMADecompressor.__delattr__" => "Implement delattr(self, name).", + "_lzma.LZMADecompressor.__eq__" => "Return self==value.", + "_lzma.LZMADecompressor.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lzma.LZMADecompressor.__ge__" => "Return self>=value.", + "_lzma.LZMADecompressor.__getattribute__" => "Return getattr(self, name).", + "_lzma.LZMADecompressor.__getstate__" => "Helper for pickle.", + "_lzma.LZMADecompressor.__gt__" => "Return self>value.", + "_lzma.LZMADecompressor.__hash__" => "Return hash(self).", + "_lzma.LZMADecompressor.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lzma.LZMADecompressor.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lzma.LZMADecompressor.__le__" => "Return self<=value.", + "_lzma.LZMADecompressor.__lt__" => "Return self "Return self!=value.", + "_lzma.LZMADecompressor.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lzma.LZMADecompressor.__reduce__" => "Helper for pickle.", + "_lzma.LZMADecompressor.__reduce_ex__" => "Helper for pickle.", + "_lzma.LZMADecompressor.__repr__" => "Return repr(self).", + "_lzma.LZMADecompressor.__setattr__" => "Implement setattr(self, name, value).", + "_lzma.LZMADecompressor.__sizeof__" => "Size of object in memory, in bytes.", + "_lzma.LZMADecompressor.__str__" => "Return str(self).", + "_lzma.LZMADecompressor.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lzma.LZMADecompressor.check" => "ID of the integrity check used by the input stream.", + "_lzma.LZMADecompressor.decompress" => "Decompress *data*, returning uncompressed data as bytes.\n\nIf *max_length* is nonnegative, returns at most *max_length* bytes of\ndecompressed data. If this limit is reached and further output can be\nproduced, *self.needs_input* will be set to ``False``. In this case, the next\ncall to *decompress()* may provide *data* as b'' to obtain more of the output.\n\nIf all of the input data was decompressed and returned (either because this\nwas less than *max_length* bytes, or because *max_length* was negative),\n*self.needs_input* will be set to True.\n\nAttempting to decompress data after the end of stream is reached raises an\nEOFError. Any data found after the end of the stream is ignored and saved in\nthe unused_data attribute.", + "_lzma.LZMADecompressor.eof" => "True if the end-of-stream marker has been reached.", + "_lzma.LZMADecompressor.needs_input" => "True if more input is needed before more decompressed data can be produced.", + "_lzma.LZMADecompressor.unused_data" => "Data found after the end of the compressed stream.", + "_lzma.LZMAError" => "Call to liblzma failed.", + "_lzma.LZMAError.__cause__" => "exception cause", + "_lzma.LZMAError.__context__" => "exception context", + "_lzma.LZMAError.__delattr__" => "Implement delattr(self, name).", + "_lzma.LZMAError.__eq__" => "Return self==value.", + "_lzma.LZMAError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_lzma.LZMAError.__ge__" => "Return self>=value.", + "_lzma.LZMAError.__getattribute__" => "Return getattr(self, name).", + "_lzma.LZMAError.__getstate__" => "Helper for pickle.", + "_lzma.LZMAError.__gt__" => "Return self>value.", + "_lzma.LZMAError.__hash__" => "Return hash(self).", + "_lzma.LZMAError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_lzma.LZMAError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_lzma.LZMAError.__le__" => "Return self<=value.", + "_lzma.LZMAError.__lt__" => "Return self "Return self!=value.", + "_lzma.LZMAError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_lzma.LZMAError.__reduce_ex__" => "Helper for pickle.", + "_lzma.LZMAError.__repr__" => "Return repr(self).", + "_lzma.LZMAError.__setattr__" => "Implement setattr(self, name, value).", + "_lzma.LZMAError.__sizeof__" => "Size of object in memory, in bytes.", + "_lzma.LZMAError.__str__" => "Return str(self).", + "_lzma.LZMAError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_lzma.LZMAError.__weakref__" => "list of weak references to the object", + "_lzma.LZMAError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_lzma.LZMAError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_lzma._decode_filter_properties" => "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n\nThe result does not include the filter ID itself, only the options.", + "_lzma._encode_filter_properties" => "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n\nThe result does not include the filter ID itself, only the options.", + "_lzma.is_check_supported" => "Test whether the given integrity check is supported.\n\nAlways returns True for CHECK_NONE and CHECK_CRC32.", + "_md5.MD5Type.__delattr__" => "Implement delattr(self, name).", + "_md5.MD5Type.__eq__" => "Return self==value.", + "_md5.MD5Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_md5.MD5Type.__ge__" => "Return self>=value.", + "_md5.MD5Type.__getattribute__" => "Return getattr(self, name).", + "_md5.MD5Type.__getstate__" => "Helper for pickle.", + "_md5.MD5Type.__gt__" => "Return self>value.", + "_md5.MD5Type.__hash__" => "Return hash(self).", + "_md5.MD5Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_md5.MD5Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_md5.MD5Type.__le__" => "Return self<=value.", + "_md5.MD5Type.__lt__" => "Return self "Return self!=value.", + "_md5.MD5Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_md5.MD5Type.__reduce__" => "Helper for pickle.", + "_md5.MD5Type.__reduce_ex__" => "Helper for pickle.", + "_md5.MD5Type.__repr__" => "Return repr(self).", + "_md5.MD5Type.__setattr__" => "Implement setattr(self, name, value).", + "_md5.MD5Type.__sizeof__" => "Size of object in memory, in bytes.", + "_md5.MD5Type.__str__" => "Return str(self).", + "_md5.MD5Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_md5.MD5Type.copy" => "Return a copy of the hash object.", + "_md5.MD5Type.digest" => "Return the digest value as a bytes object.", + "_md5.MD5Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_md5.MD5Type.update" => "Update this hash object's state with the provided string.", + "_md5.md5" => "Return a new MD5 hash object; optionally initialized with a string.", + "_multibytecodec.MultibyteIncrementalDecoder.__delattr__" => "Implement delattr(self, name).", + "_multibytecodec.MultibyteIncrementalDecoder.__eq__" => "Return self==value.", + "_multibytecodec.MultibyteIncrementalDecoder.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_multibytecodec.MultibyteIncrementalDecoder.__ge__" => "Return self>=value.", + "_multibytecodec.MultibyteIncrementalDecoder.__getattribute__" => "Return getattr(self, name).", + "_multibytecodec.MultibyteIncrementalDecoder.__getstate__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalDecoder.__gt__" => "Return self>value.", + "_multibytecodec.MultibyteIncrementalDecoder.__hash__" => "Return hash(self).", + "_multibytecodec.MultibyteIncrementalDecoder.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_multibytecodec.MultibyteIncrementalDecoder.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_multibytecodec.MultibyteIncrementalDecoder.__le__" => "Return self<=value.", + "_multibytecodec.MultibyteIncrementalDecoder.__lt__" => "Return self "Return self!=value.", + "_multibytecodec.MultibyteIncrementalDecoder.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_multibytecodec.MultibyteIncrementalDecoder.__reduce__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalDecoder.__reduce_ex__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalDecoder.__repr__" => "Return repr(self).", + "_multibytecodec.MultibyteIncrementalDecoder.__setattr__" => "Implement setattr(self, name, value).", + "_multibytecodec.MultibyteIncrementalDecoder.__sizeof__" => "Size of object in memory, in bytes.", + "_multibytecodec.MultibyteIncrementalDecoder.__str__" => "Return str(self).", + "_multibytecodec.MultibyteIncrementalDecoder.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_multibytecodec.MultibyteIncrementalDecoder.errors" => "how to treat errors", + "_multibytecodec.MultibyteIncrementalEncoder.__delattr__" => "Implement delattr(self, name).", + "_multibytecodec.MultibyteIncrementalEncoder.__eq__" => "Return self==value.", + "_multibytecodec.MultibyteIncrementalEncoder.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_multibytecodec.MultibyteIncrementalEncoder.__ge__" => "Return self>=value.", + "_multibytecodec.MultibyteIncrementalEncoder.__getattribute__" => "Return getattr(self, name).", + "_multibytecodec.MultibyteIncrementalEncoder.__getstate__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalEncoder.__gt__" => "Return self>value.", + "_multibytecodec.MultibyteIncrementalEncoder.__hash__" => "Return hash(self).", + "_multibytecodec.MultibyteIncrementalEncoder.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_multibytecodec.MultibyteIncrementalEncoder.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_multibytecodec.MultibyteIncrementalEncoder.__le__" => "Return self<=value.", + "_multibytecodec.MultibyteIncrementalEncoder.__lt__" => "Return self "Return self!=value.", + "_multibytecodec.MultibyteIncrementalEncoder.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_multibytecodec.MultibyteIncrementalEncoder.__reduce__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalEncoder.__reduce_ex__" => "Helper for pickle.", + "_multibytecodec.MultibyteIncrementalEncoder.__repr__" => "Return repr(self).", + "_multibytecodec.MultibyteIncrementalEncoder.__setattr__" => "Implement setattr(self, name, value).", + "_multibytecodec.MultibyteIncrementalEncoder.__sizeof__" => "Size of object in memory, in bytes.", + "_multibytecodec.MultibyteIncrementalEncoder.__str__" => "Return str(self).", + "_multibytecodec.MultibyteIncrementalEncoder.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_multibytecodec.MultibyteIncrementalEncoder.errors" => "how to treat errors", + "_multibytecodec.MultibyteStreamReader.__delattr__" => "Implement delattr(self, name).", + "_multibytecodec.MultibyteStreamReader.__eq__" => "Return self==value.", + "_multibytecodec.MultibyteStreamReader.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_multibytecodec.MultibyteStreamReader.__ge__" => "Return self>=value.", + "_multibytecodec.MultibyteStreamReader.__getattribute__" => "Return getattr(self, name).", + "_multibytecodec.MultibyteStreamReader.__getstate__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamReader.__gt__" => "Return self>value.", + "_multibytecodec.MultibyteStreamReader.__hash__" => "Return hash(self).", + "_multibytecodec.MultibyteStreamReader.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_multibytecodec.MultibyteStreamReader.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_multibytecodec.MultibyteStreamReader.__le__" => "Return self<=value.", + "_multibytecodec.MultibyteStreamReader.__lt__" => "Return self "Return self!=value.", + "_multibytecodec.MultibyteStreamReader.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_multibytecodec.MultibyteStreamReader.__reduce__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamReader.__reduce_ex__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamReader.__repr__" => "Return repr(self).", + "_multibytecodec.MultibyteStreamReader.__setattr__" => "Implement setattr(self, name, value).", + "_multibytecodec.MultibyteStreamReader.__sizeof__" => "Size of object in memory, in bytes.", + "_multibytecodec.MultibyteStreamReader.__str__" => "Return str(self).", + "_multibytecodec.MultibyteStreamReader.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_multibytecodec.MultibyteStreamReader.errors" => "how to treat errors", + "_multibytecodec.MultibyteStreamWriter.__delattr__" => "Implement delattr(self, name).", + "_multibytecodec.MultibyteStreamWriter.__eq__" => "Return self==value.", + "_multibytecodec.MultibyteStreamWriter.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_multibytecodec.MultibyteStreamWriter.__ge__" => "Return self>=value.", + "_multibytecodec.MultibyteStreamWriter.__getattribute__" => "Return getattr(self, name).", + "_multibytecodec.MultibyteStreamWriter.__getstate__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamWriter.__gt__" => "Return self>value.", + "_multibytecodec.MultibyteStreamWriter.__hash__" => "Return hash(self).", + "_multibytecodec.MultibyteStreamWriter.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_multibytecodec.MultibyteStreamWriter.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_multibytecodec.MultibyteStreamWriter.__le__" => "Return self<=value.", + "_multibytecodec.MultibyteStreamWriter.__lt__" => "Return self "Return self!=value.", + "_multibytecodec.MultibyteStreamWriter.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_multibytecodec.MultibyteStreamWriter.__reduce__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamWriter.__reduce_ex__" => "Helper for pickle.", + "_multibytecodec.MultibyteStreamWriter.__repr__" => "Return repr(self).", + "_multibytecodec.MultibyteStreamWriter.__setattr__" => "Implement setattr(self, name, value).", + "_multibytecodec.MultibyteStreamWriter.__sizeof__" => "Size of object in memory, in bytes.", + "_multibytecodec.MultibyteStreamWriter.__str__" => "Return str(self).", + "_multibytecodec.MultibyteStreamWriter.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_multibytecodec.MultibyteStreamWriter.errors" => "how to treat errors", + "_multiprocessing.SemLock" => "Semaphore/Mutex type", + "_multiprocessing.SemLock.__delattr__" => "Implement delattr(self, name).", + "_multiprocessing.SemLock.__enter__" => "Enter the semaphore/lock.", + "_multiprocessing.SemLock.__eq__" => "Return self==value.", + "_multiprocessing.SemLock.__exit__" => "Exit the semaphore/lock.", + "_multiprocessing.SemLock.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_multiprocessing.SemLock.__ge__" => "Return self>=value.", + "_multiprocessing.SemLock.__getattribute__" => "Return getattr(self, name).", + "_multiprocessing.SemLock.__getstate__" => "Helper for pickle.", + "_multiprocessing.SemLock.__gt__" => "Return self>value.", + "_multiprocessing.SemLock.__hash__" => "Return hash(self).", + "_multiprocessing.SemLock.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_multiprocessing.SemLock.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_multiprocessing.SemLock.__le__" => "Return self<=value.", + "_multiprocessing.SemLock.__lt__" => "Return self "Return self!=value.", + "_multiprocessing.SemLock.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_multiprocessing.SemLock.__reduce__" => "Helper for pickle.", + "_multiprocessing.SemLock.__reduce_ex__" => "Helper for pickle.", + "_multiprocessing.SemLock.__repr__" => "Return repr(self).", + "_multiprocessing.SemLock.__setattr__" => "Implement setattr(self, name, value).", + "_multiprocessing.SemLock.__sizeof__" => "Size of object in memory, in bytes.", + "_multiprocessing.SemLock.__str__" => "Return str(self).", + "_multiprocessing.SemLock.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_multiprocessing.SemLock._after_fork" => "Rezero the net acquisition count after fork().", + "_multiprocessing.SemLock._count" => "Num of `acquire()`s minus num of `release()`s for this process.", + "_multiprocessing.SemLock._get_value" => "Get the value of the semaphore.", + "_multiprocessing.SemLock._is_mine" => "Whether the lock is owned by this thread.", + "_multiprocessing.SemLock._is_zero" => "Return whether semaphore has value zero.", + "_multiprocessing.SemLock.acquire" => "Acquire the semaphore/lock.", + "_multiprocessing.SemLock.handle" => "", + "_multiprocessing.SemLock.kind" => "", + "_multiprocessing.SemLock.maxvalue" => "", + "_multiprocessing.SemLock.name" => "", + "_multiprocessing.SemLock.release" => "Release the semaphore/lock.", + "_opcode" => "Opcode support module.", + "_opcode.get_executor" => "Return the executor object at offset in code if exists, None otherwise.", + "_opcode.get_intrinsic1_descs" => "Return a list of names of the unary intrinsics.", + "_opcode.get_intrinsic2_descs" => "Return a list of names of the binary intrinsics.", + "_opcode.get_nb_ops" => "Return array of symbols of binary ops.\n\nIndexed by the BINARY_OP oparg value.", + "_opcode.get_specialization_stats" => "Return the specialization stats", + "_opcode.has_arg" => "Return True if the opcode uses its oparg, False otherwise.", + "_opcode.has_const" => "Return True if the opcode accesses a constant, False otherwise.", + "_opcode.has_exc" => "Return True if the opcode sets an exception handler, False otherwise.", + "_opcode.has_free" => "Return True if the opcode accesses a free variable, False otherwise.\n\nNote that 'free' in this context refers to names in the current scope\nthat are referenced by inner scopes or names in outer scopes that are\nreferenced from this scope. It does not include references to global\nor builtin scopes.", + "_opcode.has_jump" => "Return True if the opcode has a jump target, False otherwise.", + "_opcode.has_local" => "Return True if the opcode accesses a local variable, False otherwise.", + "_opcode.has_name" => "Return True if the opcode accesses an attribute by name, False otherwise.", + "_opcode.is_valid" => "Return True if opcode is valid, False otherwise.", + "_opcode.stack_effect" => "Compute the stack effect of the opcode.", + "_operator" => "Operator interface.\n\nThis module exports a set of functions implemented in C corresponding\nto the intrinsic operators of Python. For example, operator.add(x, y)\nis equivalent to the expression x+y. The function names are those\nused for special methods; variants without leading and trailing\n'__' are also provided for convenience.", + "_operator._compare_digest" => "Return 'a == b'.\n\nThis function uses an approach designed to prevent\ntiming analysis, making it appropriate for cryptography.\n\na and b must both be of the same type: either str (ASCII only),\nor any bytes-like object.\n\nNote: If a and b are of different lengths, or if an error occurs,\na timing attack could theoretically reveal information about the\ntypes and lengths of a and b--but not their values.", + "_operator.abs" => "Same as abs(a).", + "_operator.add" => "Same as a + b.", + "_operator.and_" => "Same as a & b.", + "_operator.call" => "Same as obj(*args, **kwargs).", + "_operator.concat" => "Same as a + b, for a and b sequences.", + "_operator.contains" => "Same as b in a (note reversed operands).", + "_operator.countOf" => "Return the number of items in a which are, or which equal, b.", + "_operator.delitem" => "Same as del a[b].", + "_operator.eq" => "Same as a == b.", + "_operator.floordiv" => "Same as a // b.", + "_operator.ge" => "Same as a >= b.", + "_operator.getitem" => "Same as a[b].", + "_operator.gt" => "Same as a > b.", + "_operator.iadd" => "Same as a += b.", + "_operator.iand" => "Same as a &= b.", + "_operator.iconcat" => "Same as a += b, for a and b sequences.", + "_operator.ifloordiv" => "Same as a //= b.", + "_operator.ilshift" => "Same as a <<= b.", + "_operator.imatmul" => "Same as a @= b.", + "_operator.imod" => "Same as a %= b.", + "_operator.imul" => "Same as a *= b.", + "_operator.index" => "Same as a.__index__()", + "_operator.indexOf" => "Return the first index of b in a.", + "_operator.inv" => "Same as ~a.", + "_operator.invert" => "Same as ~a.", + "_operator.ior" => "Same as a |= b.", + "_operator.ipow" => "Same as a **= b.", + "_operator.irshift" => "Same as a >>= b.", + "_operator.is_" => "Same as a is b.", + "_operator.is_not" => "Same as a is not b.", + "_operator.isub" => "Same as a -= b.", + "_operator.itruediv" => "Same as a /= b.", + "_operator.ixor" => "Same as a ^= b.", + "_operator.le" => "Same as a <= b.", + "_operator.length_hint" => "Return an estimate of the number of items in obj.\n\nThis is useful for presizing containers when building from an iterable.\n\nIf the object supports len(), the result will be exact.\nOtherwise, it may over- or under-estimate by an arbitrary amount.\nThe result will be an integer >= 0.", + "_operator.lshift" => "Same as a << b.", + "_operator.lt" => "Same as a < b.", + "_operator.matmul" => "Same as a @ b.", + "_operator.mod" => "Same as a % b.", + "_operator.mul" => "Same as a * b.", + "_operator.ne" => "Same as a != b.", + "_operator.neg" => "Same as -a.", + "_operator.not_" => "Same as not a.", + "_operator.or_" => "Same as a | b.", + "_operator.pos" => "Same as +a.", + "_operator.pow" => "Same as a ** b.", + "_operator.rshift" => "Same as a >> b.", + "_operator.setitem" => "Same as a[b] = c.", + "_operator.sub" => "Same as a - b.", + "_operator.truediv" => "Same as a / b.", + "_operator.truth" => "Return True if a is true, False otherwise.", + "_operator.xor" => "Same as a ^ b.", + "_overlapped.BindLocal" => "Bind a socket handle to an arbitrary local port.\n\nfamily should be AF_INET or AF_INET6.", + "_overlapped.ConnectPipe" => "Connect to the pipe for asynchronous I/O (overlapped).", + "_overlapped.CreateEvent" => "Create an event.\n\nEventAttributes must be None.", + "_overlapped.CreateIoCompletionPort" => "Create a completion port or register a handle with a port.", + "_overlapped.FormatMessage" => "Return error message for an error code.", + "_overlapped.GetQueuedCompletionStatus" => "Get a message from completion port.\n\nWait for up to msecs milliseconds.", + "_overlapped.Overlapped" => "OVERLAPPED structure wrapper.", + "_overlapped.Overlapped.AcceptEx" => "Start overlapped wait for client to connect.", + "_overlapped.Overlapped.ConnectEx" => "Start overlapped connect.\n\nclient_handle should be unbound.", + "_overlapped.Overlapped.ConnectNamedPipe" => "Start overlapped wait for a client to connect.", + "_overlapped.Overlapped.ReadFile" => "Start overlapped read.", + "_overlapped.Overlapped.ReadFileInto" => "Start overlapped receive.", + "_overlapped.Overlapped.TransmitFile" => "Transmit file data over a connected socket.", + "_overlapped.Overlapped.WSARecv" => "Start overlapped receive.", + "_overlapped.Overlapped.WSARecvFrom" => "Start overlapped receive.", + "_overlapped.Overlapped.WSARecvFromInto" => "Start overlapped receive.", + "_overlapped.Overlapped.WSARecvInto" => "Start overlapped receive.", + "_overlapped.Overlapped.WSASend" => "Start overlapped send.", + "_overlapped.Overlapped.WSASendTo" => "Start overlapped sendto over a connectionless (UDP) socket.", + "_overlapped.Overlapped.WriteFile" => "Start overlapped write.", + "_overlapped.Overlapped.__delattr__" => "Implement delattr(self, name).", + "_overlapped.Overlapped.__eq__" => "Return self==value.", + "_overlapped.Overlapped.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_overlapped.Overlapped.__ge__" => "Return self>=value.", + "_overlapped.Overlapped.__getattribute__" => "Return getattr(self, name).", + "_overlapped.Overlapped.__getstate__" => "Helper for pickle.", + "_overlapped.Overlapped.__gt__" => "Return self>value.", + "_overlapped.Overlapped.__hash__" => "Return hash(self).", + "_overlapped.Overlapped.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_overlapped.Overlapped.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_overlapped.Overlapped.__le__" => "Return self<=value.", + "_overlapped.Overlapped.__lt__" => "Return self "Return self!=value.", + "_overlapped.Overlapped.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_overlapped.Overlapped.__reduce__" => "Helper for pickle.", + "_overlapped.Overlapped.__reduce_ex__" => "Helper for pickle.", + "_overlapped.Overlapped.__repr__" => "Return repr(self).", + "_overlapped.Overlapped.__setattr__" => "Implement setattr(self, name, value).", + "_overlapped.Overlapped.__sizeof__" => "Size of object in memory, in bytes.", + "_overlapped.Overlapped.__str__" => "Return str(self).", + "_overlapped.Overlapped.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_overlapped.Overlapped.address" => "Address of overlapped structure", + "_overlapped.Overlapped.cancel" => "Cancel overlapped operation.", + "_overlapped.Overlapped.error" => "Error from last operation", + "_overlapped.Overlapped.event" => "Overlapped event handle", + "_overlapped.Overlapped.getresult" => "Retrieve result of operation.\n\nIf wait is true then it blocks until the operation is finished. If wait\nis false and the operation is still pending then an error is raised.", + "_overlapped.Overlapped.pending" => "Whether the operation is pending", + "_overlapped.PostQueuedCompletionStatus" => "Post a message to completion port.", + "_overlapped.RegisterWaitWithQueue" => "Register wait for Object; when complete CompletionPort is notified.", + "_overlapped.ResetEvent" => "Reset event.", + "_overlapped.SetEvent" => "Set event.", + "_overlapped.UnregisterWait" => "Unregister wait handle.", + "_overlapped.UnregisterWaitEx" => "Unregister wait handle.", + "_overlapped.WSAConnect" => "Bind a remote address to a connectionless (UDP) socket.", + "_pickle" => "Optimized C implementation for the Python pickle module.", + "_pickle.PickleError.__cause__" => "exception cause", + "_pickle.PickleError.__context__" => "exception context", + "_pickle.PickleError.__delattr__" => "Implement delattr(self, name).", + "_pickle.PickleError.__eq__" => "Return self==value.", + "_pickle.PickleError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_pickle.PickleError.__ge__" => "Return self>=value.", + "_pickle.PickleError.__getattribute__" => "Return getattr(self, name).", + "_pickle.PickleError.__getstate__" => "Helper for pickle.", + "_pickle.PickleError.__gt__" => "Return self>value.", + "_pickle.PickleError.__hash__" => "Return hash(self).", + "_pickle.PickleError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_pickle.PickleError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_pickle.PickleError.__le__" => "Return self<=value.", + "_pickle.PickleError.__lt__" => "Return self "Return self!=value.", + "_pickle.PickleError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_pickle.PickleError.__reduce_ex__" => "Helper for pickle.", + "_pickle.PickleError.__repr__" => "Return repr(self).", + "_pickle.PickleError.__setattr__" => "Implement setattr(self, name, value).", + "_pickle.PickleError.__sizeof__" => "Size of object in memory, in bytes.", + "_pickle.PickleError.__str__" => "Return str(self).", + "_pickle.PickleError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_pickle.PickleError.__weakref__" => "list of weak references to the object", + "_pickle.PickleError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_pickle.PickleError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_pickle.Pickler" => "This takes a binary file for writing a pickle data stream.\n\nThe optional *protocol* argument tells the pickler to use the given\nprotocol; supported protocols are 0, 1, 2, 3, 4 and 5. The default\nprotocol is 4. It was introduced in Python 3.4, and is incompatible\nwith previous versions.\n\nSpecifying a negative protocol version selects the highest protocol\nversion supported. The higher the protocol used, the more recent the\nversion of Python needed to read the pickle produced.\n\nThe *file* argument must have a write() method that accepts a single\nbytes argument. It can thus be a file object opened for binary\nwriting, an io.BytesIO instance, or any other custom object that meets\nthis interface.\n\nIf *fix_imports* is True and protocol is less than 3, pickle will try\nto map the new Python 3 names to the old module names used in Python\n2, so that the pickle data stream is readable with Python 2.\n\nIf *buffer_callback* is None (the default), buffer views are\nserialized into *file* as part of the pickle stream.\n\nIf *buffer_callback* is not None, then it can be called any number\nof times with a buffer view. If the callback returns a false value\n(such as None), the given buffer is out-of-band; otherwise the\nbuffer is serialized in-band, i.e. inside the pickle stream.\n\nIt is an error if *buffer_callback* is not None and *protocol*\nis None or smaller than 5.", + "_pickle.Pickler.__delattr__" => "Implement delattr(self, name).", + "_pickle.Pickler.__eq__" => "Return self==value.", + "_pickle.Pickler.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_pickle.Pickler.__ge__" => "Return self>=value.", + "_pickle.Pickler.__getattribute__" => "Return getattr(self, name).", + "_pickle.Pickler.__getstate__" => "Helper for pickle.", + "_pickle.Pickler.__gt__" => "Return self>value.", + "_pickle.Pickler.__hash__" => "Return hash(self).", + "_pickle.Pickler.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_pickle.Pickler.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_pickle.Pickler.__le__" => "Return self<=value.", + "_pickle.Pickler.__lt__" => "Return self "Return self!=value.", + "_pickle.Pickler.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_pickle.Pickler.__reduce__" => "Helper for pickle.", + "_pickle.Pickler.__reduce_ex__" => "Helper for pickle.", + "_pickle.Pickler.__repr__" => "Return repr(self).", + "_pickle.Pickler.__setattr__" => "Implement setattr(self, name, value).", + "_pickle.Pickler.__sizeof__" => "Returns size in memory, in bytes.", + "_pickle.Pickler.__str__" => "Return str(self).", + "_pickle.Pickler.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_pickle.Pickler.clear_memo" => "Clears the pickler's \"memo\".\n\nThe memo is the data structure that remembers which objects the\npickler has already seen, so that shared or recursive objects are\npickled by reference and not by value. This method is useful when\nre-using picklers.", + "_pickle.Pickler.dump" => "Write a pickled representation of the given object to the open file.", + "_pickle.PicklingError.__cause__" => "exception cause", + "_pickle.PicklingError.__context__" => "exception context", + "_pickle.PicklingError.__delattr__" => "Implement delattr(self, name).", + "_pickle.PicklingError.__eq__" => "Return self==value.", + "_pickle.PicklingError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_pickle.PicklingError.__ge__" => "Return self>=value.", + "_pickle.PicklingError.__getattribute__" => "Return getattr(self, name).", + "_pickle.PicklingError.__getstate__" => "Helper for pickle.", + "_pickle.PicklingError.__gt__" => "Return self>value.", + "_pickle.PicklingError.__hash__" => "Return hash(self).", + "_pickle.PicklingError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_pickle.PicklingError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_pickle.PicklingError.__le__" => "Return self<=value.", + "_pickle.PicklingError.__lt__" => "Return self "Return self!=value.", + "_pickle.PicklingError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_pickle.PicklingError.__reduce_ex__" => "Helper for pickle.", + "_pickle.PicklingError.__repr__" => "Return repr(self).", + "_pickle.PicklingError.__setattr__" => "Implement setattr(self, name, value).", + "_pickle.PicklingError.__sizeof__" => "Size of object in memory, in bytes.", + "_pickle.PicklingError.__str__" => "Return str(self).", + "_pickle.PicklingError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_pickle.PicklingError.__weakref__" => "list of weak references to the object", + "_pickle.PicklingError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_pickle.PicklingError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_pickle.Unpickler" => "This takes a binary file for reading a pickle data stream.\n\nThe protocol version of the pickle is detected automatically, so no\nprotocol argument is needed. Bytes past the pickled object's\nrepresentation are ignored.\n\nThe argument *file* must have two methods, a read() method that takes\nan integer argument, and a readline() method that requires no\narguments. Both methods should return bytes. Thus *file* can be a\nbinary file object opened for reading, an io.BytesIO object, or any\nother custom object that meets this interface.\n\nOptional keyword arguments are *fix_imports*, *encoding* and *errors*,\nwhich are used to control compatibility support for pickle stream\ngenerated by Python 2. If *fix_imports* is True, pickle will try to\nmap the old Python 2 names to the new names used in Python 3. The\n*encoding* and *errors* tell pickle how to decode 8-bit string\ninstances pickled by Python 2; these default to 'ASCII' and 'strict',\nrespectively. The *encoding* can be 'bytes' to read these 8-bit\nstring instances as bytes objects.", + "_pickle.Unpickler.__delattr__" => "Implement delattr(self, name).", + "_pickle.Unpickler.__eq__" => "Return self==value.", + "_pickle.Unpickler.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_pickle.Unpickler.__ge__" => "Return self>=value.", + "_pickle.Unpickler.__getattribute__" => "Return getattr(self, name).", + "_pickle.Unpickler.__getstate__" => "Helper for pickle.", + "_pickle.Unpickler.__gt__" => "Return self>value.", + "_pickle.Unpickler.__hash__" => "Return hash(self).", + "_pickle.Unpickler.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_pickle.Unpickler.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_pickle.Unpickler.__le__" => "Return self<=value.", + "_pickle.Unpickler.__lt__" => "Return self "Return self!=value.", + "_pickle.Unpickler.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_pickle.Unpickler.__reduce__" => "Helper for pickle.", + "_pickle.Unpickler.__reduce_ex__" => "Helper for pickle.", + "_pickle.Unpickler.__repr__" => "Return repr(self).", + "_pickle.Unpickler.__setattr__" => "Implement setattr(self, name, value).", + "_pickle.Unpickler.__sizeof__" => "Returns size in memory, in bytes.", + "_pickle.Unpickler.__str__" => "Return str(self).", + "_pickle.Unpickler.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_pickle.Unpickler.find_class" => "Return an object from a specified module.\n\nIf necessary, the module will be imported. Subclasses may override\nthis method (e.g. to restrict unpickling of arbitrary classes and\nfunctions).\n\nThis method is called whenever a class or a function object is\nneeded. Both arguments passed are str objects.", + "_pickle.Unpickler.load" => "Load a pickle.\n\nRead a pickled object representation from the open file object given\nin the constructor, and return the reconstituted object hierarchy\nspecified therein.", + "_pickle.UnpicklingError.__cause__" => "exception cause", + "_pickle.UnpicklingError.__context__" => "exception context", + "_pickle.UnpicklingError.__delattr__" => "Implement delattr(self, name).", + "_pickle.UnpicklingError.__eq__" => "Return self==value.", + "_pickle.UnpicklingError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_pickle.UnpicklingError.__ge__" => "Return self>=value.", + "_pickle.UnpicklingError.__getattribute__" => "Return getattr(self, name).", + "_pickle.UnpicklingError.__getstate__" => "Helper for pickle.", + "_pickle.UnpicklingError.__gt__" => "Return self>value.", + "_pickle.UnpicklingError.__hash__" => "Return hash(self).", + "_pickle.UnpicklingError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_pickle.UnpicklingError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_pickle.UnpicklingError.__le__" => "Return self<=value.", + "_pickle.UnpicklingError.__lt__" => "Return self "Return self!=value.", + "_pickle.UnpicklingError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_pickle.UnpicklingError.__reduce_ex__" => "Helper for pickle.", + "_pickle.UnpicklingError.__repr__" => "Return repr(self).", + "_pickle.UnpicklingError.__setattr__" => "Implement setattr(self, name, value).", + "_pickle.UnpicklingError.__sizeof__" => "Size of object in memory, in bytes.", + "_pickle.UnpicklingError.__str__" => "Return str(self).", + "_pickle.UnpicklingError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_pickle.UnpicklingError.__weakref__" => "list of weak references to the object", + "_pickle.UnpicklingError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_pickle.UnpicklingError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_pickle.dump" => "Write a pickled representation of obj to the open file object file.\n\nThis is equivalent to ``Pickler(file, protocol).dump(obj)``, but may\nbe more efficient.\n\nThe optional *protocol* argument tells the pickler to use the given\nprotocol; supported protocols are 0, 1, 2, 3, 4 and 5. The default\nprotocol is 4. It was introduced in Python 3.4, and is incompatible\nwith previous versions.\n\nSpecifying a negative protocol version selects the highest protocol\nversion supported. The higher the protocol used, the more recent the\nversion of Python needed to read the pickle produced.\n\nThe *file* argument must have a write() method that accepts a single\nbytes argument. It can thus be a file object opened for binary\nwriting, an io.BytesIO instance, or any other custom object that meets\nthis interface.\n\nIf *fix_imports* is True and protocol is less than 3, pickle will try\nto map the new Python 3 names to the old module names used in Python\n2, so that the pickle data stream is readable with Python 2.\n\nIf *buffer_callback* is None (the default), buffer views are serialized\ninto *file* as part of the pickle stream. It is an error if\n*buffer_callback* is not None and *protocol* is None or smaller than 5.", + "_pickle.dumps" => "Return the pickled representation of the object as a bytes object.\n\nThe optional *protocol* argument tells the pickler to use the given\nprotocol; supported protocols are 0, 1, 2, 3, 4 and 5. The default\nprotocol is 4. It was introduced in Python 3.4, and is incompatible\nwith previous versions.\n\nSpecifying a negative protocol version selects the highest protocol\nversion supported. The higher the protocol used, the more recent the\nversion of Python needed to read the pickle produced.\n\nIf *fix_imports* is True and *protocol* is less than 3, pickle will\ntry to map the new Python 3 names to the old module names used in\nPython 2, so that the pickle data stream is readable with Python 2.\n\nIf *buffer_callback* is None (the default), buffer views are serialized\ninto *file* as part of the pickle stream. It is an error if\n*buffer_callback* is not None and *protocol* is None or smaller than 5.", + "_pickle.load" => "Read and return an object from the pickle data stored in a file.\n\nThis is equivalent to ``Unpickler(file).load()``, but may be more\nefficient.\n\nThe protocol version of the pickle is detected automatically, so no\nprotocol argument is needed. Bytes past the pickled object's\nrepresentation are ignored.\n\nThe argument *file* must have two methods, a read() method that takes\nan integer argument, and a readline() method that requires no\narguments. Both methods should return bytes. Thus *file* can be a\nbinary file object opened for reading, an io.BytesIO object, or any\nother custom object that meets this interface.\n\nOptional keyword arguments are *fix_imports*, *encoding* and *errors*,\nwhich are used to control compatibility support for pickle stream\ngenerated by Python 2. If *fix_imports* is True, pickle will try to\nmap the old Python 2 names to the new names used in Python 3. The\n*encoding* and *errors* tell pickle how to decode 8-bit string\ninstances pickled by Python 2; these default to 'ASCII' and 'strict',\nrespectively. The *encoding* can be 'bytes' to read these 8-bit\nstring instances as bytes objects.", + "_pickle.loads" => "Read and return an object from the given pickle data.\n\nThe protocol version of the pickle is detected automatically, so no\nprotocol argument is needed. Bytes past the pickled object's\nrepresentation are ignored.\n\nOptional keyword arguments are *fix_imports*, *encoding* and *errors*,\nwhich are used to control compatibility support for pickle stream\ngenerated by Python 2. If *fix_imports* is True, pickle will try to\nmap the old Python 2 names to the new names used in Python 3. The\n*encoding* and *errors* tell pickle how to decode 8-bit string\ninstances pickled by Python 2; these default to 'ASCII' and 'strict',\nrespectively. The *encoding* can be 'bytes' to read these 8-bit\nstring instances as bytes objects.", + "_posixshmem" => "POSIX shared memory module", + "_posixshmem.shm_open" => "Open a shared memory object. Returns a file descriptor (integer).", + "_posixshmem.shm_unlink" => "Remove a shared memory object (similar to unlink()).\n\nRemove a shared memory object name, and, once all processes have unmapped\nthe object, de-allocates and destroys the contents of the associated memory\nregion.", + "_posixsubprocess" => "A POSIX helper for the subprocess module.", + "_posixsubprocess.fork_exec" => "Spawn a fresh new child process.\n\nFork a child process, close parent file descriptors as appropriate in the\nchild and duplicate the few that are needed before calling exec() in the\nchild process.\n\nIf close_fds is True, close file descriptors 3 and higher, except those listed\nin the sorted tuple pass_fds.\n\nThe preexec_fn, if supplied, will be called immediately before closing file\ndescriptors and exec.\n\nWARNING: preexec_fn is NOT SAFE if your application uses threads.\n It may trigger infrequent, difficult to debug deadlocks.\n\nIf an error occurs in the child process before the exec, it is\nserialized and written to the errpipe_write fd per subprocess.py.\n\nReturns: the child process's PID.\n\nRaises: Only on an error in the parent process.", + "_queue" => "C implementation of the Python queue module.\nThis module is an implementation detail, please do not use it directly.", + "_queue.Empty" => "Exception raised by Queue.get(block=0)/get_nowait().", + "_queue.Empty.__cause__" => "exception cause", + "_queue.Empty.__context__" => "exception context", + "_queue.Empty.__delattr__" => "Implement delattr(self, name).", + "_queue.Empty.__eq__" => "Return self==value.", + "_queue.Empty.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_queue.Empty.__ge__" => "Return self>=value.", + "_queue.Empty.__getattribute__" => "Return getattr(self, name).", + "_queue.Empty.__getstate__" => "Helper for pickle.", + "_queue.Empty.__gt__" => "Return self>value.", + "_queue.Empty.__hash__" => "Return hash(self).", + "_queue.Empty.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_queue.Empty.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_queue.Empty.__le__" => "Return self<=value.", + "_queue.Empty.__lt__" => "Return self "Return self!=value.", + "_queue.Empty.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_queue.Empty.__reduce_ex__" => "Helper for pickle.", + "_queue.Empty.__repr__" => "Return repr(self).", + "_queue.Empty.__setattr__" => "Implement setattr(self, name, value).", + "_queue.Empty.__sizeof__" => "Size of object in memory, in bytes.", + "_queue.Empty.__str__" => "Return str(self).", + "_queue.Empty.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_queue.Empty.__weakref__" => "list of weak references to the object", + "_queue.Empty.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_queue.Empty.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_queue.SimpleQueue" => "Simple, unbounded, reentrant FIFO queue.", + "_queue.SimpleQueue.__class_getitem__" => "See PEP 585", + "_queue.SimpleQueue.__delattr__" => "Implement delattr(self, name).", + "_queue.SimpleQueue.__eq__" => "Return self==value.", + "_queue.SimpleQueue.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_queue.SimpleQueue.__ge__" => "Return self>=value.", + "_queue.SimpleQueue.__getattribute__" => "Return getattr(self, name).", + "_queue.SimpleQueue.__getstate__" => "Helper for pickle.", + "_queue.SimpleQueue.__gt__" => "Return self>value.", + "_queue.SimpleQueue.__hash__" => "Return hash(self).", + "_queue.SimpleQueue.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_queue.SimpleQueue.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_queue.SimpleQueue.__le__" => "Return self<=value.", + "_queue.SimpleQueue.__lt__" => "Return self "Return self!=value.", + "_queue.SimpleQueue.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_queue.SimpleQueue.__reduce__" => "Helper for pickle.", + "_queue.SimpleQueue.__reduce_ex__" => "Helper for pickle.", + "_queue.SimpleQueue.__repr__" => "Return repr(self).", + "_queue.SimpleQueue.__setattr__" => "Implement setattr(self, name, value).", + "_queue.SimpleQueue.__sizeof__" => "Size of object in memory, in bytes.", + "_queue.SimpleQueue.__str__" => "Return str(self).", + "_queue.SimpleQueue.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_queue.SimpleQueue.empty" => "Return True if the queue is empty, False otherwise (not reliable!).", + "_queue.SimpleQueue.get" => "Remove and return an item from the queue.\n\nIf optional args 'block' is true and 'timeout' is None (the default),\nblock if necessary until an item is available. If 'timeout' is\na non-negative number, it blocks at most 'timeout' seconds and raises\nthe Empty exception if no item was available within that time.\nOtherwise ('block' is false), return an item if one is immediately\navailable, else raise the Empty exception ('timeout' is ignored\nin that case).", + "_queue.SimpleQueue.get_nowait" => "Remove and return an item from the queue without blocking.\n\nOnly get an item if one is immediately available. Otherwise\nraise the Empty exception.", + "_queue.SimpleQueue.put" => "Put the item on the queue.\n\nThe optional 'block' and 'timeout' arguments are ignored, as this method\nnever blocks. They are provided for compatibility with the Queue class.", + "_queue.SimpleQueue.put_nowait" => "Put an item into the queue without blocking.\n\nThis is exactly equivalent to `put(item)` and is only provided\nfor compatibility with the Queue class.", + "_queue.SimpleQueue.qsize" => "Return the approximate size of the queue (not reliable!).", + "_random" => "Module implements the Mersenne Twister random number generator.", + "_random.Random" => "Random() -> create a random number generator with its own internal state.", + "_random.Random.__delattr__" => "Implement delattr(self, name).", + "_random.Random.__eq__" => "Return self==value.", + "_random.Random.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_random.Random.__ge__" => "Return self>=value.", + "_random.Random.__getattribute__" => "Return getattr(self, name).", + "_random.Random.__getstate__" => "Helper for pickle.", + "_random.Random.__gt__" => "Return self>value.", + "_random.Random.__hash__" => "Return hash(self).", + "_random.Random.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_random.Random.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_random.Random.__le__" => "Return self<=value.", + "_random.Random.__lt__" => "Return self "Return self!=value.", + "_random.Random.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_random.Random.__reduce__" => "Helper for pickle.", + "_random.Random.__reduce_ex__" => "Helper for pickle.", + "_random.Random.__repr__" => "Return repr(self).", + "_random.Random.__setattr__" => "Implement setattr(self, name, value).", + "_random.Random.__sizeof__" => "Size of object in memory, in bytes.", + "_random.Random.__str__" => "Return str(self).", + "_random.Random.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_random.Random.getrandbits" => "getrandbits(k) -> x. Generates an int with k random bits.", + "_random.Random.getstate" => "getstate() -> tuple containing the current state.", + "_random.Random.random" => "random() -> x in the interval [0, 1).", + "_random.Random.seed" => "seed([n]) -> None.\n\nDefaults to use urandom and falls back to a combination\nof the current time and the process identifier.", + "_random.Random.setstate" => "setstate(state) -> None. Restores generator state.", + "_sha1.SHA1Type.__delattr__" => "Implement delattr(self, name).", + "_sha1.SHA1Type.__eq__" => "Return self==value.", + "_sha1.SHA1Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha1.SHA1Type.__ge__" => "Return self>=value.", + "_sha1.SHA1Type.__getattribute__" => "Return getattr(self, name).", + "_sha1.SHA1Type.__getstate__" => "Helper for pickle.", + "_sha1.SHA1Type.__gt__" => "Return self>value.", + "_sha1.SHA1Type.__hash__" => "Return hash(self).", + "_sha1.SHA1Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha1.SHA1Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha1.SHA1Type.__le__" => "Return self<=value.", + "_sha1.SHA1Type.__lt__" => "Return self "Return self!=value.", + "_sha1.SHA1Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha1.SHA1Type.__reduce__" => "Helper for pickle.", + "_sha1.SHA1Type.__reduce_ex__" => "Helper for pickle.", + "_sha1.SHA1Type.__repr__" => "Return repr(self).", + "_sha1.SHA1Type.__setattr__" => "Implement setattr(self, name, value).", + "_sha1.SHA1Type.__sizeof__" => "Size of object in memory, in bytes.", + "_sha1.SHA1Type.__str__" => "Return str(self).", + "_sha1.SHA1Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha1.SHA1Type.copy" => "Return a copy of the hash object.", + "_sha1.SHA1Type.digest" => "Return the digest value as a bytes object.", + "_sha1.SHA1Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha1.SHA1Type.update" => "Update this hash object's state with the provided string.", + "_sha1.sha1" => "Return a new SHA1 hash object; optionally initialized with a string.", + "_sha2.SHA224Type.__delattr__" => "Implement delattr(self, name).", + "_sha2.SHA224Type.__eq__" => "Return self==value.", + "_sha2.SHA224Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha2.SHA224Type.__ge__" => "Return self>=value.", + "_sha2.SHA224Type.__getattribute__" => "Return getattr(self, name).", + "_sha2.SHA224Type.__getstate__" => "Helper for pickle.", + "_sha2.SHA224Type.__gt__" => "Return self>value.", + "_sha2.SHA224Type.__hash__" => "Return hash(self).", + "_sha2.SHA224Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha2.SHA224Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha2.SHA224Type.__le__" => "Return self<=value.", + "_sha2.SHA224Type.__lt__" => "Return self "Return self!=value.", + "_sha2.SHA224Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha2.SHA224Type.__reduce__" => "Helper for pickle.", + "_sha2.SHA224Type.__reduce_ex__" => "Helper for pickle.", + "_sha2.SHA224Type.__repr__" => "Return repr(self).", + "_sha2.SHA224Type.__setattr__" => "Implement setattr(self, name, value).", + "_sha2.SHA224Type.__sizeof__" => "Size of object in memory, in bytes.", + "_sha2.SHA224Type.__str__" => "Return str(self).", + "_sha2.SHA224Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha2.SHA224Type.copy" => "Return a copy of the hash object.", + "_sha2.SHA224Type.digest" => "Return the digest value as a bytes object.", + "_sha2.SHA224Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha2.SHA224Type.update" => "Update this hash object's state with the provided string.", + "_sha2.SHA256Type.__delattr__" => "Implement delattr(self, name).", + "_sha2.SHA256Type.__eq__" => "Return self==value.", + "_sha2.SHA256Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha2.SHA256Type.__ge__" => "Return self>=value.", + "_sha2.SHA256Type.__getattribute__" => "Return getattr(self, name).", + "_sha2.SHA256Type.__getstate__" => "Helper for pickle.", + "_sha2.SHA256Type.__gt__" => "Return self>value.", + "_sha2.SHA256Type.__hash__" => "Return hash(self).", + "_sha2.SHA256Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha2.SHA256Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha2.SHA256Type.__le__" => "Return self<=value.", + "_sha2.SHA256Type.__lt__" => "Return self "Return self!=value.", + "_sha2.SHA256Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha2.SHA256Type.__reduce__" => "Helper for pickle.", + "_sha2.SHA256Type.__reduce_ex__" => "Helper for pickle.", + "_sha2.SHA256Type.__repr__" => "Return repr(self).", + "_sha2.SHA256Type.__setattr__" => "Implement setattr(self, name, value).", + "_sha2.SHA256Type.__sizeof__" => "Size of object in memory, in bytes.", + "_sha2.SHA256Type.__str__" => "Return str(self).", + "_sha2.SHA256Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha2.SHA256Type.copy" => "Return a copy of the hash object.", + "_sha2.SHA256Type.digest" => "Return the digest value as a bytes object.", + "_sha2.SHA256Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha2.SHA256Type.update" => "Update this hash object's state with the provided string.", + "_sha2.SHA384Type.__delattr__" => "Implement delattr(self, name).", + "_sha2.SHA384Type.__eq__" => "Return self==value.", + "_sha2.SHA384Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha2.SHA384Type.__ge__" => "Return self>=value.", + "_sha2.SHA384Type.__getattribute__" => "Return getattr(self, name).", + "_sha2.SHA384Type.__getstate__" => "Helper for pickle.", + "_sha2.SHA384Type.__gt__" => "Return self>value.", + "_sha2.SHA384Type.__hash__" => "Return hash(self).", + "_sha2.SHA384Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha2.SHA384Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha2.SHA384Type.__le__" => "Return self<=value.", + "_sha2.SHA384Type.__lt__" => "Return self "Return self!=value.", + "_sha2.SHA384Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha2.SHA384Type.__reduce__" => "Helper for pickle.", + "_sha2.SHA384Type.__reduce_ex__" => "Helper for pickle.", + "_sha2.SHA384Type.__repr__" => "Return repr(self).", + "_sha2.SHA384Type.__setattr__" => "Implement setattr(self, name, value).", + "_sha2.SHA384Type.__sizeof__" => "Size of object in memory, in bytes.", + "_sha2.SHA384Type.__str__" => "Return str(self).", + "_sha2.SHA384Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha2.SHA384Type.copy" => "Return a copy of the hash object.", + "_sha2.SHA384Type.digest" => "Return the digest value as a bytes object.", + "_sha2.SHA384Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha2.SHA384Type.update" => "Update this hash object's state with the provided string.", + "_sha2.SHA512Type.__delattr__" => "Implement delattr(self, name).", + "_sha2.SHA512Type.__eq__" => "Return self==value.", + "_sha2.SHA512Type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha2.SHA512Type.__ge__" => "Return self>=value.", + "_sha2.SHA512Type.__getattribute__" => "Return getattr(self, name).", + "_sha2.SHA512Type.__getstate__" => "Helper for pickle.", + "_sha2.SHA512Type.__gt__" => "Return self>value.", + "_sha2.SHA512Type.__hash__" => "Return hash(self).", + "_sha2.SHA512Type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha2.SHA512Type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha2.SHA512Type.__le__" => "Return self<=value.", + "_sha2.SHA512Type.__lt__" => "Return self "Return self!=value.", + "_sha2.SHA512Type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha2.SHA512Type.__reduce__" => "Helper for pickle.", + "_sha2.SHA512Type.__reduce_ex__" => "Helper for pickle.", + "_sha2.SHA512Type.__repr__" => "Return repr(self).", + "_sha2.SHA512Type.__setattr__" => "Implement setattr(self, name, value).", + "_sha2.SHA512Type.__sizeof__" => "Size of object in memory, in bytes.", + "_sha2.SHA512Type.__str__" => "Return str(self).", + "_sha2.SHA512Type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha2.SHA512Type.copy" => "Return a copy of the hash object.", + "_sha2.SHA512Type.digest" => "Return the digest value as a bytes object.", + "_sha2.SHA512Type.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha2.SHA512Type.update" => "Update this hash object's state with the provided string.", + "_sha2.sha224" => "Return a new SHA-224 hash object; optionally initialized with a string.", + "_sha2.sha256" => "Return a new SHA-256 hash object; optionally initialized with a string.", + "_sha2.sha384" => "Return a new SHA-384 hash object; optionally initialized with a string.", + "_sha2.sha512" => "Return a new SHA-512 hash object; optionally initialized with a string.", + "_sha3.sha3_224" => "sha3_224([data], *, usedforsecurity=True) -> SHA3 object\n\nReturn a new SHA3 hash object with a hashbit length of 28 bytes.", + "_sha3.sha3_224.__delattr__" => "Implement delattr(self, name).", + "_sha3.sha3_224.__eq__" => "Return self==value.", + "_sha3.sha3_224.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.sha3_224.__ge__" => "Return self>=value.", + "_sha3.sha3_224.__getattribute__" => "Return getattr(self, name).", + "_sha3.sha3_224.__getstate__" => "Helper for pickle.", + "_sha3.sha3_224.__gt__" => "Return self>value.", + "_sha3.sha3_224.__hash__" => "Return hash(self).", + "_sha3.sha3_224.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.sha3_224.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.sha3_224.__le__" => "Return self<=value.", + "_sha3.sha3_224.__lt__" => "Return self "Return self!=value.", + "_sha3.sha3_224.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.sha3_224.__reduce__" => "Helper for pickle.", + "_sha3.sha3_224.__reduce_ex__" => "Helper for pickle.", + "_sha3.sha3_224.__repr__" => "Return repr(self).", + "_sha3.sha3_224.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.sha3_224.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.sha3_224.__str__" => "Return str(self).", + "_sha3.sha3_224.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.sha3_224.copy" => "Return a copy of the hash object.", + "_sha3.sha3_224.digest" => "Return the digest value as a bytes object.", + "_sha3.sha3_224.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.sha3_224.update" => "Update this hash object's state with the provided bytes-like object.", + "_sha3.sha3_256" => "sha3_256([data], *, usedforsecurity=True) -> SHA3 object\n\nReturn a new SHA3 hash object with a hashbit length of 32 bytes.", + "_sha3.sha3_256.__delattr__" => "Implement delattr(self, name).", + "_sha3.sha3_256.__eq__" => "Return self==value.", + "_sha3.sha3_256.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.sha3_256.__ge__" => "Return self>=value.", + "_sha3.sha3_256.__getattribute__" => "Return getattr(self, name).", + "_sha3.sha3_256.__getstate__" => "Helper for pickle.", + "_sha3.sha3_256.__gt__" => "Return self>value.", + "_sha3.sha3_256.__hash__" => "Return hash(self).", + "_sha3.sha3_256.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.sha3_256.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.sha3_256.__le__" => "Return self<=value.", + "_sha3.sha3_256.__lt__" => "Return self "Return self!=value.", + "_sha3.sha3_256.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.sha3_256.__reduce__" => "Helper for pickle.", + "_sha3.sha3_256.__reduce_ex__" => "Helper for pickle.", + "_sha3.sha3_256.__repr__" => "Return repr(self).", + "_sha3.sha3_256.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.sha3_256.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.sha3_256.__str__" => "Return str(self).", + "_sha3.sha3_256.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.sha3_256.copy" => "Return a copy of the hash object.", + "_sha3.sha3_256.digest" => "Return the digest value as a bytes object.", + "_sha3.sha3_256.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.sha3_256.update" => "Update this hash object's state with the provided bytes-like object.", + "_sha3.sha3_384" => "sha3_384([data], *, usedforsecurity=True) -> SHA3 object\n\nReturn a new SHA3 hash object with a hashbit length of 48 bytes.", + "_sha3.sha3_384.__delattr__" => "Implement delattr(self, name).", + "_sha3.sha3_384.__eq__" => "Return self==value.", + "_sha3.sha3_384.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.sha3_384.__ge__" => "Return self>=value.", + "_sha3.sha3_384.__getattribute__" => "Return getattr(self, name).", + "_sha3.sha3_384.__getstate__" => "Helper for pickle.", + "_sha3.sha3_384.__gt__" => "Return self>value.", + "_sha3.sha3_384.__hash__" => "Return hash(self).", + "_sha3.sha3_384.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.sha3_384.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.sha3_384.__le__" => "Return self<=value.", + "_sha3.sha3_384.__lt__" => "Return self "Return self!=value.", + "_sha3.sha3_384.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.sha3_384.__reduce__" => "Helper for pickle.", + "_sha3.sha3_384.__reduce_ex__" => "Helper for pickle.", + "_sha3.sha3_384.__repr__" => "Return repr(self).", + "_sha3.sha3_384.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.sha3_384.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.sha3_384.__str__" => "Return str(self).", + "_sha3.sha3_384.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.sha3_384.copy" => "Return a copy of the hash object.", + "_sha3.sha3_384.digest" => "Return the digest value as a bytes object.", + "_sha3.sha3_384.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.sha3_384.update" => "Update this hash object's state with the provided bytes-like object.", + "_sha3.sha3_512" => "sha3_512([data], *, usedforsecurity=True) -> SHA3 object\n\nReturn a new SHA3 hash object with a hashbit length of 64 bytes.", + "_sha3.sha3_512.__delattr__" => "Implement delattr(self, name).", + "_sha3.sha3_512.__eq__" => "Return self==value.", + "_sha3.sha3_512.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.sha3_512.__ge__" => "Return self>=value.", + "_sha3.sha3_512.__getattribute__" => "Return getattr(self, name).", + "_sha3.sha3_512.__getstate__" => "Helper for pickle.", + "_sha3.sha3_512.__gt__" => "Return self>value.", + "_sha3.sha3_512.__hash__" => "Return hash(self).", + "_sha3.sha3_512.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.sha3_512.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.sha3_512.__le__" => "Return self<=value.", + "_sha3.sha3_512.__lt__" => "Return self "Return self!=value.", + "_sha3.sha3_512.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.sha3_512.__reduce__" => "Helper for pickle.", + "_sha3.sha3_512.__reduce_ex__" => "Helper for pickle.", + "_sha3.sha3_512.__repr__" => "Return repr(self).", + "_sha3.sha3_512.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.sha3_512.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.sha3_512.__str__" => "Return str(self).", + "_sha3.sha3_512.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.sha3_512.copy" => "Return a copy of the hash object.", + "_sha3.sha3_512.digest" => "Return the digest value as a bytes object.", + "_sha3.sha3_512.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.sha3_512.update" => "Update this hash object's state with the provided bytes-like object.", + "_sha3.shake_128" => "shake_128([data], *, usedforsecurity=True) -> SHAKE object\n\nReturn a new SHAKE hash object.", + "_sha3.shake_128.__delattr__" => "Implement delattr(self, name).", + "_sha3.shake_128.__eq__" => "Return self==value.", + "_sha3.shake_128.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.shake_128.__ge__" => "Return self>=value.", + "_sha3.shake_128.__getattribute__" => "Return getattr(self, name).", + "_sha3.shake_128.__getstate__" => "Helper for pickle.", + "_sha3.shake_128.__gt__" => "Return self>value.", + "_sha3.shake_128.__hash__" => "Return hash(self).", + "_sha3.shake_128.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.shake_128.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.shake_128.__le__" => "Return self<=value.", + "_sha3.shake_128.__lt__" => "Return self "Return self!=value.", + "_sha3.shake_128.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.shake_128.__reduce__" => "Helper for pickle.", + "_sha3.shake_128.__reduce_ex__" => "Helper for pickle.", + "_sha3.shake_128.__repr__" => "Return repr(self).", + "_sha3.shake_128.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.shake_128.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.shake_128.__str__" => "Return str(self).", + "_sha3.shake_128.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.shake_128.copy" => "Return a copy of the hash object.", + "_sha3.shake_128.digest" => "Return the digest value as a bytes object.", + "_sha3.shake_128.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.shake_128.update" => "Update this hash object's state with the provided bytes-like object.", + "_sha3.shake_256" => "shake_256([data], *, usedforsecurity=True) -> SHAKE object\n\nReturn a new SHAKE hash object.", + "_sha3.shake_256.__delattr__" => "Implement delattr(self, name).", + "_sha3.shake_256.__eq__" => "Return self==value.", + "_sha3.shake_256.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_sha3.shake_256.__ge__" => "Return self>=value.", + "_sha3.shake_256.__getattribute__" => "Return getattr(self, name).", + "_sha3.shake_256.__getstate__" => "Helper for pickle.", + "_sha3.shake_256.__gt__" => "Return self>value.", + "_sha3.shake_256.__hash__" => "Return hash(self).", + "_sha3.shake_256.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_sha3.shake_256.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_sha3.shake_256.__le__" => "Return self<=value.", + "_sha3.shake_256.__lt__" => "Return self "Return self!=value.", + "_sha3.shake_256.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_sha3.shake_256.__reduce__" => "Helper for pickle.", + "_sha3.shake_256.__reduce_ex__" => "Helper for pickle.", + "_sha3.shake_256.__repr__" => "Return repr(self).", + "_sha3.shake_256.__setattr__" => "Implement setattr(self, name, value).", + "_sha3.shake_256.__sizeof__" => "Size of object in memory, in bytes.", + "_sha3.shake_256.__str__" => "Return str(self).", + "_sha3.shake_256.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_sha3.shake_256.copy" => "Return a copy of the hash object.", + "_sha3.shake_256.digest" => "Return the digest value as a bytes object.", + "_sha3.shake_256.hexdigest" => "Return the digest value as a string of hexadecimal digits.", + "_sha3.shake_256.update" => "Update this hash object's state with the provided bytes-like object.", + "_signal" => "This module provides mechanisms to use signal handlers in Python.\n\nFunctions:\n\nalarm() -- cause SIGALRM after a specified time [Unix only]\nsetitimer() -- cause a signal (described below) after a specified\n float time and the timer may restart then [Unix only]\ngetitimer() -- get current value of timer [Unix only]\nsignal() -- set the action for a given signal\ngetsignal() -- get the signal action for a given signal\npause() -- wait until a signal arrives [Unix only]\ndefault_int_handler() -- default SIGINT handler\n\nsignal constants:\nSIG_DFL -- used to refer to the system default handler\nSIG_IGN -- used to ignore the signal\nNSIG -- number of defined signals\nSIGINT, SIGTERM, etc. -- signal numbers\n\nitimer constants:\nITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n expiration\nITIMER_VIRTUAL -- decrements only when the process is executing,\n and delivers SIGVTALRM upon expiration\nITIMER_PROF -- decrements both when the process is executing and\n when the system is executing on behalf of the process.\n Coupled with ITIMER_VIRTUAL, this timer is usually\n used to profile the time spent by the application\n in user and kernel space. SIGPROF is delivered upon\n expiration.\n\n\n*** IMPORTANT NOTICE ***\nA signal handler function is called with two arguments:\nthe first is the signal number, the second is the interrupted stack frame.", + "_signal.alarm" => "Arrange for SIGALRM to arrive after the given number of seconds.", + "_signal.default_int_handler" => "The default handler for SIGINT installed by Python.\n\nIt raises KeyboardInterrupt.", + "_signal.getitimer" => "Returns current value of given itimer.", + "_signal.getsignal" => "Return the current action for the given signal.\n\nThe return value can be:\n SIG_IGN -- if the signal is being ignored\n SIG_DFL -- if the default action for the signal is in effect\n None -- if an unknown handler is in effect\n anything else -- the callable Python object used as a handler", + "_signal.pause" => "Wait until a signal arrives.", + "_signal.pidfd_send_signal" => "Send a signal to a process referred to by a pid file descriptor.", + "_signal.pthread_kill" => "Send a signal to a thread.", + "_signal.pthread_sigmask" => "Fetch and/or change the signal mask of the calling thread.", + "_signal.raise_signal" => "Send a signal to the executing process.", + "_signal.set_wakeup_fd" => "Sets the fd to be written to (with the signal number) when a signal comes in.\n\nA library can use this to wakeup select or poll.\nThe previous fd or -1 is returned.\n\nThe fd must be non-blocking.", + "_signal.setitimer" => "Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL or ITIMER_PROF).\n\nThe timer will fire after value seconds and after that every interval seconds.\nThe itimer can be cleared by setting seconds to zero.\n\nReturns old values as a tuple: (delay, interval).", + "_signal.siginterrupt" => "Change system call restart behaviour.\n\nIf flag is False, system calls will be restarted when interrupted by\nsignal sig, else system calls will be interrupted.", + "_signal.signal" => "Set the action for the given signal.\n\nThe action can be SIG_DFL, SIG_IGN, or a callable Python object.\nThe previous action is returned. See getsignal() for possible return values.\n\n*** IMPORTANT NOTICE ***\nA signal handler function is called with two arguments:\nthe first is the signal number, the second is the interrupted stack frame.", + "_signal.sigpending" => "Examine pending signals.\n\nReturns a set of signal numbers that are pending for delivery to\nthe calling thread.", + "_signal.sigtimedwait" => "Like sigwaitinfo(), but with a timeout.\n\nThe timeout is specified in seconds, with floating-point numbers allowed.", + "_signal.sigwait" => "Wait for a signal.\n\nSuspend execution of the calling thread until the delivery of one of the\nsignals specified in the signal set sigset. The function accepts the signal\nand returns the signal number.", + "_signal.sigwaitinfo" => "Wait synchronously until one of the signals in *sigset* is delivered.\n\nReturns a struct_siginfo containing information about the signal.", + "_signal.strsignal" => "Return the system description of the given signal.\n\nReturns the description of signal *signalnum*, such as \"Interrupt\"\nfor :const:`SIGINT`. Returns :const:`None` if *signalnum* has no\ndescription. Raises :exc:`ValueError` if *signalnum* is invalid.", + "_signal.valid_signals" => "Return a set of valid signal numbers on this platform.\n\nThe signal numbers returned by this function can be safely passed to\nfunctions like `pthread_sigmask`.", + "_socket" => "Implementation module for socket operations.\n\nSee the socket module for documentation.", + "_socket.CMSG_LEN" => "CMSG_LEN(length) -> control message length\n\nReturn the total length, without trailing padding, of an ancillary\ndata item with associated data of the given length. This value can\noften be used as the buffer size for recvmsg() to receive a single\nitem of ancillary data, but RFC 3542 requires portable applications to\nuse CMSG_SPACE() and thus include space for padding, even when the\nitem will be the last in the buffer. Raises OverflowError if length\nis outside the permissible range of values.", + "_socket.CMSG_SPACE" => "CMSG_SPACE(length) -> buffer size\n\nReturn the buffer size needed for recvmsg() to receive an ancillary\ndata item with associated data of the given length, along with any\ntrailing padding. The buffer space needed to receive multiple items\nis the sum of the CMSG_SPACE() values for their associated data\nlengths. Raises OverflowError if length is outside the permissible\nrange of values.", + "_socket.SocketType" => "socket(family=AF_INET, type=SOCK_STREAM, proto=0) -> socket object\nsocket(family=-1, type=-1, proto=-1, fileno=None) -> socket object\n\nOpen a socket of the given type. The family argument specifies the\naddress family; it defaults to AF_INET. The type argument specifies\nwhether this is a stream (SOCK_STREAM, this is the default)\nor datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\nspecifying the default protocol. Keyword arguments are accepted.\nThe socket is created as non-inheritable.\n\nWhen a fileno is passed in, family, type and proto are auto-detected,\nunless they are explicitly set.\n\nA socket object represents one endpoint of a network connection.\n\nMethods of socket objects (keyword arguments not allowed):\n\n_accept() -- accept connection, returning new socket fd and client address\nbind(addr) -- bind the socket to a local address\nclose() -- close the socket\nconnect(addr) -- connect the socket to a remote address\nconnect_ex(addr) -- connect, return an error code instead of an exception\ndup() -- return a new socket fd duplicated from fileno()\nfileno() -- return underlying file descriptor\ngetpeername() -- return remote address [*]\ngetsockname() -- return local address\ngetsockopt(level, optname[, buflen]) -- get socket options\ngettimeout() -- return timeout or None\nlisten([n]) -- start listening for incoming connections\nrecv(buflen[, flags]) -- receive data\nrecv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\nrecvfrom(buflen[, flags]) -- receive data and sender's address\nrecvfrom_into(buffer[, nbytes, [, flags])\n -- receive data and sender's address (into a buffer)\nsendall(data[, flags]) -- send all data\nsend(data[, flags]) -- send data, may not send all of it\nsendto(data[, flags], addr) -- send data to a given address\nsetblocking(bool) -- set or clear the blocking I/O flag\ngetblocking() -- return True if socket is blocking, False if non-blocking\nsetsockopt(level, optname, value[, optlen]) -- set socket options\nsettimeout(None | float) -- set or clear the timeout\nshutdown(how) -- shut down traffic in one or both directions\n\n [*] not available on all platforms!", + "_socket.SocketType.__del__" => "Called when the instance is about to be destroyed.", + "_socket.SocketType.__delattr__" => "Implement delattr(self, name).", + "_socket.SocketType.__eq__" => "Return self==value.", + "_socket.SocketType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_socket.SocketType.__ge__" => "Return self>=value.", + "_socket.SocketType.__getattribute__" => "Return getattr(self, name).", + "_socket.SocketType.__getstate__" => "Helper for pickle.", + "_socket.SocketType.__gt__" => "Return self>value.", + "_socket.SocketType.__hash__" => "Return hash(self).", + "_socket.SocketType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_socket.SocketType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_socket.SocketType.__le__" => "Return self<=value.", + "_socket.SocketType.__lt__" => "Return self "Return self!=value.", + "_socket.SocketType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_socket.SocketType.__reduce__" => "Helper for pickle.", + "_socket.SocketType.__reduce_ex__" => "Helper for pickle.", + "_socket.SocketType.__repr__" => "Return repr(self).", + "_socket.SocketType.__setattr__" => "Implement setattr(self, name, value).", + "_socket.SocketType.__sizeof__" => "Size of object in memory, in bytes.", + "_socket.SocketType.__str__" => "Return str(self).", + "_socket.SocketType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_socket.SocketType._accept" => "_accept() -> (integer, address info)\n\nWait for an incoming connection. Return a new socket file descriptor\nrepresenting the connection, and the address of the client.\nFor IP sockets, the address info is a pair (hostaddr, port).", + "_socket.SocketType.bind" => "bind(address)\n\nBind the socket to a local address. For IP sockets, the address is a\npair (host, port); the host must refer to the local host. For raw packet\nsockets the address is a tuple (ifname, proto [,pkttype [,hatype [,addr]]])", + "_socket.SocketType.close" => "close()\n\nClose the socket. It cannot be used after this call.", + "_socket.SocketType.connect" => "connect(address)\n\nConnect the socket to a remote address. For IP sockets, the address\nis a pair (host, port).", + "_socket.SocketType.connect_ex" => "connect_ex(address) -> errno\n\nThis is like connect(address), but returns an error code (the errno value)\ninstead of raising an exception when an error occurs.", + "_socket.SocketType.detach" => "detach()\n\nClose the socket object without closing the underlying file descriptor.\nThe object cannot be used after this call, but the file descriptor\ncan be reused for other purposes. The file descriptor is returned.", + "_socket.SocketType.family" => "the socket family", + "_socket.SocketType.fileno" => "fileno() -> integer\n\nReturn the integer file descriptor of the socket.", + "_socket.SocketType.getblocking" => "getblocking()\n\nReturns True if socket is in blocking mode, or False if it\nis in non-blocking mode.", + "_socket.SocketType.getpeername" => "getpeername() -> address info\n\nReturn the address of the remote endpoint. For IP sockets, the address\ninfo is a pair (hostaddr, port).", + "_socket.SocketType.getsockname" => "getsockname() -> address info\n\nReturn the address of the local endpoint. The format depends on the\naddress family. For IPv4 sockets, the address info is a pair\n(hostaddr, port). For IPv6 sockets, the address info is a 4-tuple\n(hostaddr, port, flowinfo, scope_id).", + "_socket.SocketType.getsockopt" => "getsockopt(level, option[, buffersize]) -> value\n\nGet a socket option. See the Unix manual for level and option.\nIf a nonzero buffersize argument is given, the return value is a\nstring of that length; otherwise it is an integer.", + "_socket.SocketType.gettimeout" => "gettimeout() -> timeout\n\nReturns the timeout in seconds (float) associated with socket\noperations. A timeout of None indicates that timeouts on socket\noperations are disabled.", + "_socket.SocketType.ioctl" => "ioctl(cmd, option) -> long\n\nControl the socket with WSAIoctl syscall. Currently supported 'cmd' values are\nSIO_RCVALL: 'option' must be one of the socket.RCVALL_* constants.\nSIO_KEEPALIVE_VALS: 'option' is a tuple of (onoff, timeout, interval).\nSIO_LOOPBACK_FAST_PATH: 'option' is a boolean value, and is disabled by default", + "_socket.SocketType.listen" => "listen([backlog])\n\nEnable a server to accept connections. If backlog is specified, it must be\nat least 0 (if it is lower, it is set to 0); it specifies the number of\nunaccepted connections that the system will allow before refusing new\nconnections. If not specified, a default reasonable value is chosen.", + "_socket.SocketType.proto" => "the socket protocol", + "_socket.SocketType.recv" => "recv(buffersize[, flags]) -> data\n\nReceive up to buffersize bytes from the socket. For the optional flags\nargument, see the Unix manual. When no data is available, block until\nat least one byte is available or until the remote end is closed. When\nthe remote end is closed and all data is read, return the empty string.", + "_socket.SocketType.recv_into" => "recv_into(buffer, [nbytes[, flags]]) -> nbytes_read\n\nA version of recv() that stores its data into a buffer rather than creating\na new string. Receive up to buffersize bytes from the socket. If buffersize\nis not specified (or 0), receive up to the size available in the given buffer.\n\nSee recv() for documentation about the flags.", + "_socket.SocketType.recvfrom" => "recvfrom(buffersize[, flags]) -> (data, address info)\n\nLike recv(buffersize, flags) but also return the sender's address info.", + "_socket.SocketType.recvfrom_into" => "recvfrom_into(buffer[, nbytes[, flags]]) -> (nbytes, address info)\n\nLike recv_into(buffer[, nbytes[, flags]]) but also return the sender's address info.", + "_socket.SocketType.recvmsg" => "recvmsg(bufsize[, ancbufsize[, flags]]) -> (data, ancdata, msg_flags, address)\n\nReceive normal data (up to bufsize bytes) and ancillary data from the\nsocket. The ancbufsize argument sets the size in bytes of the\ninternal buffer used to receive the ancillary data; it defaults to 0,\nmeaning that no ancillary data will be received. Appropriate buffer\nsizes for ancillary data can be calculated using CMSG_SPACE() or\nCMSG_LEN(), and items which do not fit into the buffer might be\ntruncated or discarded. The flags argument defaults to 0 and has the\nsame meaning as for recv().\n\nThe return value is a 4-tuple: (data, ancdata, msg_flags, address).\nThe data item is a bytes object holding the non-ancillary data\nreceived. The ancdata item is a list of zero or more tuples\n(cmsg_level, cmsg_type, cmsg_data) representing the ancillary data\n(control messages) received: cmsg_level and cmsg_type are integers\nspecifying the protocol level and protocol-specific type respectively,\nand cmsg_data is a bytes object holding the associated data. The\nmsg_flags item is the bitwise OR of various flags indicating\nconditions on the received message; see your system documentation for\ndetails. If the receiving socket is unconnected, address is the\naddress of the sending socket, if available; otherwise, its value is\nunspecified.\n\nIf recvmsg() raises an exception after the system call returns, it\nwill first attempt to close any file descriptors received via the\nSCM_RIGHTS mechanism.", + "_socket.SocketType.recvmsg_into" => "recvmsg_into(buffers[, ancbufsize[, flags]]) -> (nbytes, ancdata, msg_flags, address)\n\nReceive normal data and ancillary data from the socket, scattering the\nnon-ancillary data into a series of buffers. The buffers argument\nmust be an iterable of objects that export writable buffers\n(e.g. bytearray objects); these will be filled with successive chunks\nof the non-ancillary data until it has all been written or there are\nno more buffers. The ancbufsize argument sets the size in bytes of\nthe internal buffer used to receive the ancillary data; it defaults to\n0, meaning that no ancillary data will be received. Appropriate\nbuffer sizes for ancillary data can be calculated using CMSG_SPACE()\nor CMSG_LEN(), and items which do not fit into the buffer might be\ntruncated or discarded. The flags argument defaults to 0 and has the\nsame meaning as for recv().\n\nThe return value is a 4-tuple: (nbytes, ancdata, msg_flags, address).\nThe nbytes item is the total number of bytes of non-ancillary data\nwritten into the buffers. The ancdata item is a list of zero or more\ntuples (cmsg_level, cmsg_type, cmsg_data) representing the ancillary\ndata (control messages) received: cmsg_level and cmsg_type are\nintegers specifying the protocol level and protocol-specific type\nrespectively, and cmsg_data is a bytes object holding the associated\ndata. The msg_flags item is the bitwise OR of various flags\nindicating conditions on the received message; see your system\ndocumentation for details. If the receiving socket is unconnected,\naddress is the address of the sending socket, if available; otherwise,\nits value is unspecified.\n\nIf recvmsg_into() raises an exception after the system call returns,\nit will first attempt to close any file descriptors received via the\nSCM_RIGHTS mechanism.", + "_socket.SocketType.send" => "send(data[, flags]) -> count\n\nSend a data string to the socket. For the optional flags\nargument, see the Unix manual. Return the number of bytes\nsent; this may be less than len(data) if the network is busy.", + "_socket.SocketType.sendall" => "sendall(data[, flags])\n\nSend a data string to the socket. For the optional flags\nargument, see the Unix manual. This calls send() repeatedly\nuntil all data is sent. If an error occurs, it's impossible\nto tell how much data has been sent.", + "_socket.SocketType.sendmsg" => "sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\nSend normal and ancillary data to the socket, gathering the\nnon-ancillary data from a series of buffers and concatenating it into\na single message. The buffers argument specifies the non-ancillary\ndata as an iterable of bytes-like objects (e.g. bytes objects).\nThe ancdata argument specifies the ancillary data (control messages)\nas an iterable of zero or more tuples (cmsg_level, cmsg_type,\ncmsg_data), where cmsg_level and cmsg_type are integers specifying the\nprotocol level and protocol-specific type respectively, and cmsg_data\nis a bytes-like object holding the associated data. The flags\nargument defaults to 0 and has the same meaning as for send(). If\naddress is supplied and not None, it sets a destination address for\nthe message. The return value is the number of bytes of non-ancillary\ndata sent.", + "_socket.SocketType.sendmsg_afalg" => "sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags=MSG_MORE]]])\n\nSet operation mode, IV and length of associated data for an AF_ALG\noperation socket.", + "_socket.SocketType.sendto" => "sendto(data[, flags], address) -> count\n\nLike send(data, flags) but allows specifying the destination address.\nFor IP sockets, the address is a pair (hostaddr, port).", + "_socket.SocketType.setblocking" => "setblocking(flag)\n\nSet the socket to blocking (flag is true) or non-blocking (false).\nsetblocking(True) is equivalent to settimeout(None);\nsetblocking(False) is equivalent to settimeout(0.0).", + "_socket.SocketType.setsockopt" => "setsockopt(level, option, value: int)\nsetsockopt(level, option, value: buffer)\nsetsockopt(level, option, None, optlen: int)\n\nSet a socket option. See the Unix manual for level and option.\nThe value argument can either be an integer, a string buffer, or\nNone, optlen.", + "_socket.SocketType.settimeout" => "settimeout(timeout)\n\nSet a timeout on socket operations. 'timeout' can be a float,\ngiving in seconds, or None. Setting a timeout of None disables\nthe timeout feature and is equivalent to setblocking(1).\nSetting a timeout of zero is the same as setblocking(0).", + "_socket.SocketType.share" => "share(process_id) -> bytes\n\nShare the socket with another process. The target process id\nmust be provided and the resulting bytes object passed to the target\nprocess. There the shared socket can be instantiated by calling\nsocket.fromshare().", + "_socket.SocketType.shutdown" => "shutdown(flag)\n\nShut down the reading side of the socket (flag == SHUT_RD), the writing side\nof the socket (flag == SHUT_WR), or both ends (flag == SHUT_RDWR).", + "_socket.SocketType.timeout" => "the socket timeout", + "_socket.SocketType.type" => "the socket type", + "_socket.close" => "close(integer) -> None\n\nClose an integer socket file descriptor. This is like os.close(), but for\nsockets; on some platforms os.close() won't work for socket file descriptors.", + "_socket.dup" => "dup(integer) -> integer\n\nDuplicate an integer socket file descriptor. This is like os.dup(), but for\nsockets; on some platforms os.dup() won't work for socket file descriptors.", + "_socket.getaddrinfo" => "getaddrinfo(host, port [, family, type, proto, flags])\n -> list of (family, type, proto, canonname, sockaddr)\n\nResolve host and port into addrinfo struct.", + "_socket.getdefaulttimeout" => "getdefaulttimeout() -> timeout\n\nReturns the default timeout in seconds (float) for new socket objects.\nA value of None indicates that new socket objects have no timeout.\nWhen the socket module is first imported, the default is None.", + "_socket.gethostbyaddr" => "gethostbyaddr(host) -> (name, aliaslist, addresslist)\n\nReturn the true host name, a list of aliases, and a list of IP addresses,\nfor a host. The host argument is a string giving a host name or IP number.", + "_socket.gethostbyname" => "gethostbyname(host) -> address\n\nReturn the IP address (a string of the form '255.255.255.255') for a host.", + "_socket.gethostbyname_ex" => "gethostbyname_ex(host) -> (name, aliaslist, addresslist)\n\nReturn the true host name, a list of aliases, and a list of IP addresses,\nfor a host. The host argument is a string giving a host name or IP number.", + "_socket.gethostname" => "gethostname() -> string\n\nReturn the current host name.", + "_socket.getnameinfo" => "getnameinfo(sockaddr, flags) --> (host, port)\n\nGet host and port for a sockaddr.", + "_socket.getprotobyname" => "getprotobyname(name) -> integer\n\nReturn the protocol number for the named protocol. (Rarely used.)", + "_socket.getservbyname" => "getservbyname(servicename[, protocolname]) -> integer\n\nReturn a port number from a service name and protocol name.\nThe optional protocol name, if given, should be 'tcp' or 'udp',\notherwise any protocol will match.", + "_socket.getservbyport" => "getservbyport(port[, protocolname]) -> string\n\nReturn the service name from a port number and protocol name.\nThe optional protocol name, if given, should be 'tcp' or 'udp',\notherwise any protocol will match.", + "_socket.htonl" => "htonl(integer) -> integer\n\nConvert a 32-bit integer from host to network byte order.", + "_socket.htons" => "Convert a 16-bit unsigned integer from host to network byte order.", + "_socket.if_indextoname" => "if_indextoname(if_index)\n\nReturns the interface name corresponding to the interface index if_index.", + "_socket.if_nameindex" => "if_nameindex()\n\nReturns a list of network interface information (index, name) tuples.", + "_socket.if_nametoindex" => "Returns the interface index corresponding to the interface name if_name.", + "_socket.inet_aton" => "Convert an IP address in string format (123.45.67.89) to the 32-bit packed binary format used in low-level network functions.", + "_socket.inet_ntoa" => "Convert an IP address from 32-bit packed binary format to string format.", + "_socket.inet_ntop" => "inet_ntop(af, packed_ip) -> string formatted IP address\n\nConvert a packed IP address of the given family to string format.", + "_socket.inet_pton" => "inet_pton(af, ip) -> packed IP address string\n\nConvert an IP address from string format to a packed string suitable\nfor use with low-level network functions.", + "_socket.ntohl" => "ntohl(integer) -> integer\n\nConvert a 32-bit integer from network to host byte order.", + "_socket.ntohs" => "Convert a 16-bit unsigned integer from network to host byte order.", + "_socket.setdefaulttimeout" => "setdefaulttimeout(timeout)\n\nSet the default timeout in seconds (float) for new socket objects.\nA value of None indicates that new socket objects have no timeout.\nWhen the socket module is first imported, the default is None.", + "_socket.sethostname" => "sethostname(name)\n\nSets the hostname to name.", + "_socket.socket" => "socket(family=AF_INET, type=SOCK_STREAM, proto=0) -> socket object\nsocket(family=-1, type=-1, proto=-1, fileno=None) -> socket object\n\nOpen a socket of the given type. The family argument specifies the\naddress family; it defaults to AF_INET. The type argument specifies\nwhether this is a stream (SOCK_STREAM, this is the default)\nor datagram (SOCK_DGRAM) socket. The protocol argument defaults to 0,\nspecifying the default protocol. Keyword arguments are accepted.\nThe socket is created as non-inheritable.\n\nWhen a fileno is passed in, family, type and proto are auto-detected,\nunless they are explicitly set.\n\nA socket object represents one endpoint of a network connection.\n\nMethods of socket objects (keyword arguments not allowed):\n\n_accept() -- accept connection, returning new socket fd and client address\nbind(addr) -- bind the socket to a local address\nclose() -- close the socket\nconnect(addr) -- connect the socket to a remote address\nconnect_ex(addr) -- connect, return an error code instead of an exception\ndup() -- return a new socket fd duplicated from fileno()\nfileno() -- return underlying file descriptor\ngetpeername() -- return remote address [*]\ngetsockname() -- return local address\ngetsockopt(level, optname[, buflen]) -- get socket options\ngettimeout() -- return timeout or None\nlisten([n]) -- start listening for incoming connections\nrecv(buflen[, flags]) -- receive data\nrecv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\nrecvfrom(buflen[, flags]) -- receive data and sender's address\nrecvfrom_into(buffer[, nbytes, [, flags])\n -- receive data and sender's address (into a buffer)\nsendall(data[, flags]) -- send all data\nsend(data[, flags]) -- send data, may not send all of it\nsendto(data[, flags], addr) -- send data to a given address\nsetblocking(bool) -- set or clear the blocking I/O flag\ngetblocking() -- return True if socket is blocking, False if non-blocking\nsetsockopt(level, optname, value[, optlen]) -- set socket options\nsettimeout(None | float) -- set or clear the timeout\nshutdown(how) -- shut down traffic in one or both directions\n\n [*] not available on all platforms!", + "_socket.socket.__del__" => "Called when the instance is about to be destroyed.", + "_socket.socket.__delattr__" => "Implement delattr(self, name).", + "_socket.socket.__eq__" => "Return self==value.", + "_socket.socket.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_socket.socket.__ge__" => "Return self>=value.", + "_socket.socket.__getattribute__" => "Return getattr(self, name).", + "_socket.socket.__getstate__" => "Helper for pickle.", + "_socket.socket.__gt__" => "Return self>value.", + "_socket.socket.__hash__" => "Return hash(self).", + "_socket.socket.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_socket.socket.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_socket.socket.__le__" => "Return self<=value.", + "_socket.socket.__lt__" => "Return self "Return self!=value.", + "_socket.socket.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_socket.socket.__reduce__" => "Helper for pickle.", + "_socket.socket.__reduce_ex__" => "Helper for pickle.", + "_socket.socket.__repr__" => "Return repr(self).", + "_socket.socket.__setattr__" => "Implement setattr(self, name, value).", + "_socket.socket.__sizeof__" => "Size of object in memory, in bytes.", + "_socket.socket.__str__" => "Return str(self).", + "_socket.socket.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_socket.socket._accept" => "_accept() -> (integer, address info)\n\nWait for an incoming connection. Return a new socket file descriptor\nrepresenting the connection, and the address of the client.\nFor IP sockets, the address info is a pair (hostaddr, port).", + "_socket.socket.bind" => "bind(address)\n\nBind the socket to a local address. For IP sockets, the address is a\npair (host, port); the host must refer to the local host. For raw packet\nsockets the address is a tuple (ifname, proto [,pkttype [,hatype [,addr]]])", + "_socket.socket.close" => "close()\n\nClose the socket. It cannot be used after this call.", + "_socket.socket.connect" => "connect(address)\n\nConnect the socket to a remote address. For IP sockets, the address\nis a pair (host, port).", + "_socket.socket.connect_ex" => "connect_ex(address) -> errno\n\nThis is like connect(address), but returns an error code (the errno value)\ninstead of raising an exception when an error occurs.", + "_socket.socket.detach" => "detach()\n\nClose the socket object without closing the underlying file descriptor.\nThe object cannot be used after this call, but the file descriptor\ncan be reused for other purposes. The file descriptor is returned.", + "_socket.socket.family" => "the socket family", + "_socket.socket.fileno" => "fileno() -> integer\n\nReturn the integer file descriptor of the socket.", + "_socket.socket.getblocking" => "getblocking()\n\nReturns True if socket is in blocking mode, or False if it\nis in non-blocking mode.", + "_socket.socket.getpeername" => "getpeername() -> address info\n\nReturn the address of the remote endpoint. For IP sockets, the address\ninfo is a pair (hostaddr, port).", + "_socket.socket.getsockname" => "getsockname() -> address info\n\nReturn the address of the local endpoint. The format depends on the\naddress family. For IPv4 sockets, the address info is a pair\n(hostaddr, port). For IPv6 sockets, the address info is a 4-tuple\n(hostaddr, port, flowinfo, scope_id).", + "_socket.socket.getsockopt" => "getsockopt(level, option[, buffersize]) -> value\n\nGet a socket option. See the Unix manual for level and option.\nIf a nonzero buffersize argument is given, the return value is a\nstring of that length; otherwise it is an integer.", + "_socket.socket.gettimeout" => "gettimeout() -> timeout\n\nReturns the timeout in seconds (float) associated with socket\noperations. A timeout of None indicates that timeouts on socket\noperations are disabled.", + "_socket.socket.ioctl" => "ioctl(cmd, option) -> long\n\nControl the socket with WSAIoctl syscall. Currently supported 'cmd' values are\nSIO_RCVALL: 'option' must be one of the socket.RCVALL_* constants.\nSIO_KEEPALIVE_VALS: 'option' is a tuple of (onoff, timeout, interval).\nSIO_LOOPBACK_FAST_PATH: 'option' is a boolean value, and is disabled by default", + "_socket.socket.listen" => "listen([backlog])\n\nEnable a server to accept connections. If backlog is specified, it must be\nat least 0 (if it is lower, it is set to 0); it specifies the number of\nunaccepted connections that the system will allow before refusing new\nconnections. If not specified, a default reasonable value is chosen.", + "_socket.socket.proto" => "the socket protocol", + "_socket.socket.recv" => "recv(buffersize[, flags]) -> data\n\nReceive up to buffersize bytes from the socket. For the optional flags\nargument, see the Unix manual. When no data is available, block until\nat least one byte is available or until the remote end is closed. When\nthe remote end is closed and all data is read, return the empty string.", + "_socket.socket.recv_into" => "recv_into(buffer, [nbytes[, flags]]) -> nbytes_read\n\nA version of recv() that stores its data into a buffer rather than creating\na new string. Receive up to buffersize bytes from the socket. If buffersize\nis not specified (or 0), receive up to the size available in the given buffer.\n\nSee recv() for documentation about the flags.", + "_socket.socket.recvfrom" => "recvfrom(buffersize[, flags]) -> (data, address info)\n\nLike recv(buffersize, flags) but also return the sender's address info.", + "_socket.socket.recvfrom_into" => "recvfrom_into(buffer[, nbytes[, flags]]) -> (nbytes, address info)\n\nLike recv_into(buffer[, nbytes[, flags]]) but also return the sender's address info.", + "_socket.socket.recvmsg" => "recvmsg(bufsize[, ancbufsize[, flags]]) -> (data, ancdata, msg_flags, address)\n\nReceive normal data (up to bufsize bytes) and ancillary data from the\nsocket. The ancbufsize argument sets the size in bytes of the\ninternal buffer used to receive the ancillary data; it defaults to 0,\nmeaning that no ancillary data will be received. Appropriate buffer\nsizes for ancillary data can be calculated using CMSG_SPACE() or\nCMSG_LEN(), and items which do not fit into the buffer might be\ntruncated or discarded. The flags argument defaults to 0 and has the\nsame meaning as for recv().\n\nThe return value is a 4-tuple: (data, ancdata, msg_flags, address).\nThe data item is a bytes object holding the non-ancillary data\nreceived. The ancdata item is a list of zero or more tuples\n(cmsg_level, cmsg_type, cmsg_data) representing the ancillary data\n(control messages) received: cmsg_level and cmsg_type are integers\nspecifying the protocol level and protocol-specific type respectively,\nand cmsg_data is a bytes object holding the associated data. The\nmsg_flags item is the bitwise OR of various flags indicating\nconditions on the received message; see your system documentation for\ndetails. If the receiving socket is unconnected, address is the\naddress of the sending socket, if available; otherwise, its value is\nunspecified.\n\nIf recvmsg() raises an exception after the system call returns, it\nwill first attempt to close any file descriptors received via the\nSCM_RIGHTS mechanism.", + "_socket.socket.recvmsg_into" => "recvmsg_into(buffers[, ancbufsize[, flags]]) -> (nbytes, ancdata, msg_flags, address)\n\nReceive normal data and ancillary data from the socket, scattering the\nnon-ancillary data into a series of buffers. The buffers argument\nmust be an iterable of objects that export writable buffers\n(e.g. bytearray objects); these will be filled with successive chunks\nof the non-ancillary data until it has all been written or there are\nno more buffers. The ancbufsize argument sets the size in bytes of\nthe internal buffer used to receive the ancillary data; it defaults to\n0, meaning that no ancillary data will be received. Appropriate\nbuffer sizes for ancillary data can be calculated using CMSG_SPACE()\nor CMSG_LEN(), and items which do not fit into the buffer might be\ntruncated or discarded. The flags argument defaults to 0 and has the\nsame meaning as for recv().\n\nThe return value is a 4-tuple: (nbytes, ancdata, msg_flags, address).\nThe nbytes item is the total number of bytes of non-ancillary data\nwritten into the buffers. The ancdata item is a list of zero or more\ntuples (cmsg_level, cmsg_type, cmsg_data) representing the ancillary\ndata (control messages) received: cmsg_level and cmsg_type are\nintegers specifying the protocol level and protocol-specific type\nrespectively, and cmsg_data is a bytes object holding the associated\ndata. The msg_flags item is the bitwise OR of various flags\nindicating conditions on the received message; see your system\ndocumentation for details. If the receiving socket is unconnected,\naddress is the address of the sending socket, if available; otherwise,\nits value is unspecified.\n\nIf recvmsg_into() raises an exception after the system call returns,\nit will first attempt to close any file descriptors received via the\nSCM_RIGHTS mechanism.", + "_socket.socket.send" => "send(data[, flags]) -> count\n\nSend a data string to the socket. For the optional flags\nargument, see the Unix manual. Return the number of bytes\nsent; this may be less than len(data) if the network is busy.", + "_socket.socket.sendall" => "sendall(data[, flags])\n\nSend a data string to the socket. For the optional flags\nargument, see the Unix manual. This calls send() repeatedly\nuntil all data is sent. If an error occurs, it's impossible\nto tell how much data has been sent.", + "_socket.socket.sendmsg" => "sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\nSend normal and ancillary data to the socket, gathering the\nnon-ancillary data from a series of buffers and concatenating it into\na single message. The buffers argument specifies the non-ancillary\ndata as an iterable of bytes-like objects (e.g. bytes objects).\nThe ancdata argument specifies the ancillary data (control messages)\nas an iterable of zero or more tuples (cmsg_level, cmsg_type,\ncmsg_data), where cmsg_level and cmsg_type are integers specifying the\nprotocol level and protocol-specific type respectively, and cmsg_data\nis a bytes-like object holding the associated data. The flags\nargument defaults to 0 and has the same meaning as for send(). If\naddress is supplied and not None, it sets a destination address for\nthe message. The return value is the number of bytes of non-ancillary\ndata sent.", + "_socket.socket.sendmsg_afalg" => "sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags=MSG_MORE]]])\n\nSet operation mode, IV and length of associated data for an AF_ALG\noperation socket.", + "_socket.socket.sendto" => "sendto(data[, flags], address) -> count\n\nLike send(data, flags) but allows specifying the destination address.\nFor IP sockets, the address is a pair (hostaddr, port).", + "_socket.socket.setblocking" => "setblocking(flag)\n\nSet the socket to blocking (flag is true) or non-blocking (false).\nsetblocking(True) is equivalent to settimeout(None);\nsetblocking(False) is equivalent to settimeout(0.0).", + "_socket.socket.setsockopt" => "setsockopt(level, option, value: int)\nsetsockopt(level, option, value: buffer)\nsetsockopt(level, option, None, optlen: int)\n\nSet a socket option. See the Unix manual for level and option.\nThe value argument can either be an integer, a string buffer, or\nNone, optlen.", + "_socket.socket.settimeout" => "settimeout(timeout)\n\nSet a timeout on socket operations. 'timeout' can be a float,\ngiving in seconds, or None. Setting a timeout of None disables\nthe timeout feature and is equivalent to setblocking(1).\nSetting a timeout of zero is the same as setblocking(0).", + "_socket.socket.share" => "share(process_id) -> bytes\n\nShare the socket with another process. The target process id\nmust be provided and the resulting bytes object passed to the target\nprocess. There the shared socket can be instantiated by calling\nsocket.fromshare().", + "_socket.socket.shutdown" => "shutdown(flag)\n\nShut down the reading side of the socket (flag == SHUT_RD), the writing side\nof the socket (flag == SHUT_WR), or both ends (flag == SHUT_RDWR).", + "_socket.socket.timeout" => "the socket timeout", + "_socket.socket.type" => "the socket type", + "_socket.socketpair" => "socketpair([family[, type [, proto]]]) -> (socket object, socket object)\n\nCreate a pair of socket objects from the sockets returned by the platform\nsocketpair() function.\nThe arguments are the same as for socket() except the default family is\nAF_UNIX if defined on the platform; otherwise, the default is AF_INET.", + "_sqlite3.adapt" => "Adapt given object to given protocol.", + "_sqlite3.complete_statement" => "Checks if a string contains a complete SQL statement.", + "_sqlite3.connect" => "Open a connection to the SQLite database file 'database'.\n\nYou can use \":memory:\" to open a database connection to a database that\nresides in RAM instead of on disk.\n\nNote: Passing more than 1 positional argument to _sqlite3.connect() is\ndeprecated. Parameters 'timeout', 'detect_types', 'isolation_level',\n'check_same_thread', 'factory', 'cached_statements' and 'uri' will\nbecome keyword-only parameters in Python 3.15.", + "_sqlite3.enable_callback_tracebacks" => "Enable or disable callback functions throwing errors to stderr.", + "_sqlite3.register_adapter" => "Register a function to adapt Python objects to SQLite values.", + "_sqlite3.register_converter" => "Register a function to convert SQLite values to Python objects.", + "_sre.template" => "template\n A list containing interleaved literal strings (str or bytes) and group\n indices (int), as returned by re._parser.parse_template():\n [literal1, group1, ..., literalN, groupN]", + "_ssl" => "Implementation module for SSL socket operations. See the socket module\nfor documentation.", + "_ssl.Certificate.__delattr__" => "Implement delattr(self, name).", + "_ssl.Certificate.__eq__" => "Return self==value.", + "_ssl.Certificate.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ssl.Certificate.__ge__" => "Return self>=value.", + "_ssl.Certificate.__getattribute__" => "Return getattr(self, name).", + "_ssl.Certificate.__getstate__" => "Helper for pickle.", + "_ssl.Certificate.__gt__" => "Return self>value.", + "_ssl.Certificate.__hash__" => "Return hash(self).", + "_ssl.Certificate.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ssl.Certificate.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ssl.Certificate.__le__" => "Return self<=value.", + "_ssl.Certificate.__lt__" => "Return self "Return self!=value.", + "_ssl.Certificate.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ssl.Certificate.__reduce__" => "Helper for pickle.", + "_ssl.Certificate.__reduce_ex__" => "Helper for pickle.", + "_ssl.Certificate.__repr__" => "Return repr(self).", + "_ssl.Certificate.__setattr__" => "Implement setattr(self, name, value).", + "_ssl.Certificate.__sizeof__" => "Size of object in memory, in bytes.", + "_ssl.Certificate.__str__" => "Return str(self).", + "_ssl.Certificate.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ssl.MemoryBIO.__delattr__" => "Implement delattr(self, name).", + "_ssl.MemoryBIO.__eq__" => "Return self==value.", + "_ssl.MemoryBIO.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ssl.MemoryBIO.__ge__" => "Return self>=value.", + "_ssl.MemoryBIO.__getattribute__" => "Return getattr(self, name).", + "_ssl.MemoryBIO.__getstate__" => "Helper for pickle.", + "_ssl.MemoryBIO.__gt__" => "Return self>value.", + "_ssl.MemoryBIO.__hash__" => "Return hash(self).", + "_ssl.MemoryBIO.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ssl.MemoryBIO.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ssl.MemoryBIO.__le__" => "Return self<=value.", + "_ssl.MemoryBIO.__lt__" => "Return self "Return self!=value.", + "_ssl.MemoryBIO.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ssl.MemoryBIO.__reduce__" => "Helper for pickle.", + "_ssl.MemoryBIO.__reduce_ex__" => "Helper for pickle.", + "_ssl.MemoryBIO.__repr__" => "Return repr(self).", + "_ssl.MemoryBIO.__setattr__" => "Implement setattr(self, name, value).", + "_ssl.MemoryBIO.__sizeof__" => "Size of object in memory, in bytes.", + "_ssl.MemoryBIO.__str__" => "Return str(self).", + "_ssl.MemoryBIO.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ssl.MemoryBIO.eof" => "Whether the memory BIO is at EOF.", + "_ssl.MemoryBIO.pending" => "The number of bytes pending in the memory BIO.", + "_ssl.MemoryBIO.read" => "Read up to size bytes from the memory BIO.\n\nIf size is not specified, read the entire buffer.\nIf the return value is an empty bytes instance, this means either\nEOF or that no data is available. Use the \"eof\" property to\ndistinguish between the two.", + "_ssl.MemoryBIO.write" => "Writes the bytes b into the memory BIO.\n\nReturns the number of bytes written.", + "_ssl.MemoryBIO.write_eof" => "Write an EOF marker to the memory BIO.\n\nWhen all data has been read, the \"eof\" property will be True.", + "_ssl.RAND_add" => "Mix string into the OpenSSL PRNG state.\n\nentropy (a float) is a lower bound on the entropy contained in\nstring. See RFC 4086.", + "_ssl.RAND_bytes" => "Generate n cryptographically strong pseudo-random bytes.", + "_ssl.RAND_status" => "Returns True if the OpenSSL PRNG has been seeded with enough data and False if not.\n\nIt is necessary to seed the PRNG with RAND_add() on some platforms before\nusing the ssl() function.", + "_ssl.SSLSession.__delattr__" => "Implement delattr(self, name).", + "_ssl.SSLSession.__eq__" => "Return self==value.", + "_ssl.SSLSession.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ssl.SSLSession.__ge__" => "Return self>=value.", + "_ssl.SSLSession.__getattribute__" => "Return getattr(self, name).", + "_ssl.SSLSession.__getstate__" => "Helper for pickle.", + "_ssl.SSLSession.__gt__" => "Return self>value.", + "_ssl.SSLSession.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ssl.SSLSession.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ssl.SSLSession.__le__" => "Return self<=value.", + "_ssl.SSLSession.__lt__" => "Return self "Return self!=value.", + "_ssl.SSLSession.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ssl.SSLSession.__reduce__" => "Helper for pickle.", + "_ssl.SSLSession.__reduce_ex__" => "Helper for pickle.", + "_ssl.SSLSession.__repr__" => "Return repr(self).", + "_ssl.SSLSession.__setattr__" => "Implement setattr(self, name, value).", + "_ssl.SSLSession.__sizeof__" => "Size of object in memory, in bytes.", + "_ssl.SSLSession.__str__" => "Return str(self).", + "_ssl.SSLSession.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ssl.SSLSession.has_ticket" => "Does the session contain a ticket?", + "_ssl.SSLSession.id" => "Session ID.", + "_ssl.SSLSession.ticket_lifetime_hint" => "Ticket life time hint.", + "_ssl.SSLSession.time" => "Session creation time (seconds since epoch).", + "_ssl.SSLSession.timeout" => "Session timeout (delta in seconds).", + "_ssl._SSLContext.__delattr__" => "Implement delattr(self, name).", + "_ssl._SSLContext.__eq__" => "Return self==value.", + "_ssl._SSLContext.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ssl._SSLContext.__ge__" => "Return self>=value.", + "_ssl._SSLContext.__getattribute__" => "Return getattr(self, name).", + "_ssl._SSLContext.__getstate__" => "Helper for pickle.", + "_ssl._SSLContext.__gt__" => "Return self>value.", + "_ssl._SSLContext.__hash__" => "Return hash(self).", + "_ssl._SSLContext.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ssl._SSLContext.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ssl._SSLContext.__le__" => "Return self<=value.", + "_ssl._SSLContext.__lt__" => "Return self "Return self!=value.", + "_ssl._SSLContext.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ssl._SSLContext.__reduce__" => "Helper for pickle.", + "_ssl._SSLContext.__reduce_ex__" => "Helper for pickle.", + "_ssl._SSLContext.__repr__" => "Return repr(self).", + "_ssl._SSLContext.__setattr__" => "Implement setattr(self, name, value).", + "_ssl._SSLContext.__sizeof__" => "Size of object in memory, in bytes.", + "_ssl._SSLContext.__str__" => "Return str(self).", + "_ssl._SSLContext.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ssl._SSLContext.cert_store_stats" => "Returns quantities of loaded X.509 certificates.\n\nX.509 certificates with a CA extension and certificate revocation lists\ninside the context's cert store.\n\nNOTE: Certificates in a capath directory aren't loaded unless they have\nbeen used at least once.", + "_ssl._SSLContext.get_ca_certs" => "Returns a list of dicts with information of loaded CA certs.\n\nIf the optional argument is True, returns a DER-encoded copy of the CA\ncertificate.\n\nNOTE: Certificates in a capath directory aren't loaded unless they have\nbeen used at least once.", + "_ssl._SSLContext.num_tickets" => "Control the number of TLSv1.3 session tickets.", + "_ssl._SSLContext.security_level" => "The current security level.", + "_ssl._SSLContext.sni_callback" => "Set a callback that will be called when a server name is provided by the SSL/TLS client in the SNI extension.\n\nIf the argument is None then the callback is disabled. The method is called\nwith the SSLSocket, the server name as a string, and the SSLContext object.\n\nSee RFC 6066 for details of the SNI extension.", + "_ssl._SSLSocket.__delattr__" => "Implement delattr(self, name).", + "_ssl._SSLSocket.__eq__" => "Return self==value.", + "_ssl._SSLSocket.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_ssl._SSLSocket.__ge__" => "Return self>=value.", + "_ssl._SSLSocket.__getattribute__" => "Return getattr(self, name).", + "_ssl._SSLSocket.__getstate__" => "Helper for pickle.", + "_ssl._SSLSocket.__gt__" => "Return self>value.", + "_ssl._SSLSocket.__hash__" => "Return hash(self).", + "_ssl._SSLSocket.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_ssl._SSLSocket.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_ssl._SSLSocket.__le__" => "Return self<=value.", + "_ssl._SSLSocket.__lt__" => "Return self "Return self!=value.", + "_ssl._SSLSocket.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_ssl._SSLSocket.__reduce__" => "Helper for pickle.", + "_ssl._SSLSocket.__reduce_ex__" => "Helper for pickle.", + "_ssl._SSLSocket.__repr__" => "Return repr(self).", + "_ssl._SSLSocket.__setattr__" => "Implement setattr(self, name, value).", + "_ssl._SSLSocket.__sizeof__" => "Size of object in memory, in bytes.", + "_ssl._SSLSocket.__str__" => "Return str(self).", + "_ssl._SSLSocket.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_ssl._SSLSocket.context" => "This changes the context associated with the SSLSocket.\n\nThis is typically used from within a callback function set by the sni_callback\non the SSLContext to change the certificate information associated with the\nSSLSocket before the cryptographic exchange handshake messages.", + "_ssl._SSLSocket.get_channel_binding" => "Get channel binding data for current connection.\n\nRaise ValueError if the requested `cb_type` is not supported. Return bytes\nof the data or None if the data is not available (e.g. before the handshake).\nOnly 'tls-unique' channel binding data from RFC 5929 is supported.", + "_ssl._SSLSocket.getpeercert" => "Returns the certificate for the peer.\n\nIf no certificate was provided, returns None. If a certificate was\nprovided, but not validated, returns an empty dictionary. Otherwise\nreturns a dict containing information about the peer certificate.\n\nIf the optional argument is True, returns a DER-encoded copy of the\npeer certificate, or None if no certificate was provided. This will\nreturn the certificate even if it wasn't validated.", + "_ssl._SSLSocket.owner" => "The Python-level owner of this object.\n\nPassed as \"self\" in servername callback.", + "_ssl._SSLSocket.pending" => "Returns the number of already decrypted bytes available for read, pending on the connection.", + "_ssl._SSLSocket.read" => "read(size, [buffer])\nRead up to size bytes from the SSL socket.", + "_ssl._SSLSocket.server_hostname" => "The currently set server hostname (for SNI).", + "_ssl._SSLSocket.server_side" => "Whether this is a server-side socket.", + "_ssl._SSLSocket.session" => "The underlying SSLSession object.", + "_ssl._SSLSocket.session_reused" => "Was the client session reused during handshake?", + "_ssl._SSLSocket.shutdown" => "Does the SSL shutdown handshake with the remote end.", + "_ssl._SSLSocket.verify_client_post_handshake" => "Initiate TLS 1.3 post-handshake authentication", + "_ssl._SSLSocket.write" => "Writes the bytes-like object b into the SSL object.\n\nReturns the number of bytes written.", + "_ssl.enum_certificates" => "Retrieve certificates from Windows' cert store.\n\nstore_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide\nmore cert storages, too. The function returns a list of (bytes,\nencoding_type, trust) tuples. The encoding_type flag can be interpreted\nwith X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either\na set of OIDs or the boolean True.", + "_ssl.enum_crls" => "Retrieve CRLs from Windows' cert store.\n\nstore_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide\nmore cert storages, too. The function returns a list of (bytes,\nencoding_type) tuples. The encoding_type flag can be interpreted with\nX509_ASN_ENCODING or PKCS_7_ASN_ENCODING.", + "_ssl.get_default_verify_paths" => "Return search paths and environment vars that are used by SSLContext's set_default_verify_paths() to load default CAs.\n\nThe values are 'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'.", + "_ssl.nid2obj" => "Lookup NID, short name, long name and OID of an ASN1_OBJECT by NID.", + "_ssl.txt2obj" => "Lookup NID, short name, long name and OID of an ASN1_OBJECT.\n\nBy default objects are looked up by OID. With name=True short and\nlong name are also matched.", + "_stat" => "S_IFMT_: file type bits\nS_IFDIR: directory\nS_IFCHR: character device\nS_IFBLK: block device\nS_IFREG: regular file\nS_IFIFO: fifo (named pipe)\nS_IFLNK: symbolic link\nS_IFSOCK: socket file\nS_IFDOOR: door\nS_IFPORT: event port\nS_IFWHT: whiteout\n\nS_ISUID: set UID bit\nS_ISGID: set GID bit\nS_ENFMT: file locking enforcement\nS_ISVTX: sticky bit\nS_IREAD: Unix V7 synonym for S_IRUSR\nS_IWRITE: Unix V7 synonym for S_IWUSR\nS_IEXEC: Unix V7 synonym for S_IXUSR\nS_IRWXU: mask for owner permissions\nS_IRUSR: read by owner\nS_IWUSR: write by owner\nS_IXUSR: execute by owner\nS_IRWXG: mask for group permissions\nS_IRGRP: read by group\nS_IWGRP: write by group\nS_IXGRP: execute by group\nS_IRWXO: mask for others (not in group) permissions\nS_IROTH: read by others\nS_IWOTH: write by others\nS_IXOTH: execute by others\n\nUF_SETTABLE: mask of owner changable flags\nUF_NODUMP: do not dump file\nUF_IMMUTABLE: file may not be changed\nUF_APPEND: file may only be appended to\nUF_OPAQUE: directory is opaque when viewed through a union stack\nUF_NOUNLINK: file may not be renamed or deleted\nUF_COMPRESSED: macOS: file is hfs-compressed\nUF_TRACKED: used for dealing with document IDs\nUF_DATAVAULT: entitlement required for reading and writing\nUF_HIDDEN: macOS: file should not be displayed\nSF_SETTABLE: mask of super user changeable flags\nSF_ARCHIVED: file may be archived\nSF_IMMUTABLE: file may not be changed\nSF_APPEND: file may only be appended to\nSF_RESTRICTED: entitlement required for writing\nSF_NOUNLINK: file may not be renamed or deleted\nSF_SNAPSHOT: file is a snapshot file\nSF_FIRMLINK: file is a firmlink\nSF_DATALESS: file is a dataless object\n\nOn macOS:\nSF_SUPPORTED: mask of super user supported flags\nSF_SYNTHETIC: mask of read-only synthetic flags\n\nST_MODE\nST_INO\nST_DEV\nST_NLINK\nST_UID\nST_GID\nST_SIZE\nST_ATIME\nST_MTIME\nST_CTIME\n\nFILE_ATTRIBUTE_*: Windows file attribute constants\n (only present on Windows)", + "_stat.S_IFMT" => "Return the portion of the file's mode that describes the file type.", + "_stat.S_IMODE" => "Return the portion of the file's mode that can be set by os.chmod().", + "_stat.S_ISBLK" => "S_ISBLK(mode) -> bool\n\nReturn True if mode is from a block special device file.", + "_stat.S_ISCHR" => "S_ISCHR(mode) -> bool\n\nReturn True if mode is from a character special device file.", + "_stat.S_ISDIR" => "S_ISDIR(mode) -> bool\n\nReturn True if mode is from a directory.", + "_stat.S_ISDOOR" => "S_ISDOOR(mode) -> bool\n\nReturn True if mode is from a door.", + "_stat.S_ISFIFO" => "S_ISFIFO(mode) -> bool\n\nReturn True if mode is from a FIFO (named pipe).", + "_stat.S_ISLNK" => "S_ISLNK(mode) -> bool\n\nReturn True if mode is from a symbolic link.", + "_stat.S_ISPORT" => "S_ISPORT(mode) -> bool\n\nReturn True if mode is from an event port.", + "_stat.S_ISREG" => "S_ISREG(mode) -> bool\n\nReturn True if mode is from a regular file.", + "_stat.S_ISSOCK" => "S_ISSOCK(mode) -> bool\n\nReturn True if mode is from a socket.", + "_stat.S_ISWHT" => "S_ISWHT(mode) -> bool\n\nReturn True if mode is from a whiteout.", + "_stat.filemode" => "Convert a file's mode to a string of the form '-rwxrwxrwx'", + "_statistics" => "Accelerators for the statistics module.", + "_string" => "string helper module", + "_string.formatter_field_name_split" => "split the argument as a field name", + "_string.formatter_parser" => "parse the argument as a format string", + "_struct" => "Functions to convert between Python values and C structs.\nPython bytes objects are used to hold the data representing the C struct\nand also as format strings (explained below) to describe the layout of data\nin the C struct.\n\nThe optional first format char indicates byte order, size and alignment:\n @: native order, size & alignment (default)\n =: native order, std. size & alignment\n <: little-endian, std. size & alignment\n >: big-endian, std. size & alignment\n !: same as >\n\nThe remaining chars indicate types of args and must match exactly;\nthese can be preceded by a decimal repeat count:\n x: pad byte (no data); c:char; b:signed byte; B:unsigned byte;\n ?: _Bool (requires C99; if not available, char is used instead)\n h:short; H:unsigned short; i:int; I:unsigned int;\n l:long; L:unsigned long; f:float; d:double; e:half-float.\nSpecial cases (preceding decimal count indicates length):\n s:string (array of char); p: pascal string (with count byte).\nSpecial cases (only available in native format):\n n:ssize_t; N:size_t;\n P:an integer type that is wide enough to hold a pointer.\nSpecial case (not in native mode unless 'long long' in platform C):\n q:long long; Q:unsigned long long\nWhitespace between formats is ignored.\n\nThe variable struct.error is an exception raised on errors.", + "_struct.Struct" => "Struct(fmt) --> compiled struct object", + "_struct.Struct.__delattr__" => "Implement delattr(self, name).", + "_struct.Struct.__eq__" => "Return self==value.", + "_struct.Struct.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_struct.Struct.__ge__" => "Return self>=value.", + "_struct.Struct.__getattribute__" => "Return getattr(self, name).", + "_struct.Struct.__getstate__" => "Helper for pickle.", + "_struct.Struct.__gt__" => "Return self>value.", + "_struct.Struct.__hash__" => "Return hash(self).", + "_struct.Struct.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_struct.Struct.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_struct.Struct.__le__" => "Return self<=value.", + "_struct.Struct.__lt__" => "Return self "Return self!=value.", + "_struct.Struct.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_struct.Struct.__reduce__" => "Helper for pickle.", + "_struct.Struct.__reduce_ex__" => "Helper for pickle.", + "_struct.Struct.__repr__" => "Return repr(self).", + "_struct.Struct.__setattr__" => "Implement setattr(self, name, value).", + "_struct.Struct.__sizeof__" => "S.__sizeof__() -> size of S in memory, in bytes", + "_struct.Struct.__str__" => "Return str(self).", + "_struct.Struct.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_struct.Struct.format" => "struct format string", + "_struct.Struct.iter_unpack" => "Return an iterator yielding tuples.\n\nTuples are unpacked from the given bytes source, like a repeated\ninvocation of unpack_from().\n\nRequires that the bytes length be a multiple of the struct size.", + "_struct.Struct.pack" => "S.pack(v1, v2, ...) -> bytes\n\nReturn a bytes object containing values v1, v2, ... packed according\nto the format string S.format. See help(struct) for more on format\nstrings.", + "_struct.Struct.pack_into" => "S.pack_into(buffer, offset, v1, v2, ...)\n\nPack the values v1, v2, ... according to the format string S.format\nand write the packed bytes into the writable buffer buf starting at\noffset. Note that the offset is a required argument. See\nhelp(struct) for more on format strings.", + "_struct.Struct.size" => "struct size in bytes", + "_struct.Struct.unpack" => "Return a tuple containing unpacked values.\n\nUnpack according to the format string Struct.format. The buffer's size\nin bytes must be Struct.size.\n\nSee help(struct) for more on format strings.", + "_struct.Struct.unpack_from" => "Return a tuple containing unpacked values.\n\nValues are unpacked according to the format string Struct.format.\n\nThe buffer's size in bytes, starting at position offset, must be\nat least Struct.size.\n\nSee help(struct) for more on format strings.", + "_struct._clearcache" => "Clear the internal cache.", + "_struct.calcsize" => "Return size in bytes of the struct described by the format string.", + "_struct.iter_unpack" => "Return an iterator yielding tuples unpacked from the given bytes.\n\nThe bytes are unpacked according to the format string, like\na repeated invocation of unpack_from().\n\nRequires that the bytes length be a multiple of the format struct size.", + "_struct.pack" => "pack(format, v1, v2, ...) -> bytes\n\nReturn a bytes object containing the values v1, v2, ... packed according\nto the format string. See help(struct) for more on format strings.", + "_struct.pack_into" => "pack_into(format, buffer, offset, v1, v2, ...)\n\nPack the values v1, v2, ... according to the format string and write\nthe packed bytes into the writable buffer buf starting at offset. Note\nthat the offset is a required argument. See help(struct) for more\non format strings.", + "_struct.unpack" => "Return a tuple containing values unpacked according to the format string.\n\nThe buffer's size in bytes must be calcsize(format).\n\nSee help(struct) for more on format strings.", + "_struct.unpack_from" => "Return a tuple containing values unpacked according to the format string.\n\nThe buffer's size, minus offset, must be at least calcsize(format).\n\nSee help(struct) for more on format strings.", + "_suggestions._generate_suggestions" => "Returns the candidate in candidates that's closest to item", + "_symtable.symtable" => "Return symbol and scope dictionaries used internally by compiler.", + "_sysconfig" => "A helper for the sysconfig module.", + "_sysconfig.config_vars" => "Returns a dictionary containing build variables intended to be exposed by sysconfig.", + "_thread" => "This module provides primitive operations to write multi-threaded programs.\nThe 'threading' module provides a more convenient interface.", + "_thread.LockType" => "A lock object is a synchronization primitive. To create a lock,\ncall threading.Lock(). Methods are:\n\nacquire() -- lock the lock, possibly blocking until it can be obtained\nrelease() -- unlock of the lock\nlocked() -- test whether the lock is currently locked\n\nA lock is not owned by the thread that locked it; another thread may\nunlock it. A thread attempting to lock a lock that it has already locked\nwill block until another thread unlocks it. Deadlocks may ensue.", + "_thread.LockType.__delattr__" => "Implement delattr(self, name).", + "_thread.LockType.__enter__" => "Lock the lock.", + "_thread.LockType.__eq__" => "Return self==value.", + "_thread.LockType.__exit__" => "Release the lock.", + "_thread.LockType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread.LockType.__ge__" => "Return self>=value.", + "_thread.LockType.__getattribute__" => "Return getattr(self, name).", + "_thread.LockType.__getstate__" => "Helper for pickle.", + "_thread.LockType.__gt__" => "Return self>value.", + "_thread.LockType.__hash__" => "Return hash(self).", + "_thread.LockType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread.LockType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread.LockType.__le__" => "Return self<=value.", + "_thread.LockType.__lt__" => "Return self "Return self!=value.", + "_thread.LockType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread.LockType.__reduce__" => "Helper for pickle.", + "_thread.LockType.__reduce_ex__" => "Helper for pickle.", + "_thread.LockType.__repr__" => "Return repr(self).", + "_thread.LockType.__setattr__" => "Implement setattr(self, name, value).", + "_thread.LockType.__sizeof__" => "Size of object in memory, in bytes.", + "_thread.LockType.__str__" => "Return str(self).", + "_thread.LockType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread.LockType.acquire" => "Lock the lock. Without argument, this blocks if the lock is already\nlocked (even by the same thread), waiting for another thread to release\nthe lock, and return True once the lock is acquired.\nWith an argument, this will only block if the argument is true,\nand the return value reflects whether the lock is acquired.\nThe blocking operation is interruptible.", + "_thread.LockType.acquire_lock" => "An obsolete synonym of acquire().", + "_thread.LockType.locked" => "Return whether the lock is in the locked state.", + "_thread.LockType.locked_lock" => "An obsolete synonym of locked().", + "_thread.LockType.release" => "Release the lock, allowing another thread that is blocked waiting for\nthe lock to acquire the lock. The lock must be in the locked state,\nbut it needn't be locked by the same thread that unlocks it.", + "_thread.LockType.release_lock" => "An obsolete synonym of release().", + "_thread.RLock.__delattr__" => "Implement delattr(self, name).", + "_thread.RLock.__enter__" => "Lock the lock.", + "_thread.RLock.__eq__" => "Return self==value.", + "_thread.RLock.__exit__" => "Release the lock.", + "_thread.RLock.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread.RLock.__ge__" => "Return self>=value.", + "_thread.RLock.__getattribute__" => "Return getattr(self, name).", + "_thread.RLock.__getstate__" => "Helper for pickle.", + "_thread.RLock.__gt__" => "Return self>value.", + "_thread.RLock.__hash__" => "Return hash(self).", + "_thread.RLock.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread.RLock.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread.RLock.__le__" => "Return self<=value.", + "_thread.RLock.__lt__" => "Return self "Return self!=value.", + "_thread.RLock.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread.RLock.__reduce__" => "Helper for pickle.", + "_thread.RLock.__reduce_ex__" => "Helper for pickle.", + "_thread.RLock.__repr__" => "Return repr(self).", + "_thread.RLock.__setattr__" => "Implement setattr(self, name, value).", + "_thread.RLock.__sizeof__" => "Size of object in memory, in bytes.", + "_thread.RLock.__str__" => "Return str(self).", + "_thread.RLock.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread.RLock._acquire_restore" => "For internal use by `threading.Condition`.", + "_thread.RLock._is_owned" => "For internal use by `threading.Condition`.", + "_thread.RLock._recursion_count" => "For internal use by reentrancy checks.", + "_thread.RLock._release_save" => "For internal use by `threading.Condition`.", + "_thread.RLock.acquire" => "Lock the lock. `blocking` indicates whether we should wait\nfor the lock to be available or not. If `blocking` is False\nand another thread holds the lock, the method will return False\nimmediately. If `blocking` is True and another thread holds\nthe lock, the method will wait for the lock to be released,\ntake it and then return True.\n(note: the blocking operation is interruptible.)\n\nIn all other cases, the method will return True immediately.\nPrecisely, if the current thread already holds the lock, its\ninternal counter is simply incremented. If nobody holds the lock,\nthe lock is taken and its internal counter initialized to 1.", + "_thread.RLock.release" => "Release the lock, allowing another thread that is blocked waiting for\nthe lock to acquire the lock. The lock must be in the locked state,\nand must be locked by the same thread that unlocks it; otherwise a\n`RuntimeError` is raised.\n\nDo note that if the lock was acquire()d several times in a row by the\ncurrent thread, release() needs to be called as many times for the lock\nto be available for other threads.", + "_thread._ExceptHookArgs" => "ExceptHookArgs\n\nType used to pass arguments to threading.excepthook.", + "_thread._ExceptHookArgs.__add__" => "Return self+value.", + "_thread._ExceptHookArgs.__class_getitem__" => "See PEP 585", + "_thread._ExceptHookArgs.__contains__" => "Return bool(key in self).", + "_thread._ExceptHookArgs.__delattr__" => "Implement delattr(self, name).", + "_thread._ExceptHookArgs.__eq__" => "Return self==value.", + "_thread._ExceptHookArgs.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread._ExceptHookArgs.__ge__" => "Return self>=value.", + "_thread._ExceptHookArgs.__getattribute__" => "Return getattr(self, name).", + "_thread._ExceptHookArgs.__getitem__" => "Return self[key].", + "_thread._ExceptHookArgs.__getstate__" => "Helper for pickle.", + "_thread._ExceptHookArgs.__gt__" => "Return self>value.", + "_thread._ExceptHookArgs.__hash__" => "Return hash(self).", + "_thread._ExceptHookArgs.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread._ExceptHookArgs.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread._ExceptHookArgs.__iter__" => "Implement iter(self).", + "_thread._ExceptHookArgs.__le__" => "Return self<=value.", + "_thread._ExceptHookArgs.__len__" => "Return len(self).", + "_thread._ExceptHookArgs.__lt__" => "Return self "Return self*value.", + "_thread._ExceptHookArgs.__ne__" => "Return self!=value.", + "_thread._ExceptHookArgs.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread._ExceptHookArgs.__reduce_ex__" => "Helper for pickle.", + "_thread._ExceptHookArgs.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "_thread._ExceptHookArgs.__repr__" => "Return repr(self).", + "_thread._ExceptHookArgs.__rmul__" => "Return value*self.", + "_thread._ExceptHookArgs.__setattr__" => "Implement setattr(self, name, value).", + "_thread._ExceptHookArgs.__sizeof__" => "Size of object in memory, in bytes.", + "_thread._ExceptHookArgs.__str__" => "Return str(self).", + "_thread._ExceptHookArgs.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread._ExceptHookArgs.count" => "Return number of occurrences of value.", + "_thread._ExceptHookArgs.exc_traceback" => "Exception traceback", + "_thread._ExceptHookArgs.exc_type" => "Exception type", + "_thread._ExceptHookArgs.exc_value" => "Exception value", + "_thread._ExceptHookArgs.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "_thread._ExceptHookArgs.thread" => "Thread", + "_thread._ThreadHandle.__delattr__" => "Implement delattr(self, name).", + "_thread._ThreadHandle.__eq__" => "Return self==value.", + "_thread._ThreadHandle.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread._ThreadHandle.__ge__" => "Return self>=value.", + "_thread._ThreadHandle.__getattribute__" => "Return getattr(self, name).", + "_thread._ThreadHandle.__getstate__" => "Helper for pickle.", + "_thread._ThreadHandle.__gt__" => "Return self>value.", + "_thread._ThreadHandle.__hash__" => "Return hash(self).", + "_thread._ThreadHandle.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread._ThreadHandle.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread._ThreadHandle.__le__" => "Return self<=value.", + "_thread._ThreadHandle.__lt__" => "Return self "Return self!=value.", + "_thread._ThreadHandle.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread._ThreadHandle.__reduce__" => "Helper for pickle.", + "_thread._ThreadHandle.__reduce_ex__" => "Helper for pickle.", + "_thread._ThreadHandle.__repr__" => "Return repr(self).", + "_thread._ThreadHandle.__setattr__" => "Implement setattr(self, name, value).", + "_thread._ThreadHandle.__sizeof__" => "Size of object in memory, in bytes.", + "_thread._ThreadHandle.__str__" => "Return str(self).", + "_thread._ThreadHandle.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread._count" => "Return the number of currently running Python threads, excluding\nthe main thread. The returned number comprises all threads created\nthrough `start_new_thread()` as well as `threading.Thread`, and not\nyet finished.\n\nThis function is meant for internal and specialized purposes only.\nIn most applications `threading.enumerate()` should be used instead.", + "_thread._excepthook" => "Handle uncaught Thread.run() exception.", + "_thread._get_main_thread_ident" => "Internal only. Return a non-zero integer that uniquely identifies the main thread\nof the main interpreter.", + "_thread._is_main_interpreter" => "Return True if the current interpreter is the main Python interpreter.", + "_thread._local" => "Thread-local data", + "_thread._local.__delattr__" => "Implement delattr(self, name).", + "_thread._local.__eq__" => "Return self==value.", + "_thread._local.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread._local.__ge__" => "Return self>=value.", + "_thread._local.__getattribute__" => "Return getattr(self, name).", + "_thread._local.__getstate__" => "Helper for pickle.", + "_thread._local.__gt__" => "Return self>value.", + "_thread._local.__hash__" => "Return hash(self).", + "_thread._local.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread._local.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread._local.__le__" => "Return self<=value.", + "_thread._local.__lt__" => "Return self "Return self!=value.", + "_thread._local.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread._local.__reduce__" => "Helper for pickle.", + "_thread._local.__reduce_ex__" => "Helper for pickle.", + "_thread._local.__repr__" => "Return repr(self).", + "_thread._local.__setattr__" => "Implement setattr(self, name, value).", + "_thread._local.__sizeof__" => "Size of object in memory, in bytes.", + "_thread._local.__str__" => "Return str(self).", + "_thread._local.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread._make_thread_handle" => "Internal only. Make a thread handle for threads not spawned\nby the _thread or threading module.", + "_thread._shutdown" => "Wait for all non-daemon threads (other than the calling thread) to stop.", + "_thread.allocate" => "An obsolete synonym of allocate_lock().", + "_thread.allocate_lock" => "Create a new lock object. See help(type(threading.Lock())) for\ninformation about locks.", + "_thread.daemon_threads_allowed" => "Return True if daemon threads are allowed in the current interpreter,\nand False otherwise.", + "_thread.exit" => "This is synonymous to ``raise SystemExit''. It will cause the current\nthread to exit silently unless the exception is caught.", + "_thread.exit_thread" => "An obsolete synonym of exit().", + "_thread.get_ident" => "Return a non-zero integer that uniquely identifies the current thread\namongst other threads that exist simultaneously.\nThis may be used to identify per-thread resources.\nEven though on some platforms threads identities may appear to be\nallocated consecutive numbers starting at 1, this behavior should not\nbe relied upon, and the number should be seen purely as a magic cookie.\nA thread's identity may be reused for another thread after it exits.", + "_thread.get_native_id" => "Return a non-negative integer identifying the thread as reported\nby the OS (kernel). This may be used to uniquely identify a\nparticular thread within a system.", + "_thread.interrupt_main" => "Simulate the arrival of the given signal in the main thread,\nwhere the corresponding signal handler will be executed.\nIf *signum* is omitted, SIGINT is assumed.\nA subthread can use this function to interrupt the main thread.\n\nNote: the default signal handler for SIGINT raises ``KeyboardInterrupt``.", + "_thread.lock" => "A lock object is a synchronization primitive. To create a lock,\ncall threading.Lock(). Methods are:\n\nacquire() -- lock the lock, possibly blocking until it can be obtained\nrelease() -- unlock of the lock\nlocked() -- test whether the lock is currently locked\n\nA lock is not owned by the thread that locked it; another thread may\nunlock it. A thread attempting to lock a lock that it has already locked\nwill block until another thread unlocks it. Deadlocks may ensue.", + "_thread.lock.__delattr__" => "Implement delattr(self, name).", + "_thread.lock.__enter__" => "Lock the lock.", + "_thread.lock.__eq__" => "Return self==value.", + "_thread.lock.__exit__" => "Release the lock.", + "_thread.lock.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_thread.lock.__ge__" => "Return self>=value.", + "_thread.lock.__getattribute__" => "Return getattr(self, name).", + "_thread.lock.__getstate__" => "Helper for pickle.", + "_thread.lock.__gt__" => "Return self>value.", + "_thread.lock.__hash__" => "Return hash(self).", + "_thread.lock.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_thread.lock.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_thread.lock.__le__" => "Return self<=value.", + "_thread.lock.__lt__" => "Return self "Return self!=value.", + "_thread.lock.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_thread.lock.__reduce__" => "Helper for pickle.", + "_thread.lock.__reduce_ex__" => "Helper for pickle.", + "_thread.lock.__repr__" => "Return repr(self).", + "_thread.lock.__setattr__" => "Implement setattr(self, name, value).", + "_thread.lock.__sizeof__" => "Size of object in memory, in bytes.", + "_thread.lock.__str__" => "Return str(self).", + "_thread.lock.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_thread.lock.acquire" => "Lock the lock. Without argument, this blocks if the lock is already\nlocked (even by the same thread), waiting for another thread to release\nthe lock, and return True once the lock is acquired.\nWith an argument, this will only block if the argument is true,\nand the return value reflects whether the lock is acquired.\nThe blocking operation is interruptible.", + "_thread.lock.acquire_lock" => "An obsolete synonym of acquire().", + "_thread.lock.locked" => "Return whether the lock is in the locked state.", + "_thread.lock.locked_lock" => "An obsolete synonym of locked().", + "_thread.lock.release" => "Release the lock, allowing another thread that is blocked waiting for\nthe lock to acquire the lock. The lock must be in the locked state,\nbut it needn't be locked by the same thread that unlocks it.", + "_thread.lock.release_lock" => "An obsolete synonym of release().", + "_thread.stack_size" => "Return the thread stack size used when creating new threads. The\noptional size argument specifies the stack size (in bytes) to be used\nfor subsequently created threads, and must be 0 (use platform or\nconfigured default) or a positive integer value of at least 32,768 (32k).\nIf changing the thread stack size is unsupported, a ThreadError\nexception is raised. If the specified size is invalid, a ValueError\nexception is raised, and the stack size is unmodified. 32k bytes\n currently the minimum supported stack size value to guarantee\nsufficient stack space for the interpreter itself.\n\nNote that some platforms may have particular restrictions on values for\nthe stack size, such as requiring a minimum stack size larger than 32 KiB or\nrequiring allocation in multiples of the system memory page size\n- platform documentation should be referred to for more information\n(4 KiB pages are common; using multiples of 4096 for the stack size is\nthe suggested approach in the absence of more specific information).", + "_thread.start_joinable_thread" => "*For internal use only*: start a new thread.\n\nLike start_new_thread(), this starts a new thread calling the given function.\nUnlike start_new_thread(), this returns a handle object with methods to join\nor detach the given thread.\nThis function is not for third-party code, please use the\n`threading` module instead. During finalization the runtime will not wait for\nthe thread to exit if daemon is True. If handle is provided it must be a\nnewly created thread._ThreadHandle instance.", + "_thread.start_new" => "An obsolete synonym of start_new_thread().", + "_thread.start_new_thread" => "Start a new thread and return its identifier.\n\nThe thread will call the function with positional arguments from the\ntuple args and keyword arguments taken from the optional dictionary\nkwargs. The thread exits when the function returns; the return value\nis ignored. The thread will also exit when the function raises an\nunhandled exception; a stack trace will be printed unless the exception\nis SystemExit.", + "_tkinter.TclError.__cause__" => "exception cause", + "_tkinter.TclError.__context__" => "exception context", + "_tkinter.TclError.__delattr__" => "Implement delattr(self, name).", + "_tkinter.TclError.__eq__" => "Return self==value.", + "_tkinter.TclError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_tkinter.TclError.__ge__" => "Return self>=value.", + "_tkinter.TclError.__getattribute__" => "Return getattr(self, name).", + "_tkinter.TclError.__getstate__" => "Helper for pickle.", + "_tkinter.TclError.__gt__" => "Return self>value.", + "_tkinter.TclError.__hash__" => "Return hash(self).", + "_tkinter.TclError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_tkinter.TclError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_tkinter.TclError.__le__" => "Return self<=value.", + "_tkinter.TclError.__lt__" => "Return self "Return self!=value.", + "_tkinter.TclError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_tkinter.TclError.__reduce_ex__" => "Helper for pickle.", + "_tkinter.TclError.__repr__" => "Return repr(self).", + "_tkinter.TclError.__setattr__" => "Implement setattr(self, name, value).", + "_tkinter.TclError.__sizeof__" => "Size of object in memory, in bytes.", + "_tkinter.TclError.__str__" => "Return str(self).", + "_tkinter.TclError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_tkinter.TclError.__weakref__" => "list of weak references to the object", + "_tkinter.TclError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "_tkinter.TclError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "_tkinter.Tcl_Obj.__delattr__" => "Implement delattr(self, name).", + "_tkinter.Tcl_Obj.__eq__" => "Return self==value.", + "_tkinter.Tcl_Obj.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_tkinter.Tcl_Obj.__ge__" => "Return self>=value.", + "_tkinter.Tcl_Obj.__getattribute__" => "Return getattr(self, name).", + "_tkinter.Tcl_Obj.__getstate__" => "Helper for pickle.", + "_tkinter.Tcl_Obj.__gt__" => "Return self>value.", + "_tkinter.Tcl_Obj.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_tkinter.Tcl_Obj.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_tkinter.Tcl_Obj.__le__" => "Return self<=value.", + "_tkinter.Tcl_Obj.__lt__" => "Return self "Return self!=value.", + "_tkinter.Tcl_Obj.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_tkinter.Tcl_Obj.__reduce__" => "Helper for pickle.", + "_tkinter.Tcl_Obj.__reduce_ex__" => "Helper for pickle.", + "_tkinter.Tcl_Obj.__repr__" => "Return repr(self).", + "_tkinter.Tcl_Obj.__setattr__" => "Implement setattr(self, name, value).", + "_tkinter.Tcl_Obj.__sizeof__" => "Size of object in memory, in bytes.", + "_tkinter.Tcl_Obj.__str__" => "Return str(self).", + "_tkinter.Tcl_Obj.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_tkinter.Tcl_Obj.string" => "the string representation of this object, either as str or bytes", + "_tkinter.Tcl_Obj.typename" => "name of the Tcl type", + "_tkinter.TkappType.__delattr__" => "Implement delattr(self, name).", + "_tkinter.TkappType.__eq__" => "Return self==value.", + "_tkinter.TkappType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_tkinter.TkappType.__ge__" => "Return self>=value.", + "_tkinter.TkappType.__getattribute__" => "Return getattr(self, name).", + "_tkinter.TkappType.__getstate__" => "Helper for pickle.", + "_tkinter.TkappType.__gt__" => "Return self>value.", + "_tkinter.TkappType.__hash__" => "Return hash(self).", + "_tkinter.TkappType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_tkinter.TkappType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_tkinter.TkappType.__le__" => "Return self<=value.", + "_tkinter.TkappType.__lt__" => "Return self "Return self!=value.", + "_tkinter.TkappType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_tkinter.TkappType.__reduce__" => "Helper for pickle.", + "_tkinter.TkappType.__reduce_ex__" => "Helper for pickle.", + "_tkinter.TkappType.__repr__" => "Return repr(self).", + "_tkinter.TkappType.__setattr__" => "Implement setattr(self, name, value).", + "_tkinter.TkappType.__sizeof__" => "Size of object in memory, in bytes.", + "_tkinter.TkappType.__str__" => "Return str(self).", + "_tkinter.TkappType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_tkinter.TkappType.gettrace" => "Get the tracing function.", + "_tkinter.TkappType.settrace" => "Set the tracing function.", + "_tkinter.TkttType.__delattr__" => "Implement delattr(self, name).", + "_tkinter.TkttType.__eq__" => "Return self==value.", + "_tkinter.TkttType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_tkinter.TkttType.__ge__" => "Return self>=value.", + "_tkinter.TkttType.__getattribute__" => "Return getattr(self, name).", + "_tkinter.TkttType.__getstate__" => "Helper for pickle.", + "_tkinter.TkttType.__gt__" => "Return self>value.", + "_tkinter.TkttType.__hash__" => "Return hash(self).", + "_tkinter.TkttType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_tkinter.TkttType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_tkinter.TkttType.__le__" => "Return self<=value.", + "_tkinter.TkttType.__lt__" => "Return self "Return self!=value.", + "_tkinter.TkttType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_tkinter.TkttType.__reduce__" => "Helper for pickle.", + "_tkinter.TkttType.__reduce_ex__" => "Helper for pickle.", + "_tkinter.TkttType.__repr__" => "Return repr(self).", + "_tkinter.TkttType.__setattr__" => "Implement setattr(self, name, value).", + "_tkinter.TkttType.__sizeof__" => "Size of object in memory, in bytes.", + "_tkinter.TkttType.__str__" => "Return str(self).", + "_tkinter.TkttType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_tkinter.create" => "wantTk\n if false, then Tk_Init() doesn't get called\nsync\n if true, then pass -sync to wish\nuse\n if not None, then pass -use to wish", + "_tkinter.getbusywaitinterval" => "Return the current busy-wait interval between successive calls to Tcl_DoOneEvent in a threaded Python interpreter.", + "_tkinter.setbusywaitinterval" => "Set the busy-wait interval in milliseconds between successive calls to Tcl_DoOneEvent in a threaded Python interpreter.\n\nIt should be set to a divisor of the maximum time between frames in an animation.", + "_tokenize.TokenizerIter.__delattr__" => "Implement delattr(self, name).", + "_tokenize.TokenizerIter.__eq__" => "Return self==value.", + "_tokenize.TokenizerIter.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_tokenize.TokenizerIter.__ge__" => "Return self>=value.", + "_tokenize.TokenizerIter.__getattribute__" => "Return getattr(self, name).", + "_tokenize.TokenizerIter.__getstate__" => "Helper for pickle.", + "_tokenize.TokenizerIter.__gt__" => "Return self>value.", + "_tokenize.TokenizerIter.__hash__" => "Return hash(self).", + "_tokenize.TokenizerIter.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_tokenize.TokenizerIter.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_tokenize.TokenizerIter.__iter__" => "Implement iter(self).", + "_tokenize.TokenizerIter.__le__" => "Return self<=value.", + "_tokenize.TokenizerIter.__lt__" => "Return self "Return self!=value.", + "_tokenize.TokenizerIter.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_tokenize.TokenizerIter.__next__" => "Implement next(self).", + "_tokenize.TokenizerIter.__reduce__" => "Helper for pickle.", + "_tokenize.TokenizerIter.__reduce_ex__" => "Helper for pickle.", + "_tokenize.TokenizerIter.__repr__" => "Return repr(self).", + "_tokenize.TokenizerIter.__setattr__" => "Implement setattr(self, name, value).", + "_tokenize.TokenizerIter.__sizeof__" => "Size of object in memory, in bytes.", + "_tokenize.TokenizerIter.__str__" => "Return str(self).", + "_tokenize.TokenizerIter.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_tracemalloc" => "Debug module to trace memory blocks allocated by Python.", + "_tracemalloc._get_object_traceback" => "Get the traceback where the Python object obj was allocated.\n\nReturn a tuple of (filename: str, lineno: int) tuples.\nReturn None if the tracemalloc module is disabled or did not\ntrace the allocation of the object.", + "_tracemalloc._get_traces" => "Get traces of all memory blocks allocated by Python.\n\nReturn a list of (size: int, traceback: tuple) tuples.\ntraceback is a tuple of (filename: str, lineno: int) tuples.\n\nReturn an empty list if the tracemalloc module is disabled.", + "_tracemalloc.clear_traces" => "Clear traces of memory blocks allocated by Python.", + "_tracemalloc.get_traceback_limit" => "Get the maximum number of frames stored in the traceback of a trace.\n\nBy default, a trace of an allocated memory block only stores\nthe most recent frame: the limit is 1.", + "_tracemalloc.get_traced_memory" => "Get the current size and peak size of memory blocks traced by tracemalloc.\n\nReturns a tuple: (current: int, peak: int).", + "_tracemalloc.get_tracemalloc_memory" => "Get the memory usage in bytes of the tracemalloc module.\n\nThis memory is used internally to trace memory allocations.", + "_tracemalloc.is_tracing" => "Return True if the tracemalloc module is tracing Python memory allocations.", + "_tracemalloc.reset_peak" => "Set the peak size of memory blocks traced by tracemalloc to the current size.\n\nDo nothing if the tracemalloc module is not tracing memory allocations.", + "_tracemalloc.start" => "Start tracing Python memory allocations.\n\nAlso set the maximum number of frames stored in the traceback of a\ntrace to nframe.", + "_tracemalloc.stop" => "Stop tracing Python memory allocations.\n\nAlso clear traces of memory blocks allocated by Python.", + "_typing" => "Primitives and accelerators for the typing module.", + "_warnings" => "_warnings provides basic warning filtering support.\nIt is a helper module to speed up interpreter start-up.", + "_warnings.warn" => "Issue a warning, or maybe ignore it or raise an exception.\n\nmessage\n Text of the warning message.\ncategory\n The Warning category subclass. Defaults to UserWarning.\nstacklevel\n How far up the call stack to make this warning appear. A value of 2 for\n example attributes the warning to the caller of the code calling warn().\nsource\n If supplied, the destroyed object which emitted a ResourceWarning\nskip_file_prefixes\n An optional tuple of module filename prefixes indicating frames to skip\n during stacklevel computations for stack frame attribution.", + "_warnings.warn_explicit" => "Issue a warning, or maybe ignore it or raise an exception.", + "_weakref" => "Weak-reference support module.", + "_weakref._remove_dead_weakref" => "Atomically remove key from dict if it points to a dead weakref.", + "_weakref.getweakrefcount" => "Return the number of weak references to 'object'.", + "_weakref.getweakrefs" => "Return a list of all weak reference objects pointing to 'object'.", + "_weakref.proxy" => "Create a proxy object that weakly references 'object'.\n\n'callback', if given, is called with a reference to the\nproxy when 'object' is about to be finalized.", + "_winapi.BatchedWaitForMultipleObjects" => "Supports a larger number of handles than WaitForMultipleObjects\n\nNote that the handles may be waited on other threads, which could cause\nissues for objects like mutexes that become associated with the thread\nthat was waiting for them. Objects may also be left signalled, even if\nthe wait fails.\n\nIt is recommended to use WaitForMultipleObjects whenever possible, and\nonly switch to BatchedWaitForMultipleObjects for scenarios where you\ncontrol all the handles involved, such as your own thread pool or\nfiles, and all wait objects are left unmodified by a wait (for example,\nmanual reset events, threads, and files/pipes).\n\nOverlapped handles returned from this module use manual reset events.", + "_winapi.CloseHandle" => "Close handle.", + "_winapi.CopyFile2" => "Copies a file from one name to a new name.\n\nThis is implemented using the CopyFile2 API, which preserves all stat\nand metadata information apart from security attributes.\n\nprogress_routine is reserved for future use, but is currently not\nimplemented. Its value is ignored.", + "_winapi.CreatePipe" => "Create an anonymous pipe.\n\n pipe_attrs\n Ignored internally, can be None.\n\nReturns a 2-tuple of handles, to the read and write ends of the pipe.", + "_winapi.CreateProcess" => "Create a new process and its primary thread.\n\n command_line\n Can be str or None\n proc_attrs\n Ignored internally, can be None.\n thread_attrs\n Ignored internally, can be None.\n\nThe return value is a tuple of the process handle, thread handle,\nprocess ID, and thread ID.", + "_winapi.DuplicateHandle" => "Return a duplicate handle object.\n\nThe duplicate handle refers to the same object as the original\nhandle. Therefore, any changes to the object are reflected\nthrough both handles.", + "_winapi.GetACP" => "Get the current Windows ANSI code page identifier.", + "_winapi.GetCurrentProcess" => "Return a handle object for the current process.", + "_winapi.GetExitCodeProcess" => "Return the termination status of the specified process.", + "_winapi.GetLongPathName" => "Return the long version of the provided path.\n\nIf the path is already in its long form, returns the same value.\n\nThe path must already be a 'str'. If the type is not known, use\nos.fsdecode before calling this function.", + "_winapi.GetModuleFileName" => "Return the fully-qualified path for the file that contains module.\n\nThe module must have been loaded by the current process.\n\nThe module parameter should be a handle to the loaded module\nwhose path is being requested. If this parameter is 0,\nGetModuleFileName retrieves the path of the executable file\nof the current process.", + "_winapi.GetShortPathName" => "Return the short version of the provided path.\n\nIf the path is already in its short form, returns the same value.\n\nThe path must already be a 'str'. If the type is not known, use\nos.fsdecode before calling this function.", + "_winapi.GetStdHandle" => "Return a handle to the specified standard device.\n\n std_handle\n One of STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, or STD_ERROR_HANDLE.\n\nThe integer associated with the handle object is returned.", + "_winapi.GetVersion" => "Return the version number of the current operating system.", + "_winapi.Overlapped" => "OVERLAPPED structure wrapper", + "_winapi.Overlapped.__delattr__" => "Implement delattr(self, name).", + "_winapi.Overlapped.__eq__" => "Return self==value.", + "_winapi.Overlapped.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "_winapi.Overlapped.__ge__" => "Return self>=value.", + "_winapi.Overlapped.__getattribute__" => "Return getattr(self, name).", + "_winapi.Overlapped.__getstate__" => "Helper for pickle.", + "_winapi.Overlapped.__gt__" => "Return self>value.", + "_winapi.Overlapped.__hash__" => "Return hash(self).", + "_winapi.Overlapped.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "_winapi.Overlapped.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "_winapi.Overlapped.__le__" => "Return self<=value.", + "_winapi.Overlapped.__lt__" => "Return self "Return self!=value.", + "_winapi.Overlapped.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "_winapi.Overlapped.__reduce__" => "Helper for pickle.", + "_winapi.Overlapped.__reduce_ex__" => "Helper for pickle.", + "_winapi.Overlapped.__repr__" => "Return repr(self).", + "_winapi.Overlapped.__setattr__" => "Implement setattr(self, name, value).", + "_winapi.Overlapped.__sizeof__" => "Size of object in memory, in bytes.", + "_winapi.Overlapped.__str__" => "Return str(self).", + "_winapi.Overlapped.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "_winapi.Overlapped.event" => "overlapped event handle", + "_winapi.TerminateProcess" => "Terminate the specified process and all of its threads.", + "_winapi.WaitForSingleObject" => "Wait for a single object.\n\nWait until the specified object is in the signaled state or\nthe time-out interval elapses. The timeout value is specified\nin milliseconds.", + "_winapi._mimetypes_read_windows_registry" => "Optimized function for reading all known MIME types from the registry.\n\n*on_type_read* is a callable taking *type* and *ext* arguments, as for\nMimeTypes.add_type.", + "_wmi.exec_query" => "Runs a WMI query against the local machine.\n\nThis returns a single string with 'name=value' pairs in a flat array separated\nby null characters.", + "_zoneinfo" => "C implementation of the zoneinfo module", + "array" => "This module defines an object type which can efficiently represent\nan array of basic values: characters, integers, floating-point\nnumbers. Arrays are sequence types and behave very much like lists,\nexcept that the type of objects stored in them is constrained.", + "array.ArrayType" => "array(typecode [, initializer]) -> array\n\nReturn a new array whose items are restricted by typecode, and\ninitialized from the optional initializer value, which must be a list,\nstring or iterable over elements of the appropriate type.\n\nArrays represent basic values and behave very much like lists, except\nthe type of objects stored in them is constrained. The type is specified\nat object creation time by using a type code, which is a single character.\nThe following type codes are defined:\n\n Type code C Type Minimum size in bytes\n 'b' signed integer 1\n 'B' unsigned integer 1\n 'u' Unicode character 2 (see note)\n 'h' signed integer 2\n 'H' unsigned integer 2\n 'i' signed integer 2\n 'I' unsigned integer 2\n 'l' signed integer 4\n 'L' unsigned integer 4\n 'q' signed integer 8 (see note)\n 'Q' unsigned integer 8 (see note)\n 'f' floating-point 4\n 'd' floating-point 8\n\nNOTE: The 'u' typecode corresponds to Python's unicode character. On\nnarrow builds this is 2-bytes on wide builds this is 4-bytes.\n\nNOTE: The 'q' and 'Q' type codes are only available if the platform\nC compiler used to build Python supports 'long long', or, on Windows,\n'__int64'.\n\nMethods:\n\nappend() -- append a new item to the end of the array\nbuffer_info() -- return information giving the current memory info\nbyteswap() -- byteswap all the items of the array\ncount() -- return number of occurrences of an object\nextend() -- extend array by appending multiple elements from an iterable\nfromfile() -- read items from a file object\nfromlist() -- append items from the list\nfrombytes() -- append items from the string\nindex() -- return index of first occurrence of an object\ninsert() -- insert a new item into the array at a provided position\npop() -- remove and return item (default last)\nremove() -- remove first occurrence of an object\nreverse() -- reverse the order of the items in the array\ntofile() -- write all items to a file object\ntolist() -- return the array converted to an ordinary list\ntobytes() -- return the array converted to a string\n\nAttributes:\n\ntypecode -- the typecode character used to create the array\nitemsize -- the length in bytes of one array item", + "array.ArrayType.__add__" => "Return self+value.", + "array.ArrayType.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "array.ArrayType.__class_getitem__" => "See PEP 585", + "array.ArrayType.__contains__" => "Return bool(key in self).", + "array.ArrayType.__copy__" => "Return a copy of the array.", + "array.ArrayType.__deepcopy__" => "Return a copy of the array.", + "array.ArrayType.__delattr__" => "Implement delattr(self, name).", + "array.ArrayType.__delitem__" => "Delete self[key].", + "array.ArrayType.__eq__" => "Return self==value.", + "array.ArrayType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "array.ArrayType.__ge__" => "Return self>=value.", + "array.ArrayType.__getattribute__" => "Return getattr(self, name).", + "array.ArrayType.__getitem__" => "Return self[key].", + "array.ArrayType.__getstate__" => "Helper for pickle.", + "array.ArrayType.__gt__" => "Return self>value.", + "array.ArrayType.__iadd__" => "Implement self+=value.", + "array.ArrayType.__imul__" => "Implement self*=value.", + "array.ArrayType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "array.ArrayType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "array.ArrayType.__iter__" => "Implement iter(self).", + "array.ArrayType.__le__" => "Return self<=value.", + "array.ArrayType.__len__" => "Return len(self).", + "array.ArrayType.__lt__" => "Return self "Return self*value.", + "array.ArrayType.__ne__" => "Return self!=value.", + "array.ArrayType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "array.ArrayType.__reduce__" => "Helper for pickle.", + "array.ArrayType.__reduce_ex__" => "Return state information for pickling.", + "array.ArrayType.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "array.ArrayType.__repr__" => "Return repr(self).", + "array.ArrayType.__rmul__" => "Return value*self.", + "array.ArrayType.__setattr__" => "Implement setattr(self, name, value).", + "array.ArrayType.__setitem__" => "Set self[key] to value.", + "array.ArrayType.__sizeof__" => "Size of the array in memory, in bytes.", + "array.ArrayType.__str__" => "Return str(self).", + "array.ArrayType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "array.ArrayType.append" => "Append new value v to the end of the array.", + "array.ArrayType.buffer_info" => "Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array's contents.\n\nThe length should be multiplied by the itemsize attribute to calculate\nthe buffer length in bytes.", + "array.ArrayType.byteswap" => "Byteswap all items of the array.\n\nIf the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is\nraised.", + "array.ArrayType.clear" => "Remove all items from the array.", + "array.ArrayType.count" => "Return number of occurrences of v in the array.", + "array.ArrayType.extend" => "Append items to the end of the array.", + "array.ArrayType.frombytes" => "Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method.", + "array.ArrayType.fromfile" => "Read n objects from the file object f and append them to the end of the array.", + "array.ArrayType.fromlist" => "Append items to array from list.", + "array.ArrayType.fromunicode" => "Extends this array with data from the unicode string ustr.\n\nThe array must be a unicode type array; otherwise a ValueError is raised.\nUse array.frombytes(ustr.encode(...)) to append Unicode data to an array of\nsome other type.", + "array.ArrayType.index" => "Return index of first occurrence of v in the array.\n\nRaise ValueError if the value is not present.", + "array.ArrayType.insert" => "Insert a new item v into the array before position i.", + "array.ArrayType.itemsize" => "the size, in bytes, of one array item", + "array.ArrayType.pop" => "Return the i-th element and delete it from the array.\n\ni defaults to -1.", + "array.ArrayType.remove" => "Remove the first occurrence of v in the array.", + "array.ArrayType.reverse" => "Reverse the order of the items in the array.", + "array.ArrayType.tobytes" => "Convert the array to an array of machine values and return the bytes representation.", + "array.ArrayType.tofile" => "Write all items (as machine values) to the file object f.", + "array.ArrayType.tolist" => "Convert array to an ordinary list with the same items.", + "array.ArrayType.tounicode" => "Extends this array with data from the unicode string ustr.\n\nConvert the array to a unicode string. The array must be a unicode type array;\notherwise a ValueError is raised. Use array.tobytes().decode() to obtain a\nunicode string from an array of some other type.", + "array.ArrayType.typecode" => "the typecode character used to create the array", + "array._array_reconstructor" => "Internal. Used for pickling support.", + "array.array" => "array(typecode [, initializer]) -> array\n\nReturn a new array whose items are restricted by typecode, and\ninitialized from the optional initializer value, which must be a list,\nstring or iterable over elements of the appropriate type.\n\nArrays represent basic values and behave very much like lists, except\nthe type of objects stored in them is constrained. The type is specified\nat object creation time by using a type code, which is a single character.\nThe following type codes are defined:\n\n Type code C Type Minimum size in bytes\n 'b' signed integer 1\n 'B' unsigned integer 1\n 'u' Unicode character 2 (see note)\n 'h' signed integer 2\n 'H' unsigned integer 2\n 'i' signed integer 2\n 'I' unsigned integer 2\n 'l' signed integer 4\n 'L' unsigned integer 4\n 'q' signed integer 8 (see note)\n 'Q' unsigned integer 8 (see note)\n 'f' floating-point 4\n 'd' floating-point 8\n\nNOTE: The 'u' typecode corresponds to Python's unicode character. On\nnarrow builds this is 2-bytes on wide builds this is 4-bytes.\n\nNOTE: The 'q' and 'Q' type codes are only available if the platform\nC compiler used to build Python supports 'long long', or, on Windows,\n'__int64'.\n\nMethods:\n\nappend() -- append a new item to the end of the array\nbuffer_info() -- return information giving the current memory info\nbyteswap() -- byteswap all the items of the array\ncount() -- return number of occurrences of an object\nextend() -- extend array by appending multiple elements from an iterable\nfromfile() -- read items from a file object\nfromlist() -- append items from the list\nfrombytes() -- append items from the string\nindex() -- return index of first occurrence of an object\ninsert() -- insert a new item into the array at a provided position\npop() -- remove and return item (default last)\nremove() -- remove first occurrence of an object\nreverse() -- reverse the order of the items in the array\ntofile() -- write all items to a file object\ntolist() -- return the array converted to an ordinary list\ntobytes() -- return the array converted to a string\n\nAttributes:\n\ntypecode -- the typecode character used to create the array\nitemsize -- the length in bytes of one array item", + "array.array.__add__" => "Return self+value.", + "array.array.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "array.array.__class_getitem__" => "See PEP 585", + "array.array.__contains__" => "Return bool(key in self).", + "array.array.__copy__" => "Return a copy of the array.", + "array.array.__deepcopy__" => "Return a copy of the array.", + "array.array.__delattr__" => "Implement delattr(self, name).", + "array.array.__delitem__" => "Delete self[key].", + "array.array.__eq__" => "Return self==value.", + "array.array.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "array.array.__ge__" => "Return self>=value.", + "array.array.__getattribute__" => "Return getattr(self, name).", + "array.array.__getitem__" => "Return self[key].", + "array.array.__getstate__" => "Helper for pickle.", + "array.array.__gt__" => "Return self>value.", + "array.array.__iadd__" => "Implement self+=value.", + "array.array.__imul__" => "Implement self*=value.", + "array.array.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "array.array.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "array.array.__iter__" => "Implement iter(self).", + "array.array.__le__" => "Return self<=value.", + "array.array.__len__" => "Return len(self).", + "array.array.__lt__" => "Return self "Return self*value.", + "array.array.__ne__" => "Return self!=value.", + "array.array.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "array.array.__reduce__" => "Helper for pickle.", + "array.array.__reduce_ex__" => "Return state information for pickling.", + "array.array.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "array.array.__repr__" => "Return repr(self).", + "array.array.__rmul__" => "Return value*self.", + "array.array.__setattr__" => "Implement setattr(self, name, value).", + "array.array.__setitem__" => "Set self[key] to value.", + "array.array.__sizeof__" => "Size of the array in memory, in bytes.", + "array.array.__str__" => "Return str(self).", + "array.array.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "array.array.append" => "Append new value v to the end of the array.", + "array.array.buffer_info" => "Return a tuple (address, length) giving the current memory address and the length in items of the buffer used to hold array's contents.\n\nThe length should be multiplied by the itemsize attribute to calculate\nthe buffer length in bytes.", + "array.array.byteswap" => "Byteswap all items of the array.\n\nIf the items in the array are not 1, 2, 4, or 8 bytes in size, RuntimeError is\nraised.", + "array.array.clear" => "Remove all items from the array.", + "array.array.count" => "Return number of occurrences of v in the array.", + "array.array.extend" => "Append items to the end of the array.", + "array.array.frombytes" => "Appends items from the string, interpreting it as an array of machine values, as if it had been read from a file using the fromfile() method.", + "array.array.fromfile" => "Read n objects from the file object f and append them to the end of the array.", + "array.array.fromlist" => "Append items to array from list.", + "array.array.fromunicode" => "Extends this array with data from the unicode string ustr.\n\nThe array must be a unicode type array; otherwise a ValueError is raised.\nUse array.frombytes(ustr.encode(...)) to append Unicode data to an array of\nsome other type.", + "array.array.index" => "Return index of first occurrence of v in the array.\n\nRaise ValueError if the value is not present.", + "array.array.insert" => "Insert a new item v into the array before position i.", + "array.array.itemsize" => "the size, in bytes, of one array item", + "array.array.pop" => "Return the i-th element and delete it from the array.\n\ni defaults to -1.", + "array.array.remove" => "Remove the first occurrence of v in the array.", + "array.array.reverse" => "Reverse the order of the items in the array.", + "array.array.tobytes" => "Convert the array to an array of machine values and return the bytes representation.", + "array.array.tofile" => "Write all items (as machine values) to the file object f.", + "array.array.tolist" => "Convert array to an ordinary list with the same items.", + "array.array.tounicode" => "Extends this array with data from the unicode string ustr.\n\nConvert the array to a unicode string. The array must be a unicode type array;\notherwise a ValueError is raised. Use array.tobytes().decode() to obtain a\nunicode string from an array of some other type.", + "array.array.typecode" => "the typecode character used to create the array", + "atexit" => "allow programmer to define multiple exit functions to be executed\nupon normal program termination.\n\nTwo public functions, register and unregister, are defined.", + "atexit._clear" => "Clear the list of previously registered exit functions.", + "atexit._ncallbacks" => "Return the number of registered exit functions.", + "atexit._run_exitfuncs" => "Run all registered exit functions.\n\nIf a callback raises an exception, it is logged with sys.unraisablehook.", + "atexit.register" => "Register a function to be executed upon normal program termination\n\nfunc - function to be called at exit\nargs - optional arguments to pass to func\nkwargs - optional keyword arguments to pass to func\n\nfunc is returned to facilitate usage as a decorator.", + "atexit.unregister" => "Unregister an exit function which was previously registered using\natexit.register\n\n func - function to be unregistered", + "binascii" => "Conversion between binary data and ASCII", + "binascii.Error.__cause__" => "exception cause", + "binascii.Error.__context__" => "exception context", + "binascii.Error.__delattr__" => "Implement delattr(self, name).", + "binascii.Error.__eq__" => "Return self==value.", + "binascii.Error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "binascii.Error.__ge__" => "Return self>=value.", + "binascii.Error.__getattribute__" => "Return getattr(self, name).", + "binascii.Error.__getstate__" => "Helper for pickle.", + "binascii.Error.__gt__" => "Return self>value.", + "binascii.Error.__hash__" => "Return hash(self).", + "binascii.Error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "binascii.Error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "binascii.Error.__le__" => "Return self<=value.", + "binascii.Error.__lt__" => "Return self "Return self!=value.", + "binascii.Error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "binascii.Error.__reduce_ex__" => "Helper for pickle.", + "binascii.Error.__repr__" => "Return repr(self).", + "binascii.Error.__setattr__" => "Implement setattr(self, name, value).", + "binascii.Error.__sizeof__" => "Size of object in memory, in bytes.", + "binascii.Error.__str__" => "Return str(self).", + "binascii.Error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "binascii.Error.__weakref__" => "list of weak references to the object", + "binascii.Error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "binascii.Error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "binascii.Incomplete.__cause__" => "exception cause", + "binascii.Incomplete.__context__" => "exception context", + "binascii.Incomplete.__delattr__" => "Implement delattr(self, name).", + "binascii.Incomplete.__eq__" => "Return self==value.", + "binascii.Incomplete.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "binascii.Incomplete.__ge__" => "Return self>=value.", + "binascii.Incomplete.__getattribute__" => "Return getattr(self, name).", + "binascii.Incomplete.__getstate__" => "Helper for pickle.", + "binascii.Incomplete.__gt__" => "Return self>value.", + "binascii.Incomplete.__hash__" => "Return hash(self).", + "binascii.Incomplete.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "binascii.Incomplete.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "binascii.Incomplete.__le__" => "Return self<=value.", + "binascii.Incomplete.__lt__" => "Return self "Return self!=value.", + "binascii.Incomplete.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "binascii.Incomplete.__reduce_ex__" => "Helper for pickle.", + "binascii.Incomplete.__repr__" => "Return repr(self).", + "binascii.Incomplete.__setattr__" => "Implement setattr(self, name, value).", + "binascii.Incomplete.__sizeof__" => "Size of object in memory, in bytes.", + "binascii.Incomplete.__str__" => "Return str(self).", + "binascii.Incomplete.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "binascii.Incomplete.__weakref__" => "list of weak references to the object", + "binascii.Incomplete.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "binascii.Incomplete.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "binascii.a2b_base64" => "Decode a line of base64 data.\n\nstrict_mode\n When set to True, bytes that are not part of the base64 standard are not allowed.\n The same applies to excess data after padding (= / ==).", + "binascii.a2b_hex" => "Binary data of hexadecimal representation.\n\nhexstr must contain an even number of hex digits (upper or lower case).\nThis function is also available as \"unhexlify()\".", + "binascii.a2b_qp" => "Decode a string of qp-encoded data.", + "binascii.a2b_uu" => "Decode a line of uuencoded data.", + "binascii.b2a_base64" => "Base64-code line of data.", + "binascii.b2a_hex" => "Hexadecimal representation of binary data.\n\n sep\n An optional single character or byte to separate hex bytes.\n bytes_per_sep\n How many bytes between separators. Positive values count from the\n right, negative values count from the left.\n\nThe return value is a bytes object. This function is also\navailable as \"hexlify()\".\n\nExample:\n>>> binascii.b2a_hex(b'\\xb9\\x01\\xef')\nb'b901ef'\n>>> binascii.hexlify(b'\\xb9\\x01\\xef', ':')\nb'b9:01:ef'\n>>> binascii.b2a_hex(b'\\xb9\\x01\\xef', b'_', 2)\nb'b9_01ef'", + "binascii.b2a_qp" => "Encode a string using quoted-printable encoding.\n\nOn encoding, when istext is set, newlines are not encoded, and white\nspace at end of lines is. When istext is not set, \\r and \\n (CR/LF)\nare both encoded. When quotetabs is set, space and tabs are encoded.", + "binascii.b2a_uu" => "Uuencode line of data.", + "binascii.crc32" => "Compute CRC-32 incrementally.", + "binascii.crc_hqx" => "Compute CRC-CCITT incrementally.", + "binascii.hexlify" => "Hexadecimal representation of binary data.\n\n sep\n An optional single character or byte to separate hex bytes.\n bytes_per_sep\n How many bytes between separators. Positive values count from the\n right, negative values count from the left.\n\nThe return value is a bytes object. This function is also\navailable as \"b2a_hex()\".", + "binascii.unhexlify" => "Binary data of hexadecimal representation.\n\nhexstr must contain an even number of hex digits (upper or lower case).", + "builtins" => "Built-in functions, types, exceptions, and other objects.\n\nThis module provides direct access to all 'built-in'\nidentifiers of Python; for example, builtins.len is\nthe full name for the built-in function len().\n\nThis module is not normally accessed explicitly by most\napplications, but can be useful in modules that provide\nobjects with the same name as a built-in value, but in\nwhich the built-in of that name is also needed.", + "builtins.ArithmeticError" => "Base class for arithmetic errors.", + "builtins.ArithmeticError.__cause__" => "exception cause", + "builtins.ArithmeticError.__context__" => "exception context", + "builtins.ArithmeticError.__delattr__" => "Implement delattr(self, name).", + "builtins.ArithmeticError.__eq__" => "Return self==value.", + "builtins.ArithmeticError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ArithmeticError.__ge__" => "Return self>=value.", + "builtins.ArithmeticError.__getattribute__" => "Return getattr(self, name).", + "builtins.ArithmeticError.__getstate__" => "Helper for pickle.", + "builtins.ArithmeticError.__gt__" => "Return self>value.", + "builtins.ArithmeticError.__hash__" => "Return hash(self).", + "builtins.ArithmeticError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ArithmeticError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ArithmeticError.__le__" => "Return self<=value.", + "builtins.ArithmeticError.__lt__" => "Return self "Return self!=value.", + "builtins.ArithmeticError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ArithmeticError.__reduce_ex__" => "Helper for pickle.", + "builtins.ArithmeticError.__repr__" => "Return repr(self).", + "builtins.ArithmeticError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ArithmeticError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ArithmeticError.__str__" => "Return str(self).", + "builtins.ArithmeticError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ArithmeticError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ArithmeticError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.AssertionError" => "Assertion failed.", + "builtins.AssertionError.__cause__" => "exception cause", + "builtins.AssertionError.__context__" => "exception context", + "builtins.AssertionError.__delattr__" => "Implement delattr(self, name).", + "builtins.AssertionError.__eq__" => "Return self==value.", + "builtins.AssertionError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.AssertionError.__ge__" => "Return self>=value.", + "builtins.AssertionError.__getattribute__" => "Return getattr(self, name).", + "builtins.AssertionError.__getstate__" => "Helper for pickle.", + "builtins.AssertionError.__gt__" => "Return self>value.", + "builtins.AssertionError.__hash__" => "Return hash(self).", + "builtins.AssertionError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.AssertionError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.AssertionError.__le__" => "Return self<=value.", + "builtins.AssertionError.__lt__" => "Return self "Return self!=value.", + "builtins.AssertionError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.AssertionError.__reduce_ex__" => "Helper for pickle.", + "builtins.AssertionError.__repr__" => "Return repr(self).", + "builtins.AssertionError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.AssertionError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.AssertionError.__str__" => "Return str(self).", + "builtins.AssertionError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.AssertionError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.AssertionError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.AttributeError" => "Attribute not found.", + "builtins.AttributeError.__cause__" => "exception cause", + "builtins.AttributeError.__context__" => "exception context", + "builtins.AttributeError.__delattr__" => "Implement delattr(self, name).", + "builtins.AttributeError.__eq__" => "Return self==value.", + "builtins.AttributeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.AttributeError.__ge__" => "Return self>=value.", + "builtins.AttributeError.__getattribute__" => "Return getattr(self, name).", + "builtins.AttributeError.__gt__" => "Return self>value.", + "builtins.AttributeError.__hash__" => "Return hash(self).", + "builtins.AttributeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.AttributeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.AttributeError.__le__" => "Return self<=value.", + "builtins.AttributeError.__lt__" => "Return self "Return self!=value.", + "builtins.AttributeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.AttributeError.__reduce_ex__" => "Helper for pickle.", + "builtins.AttributeError.__repr__" => "Return repr(self).", + "builtins.AttributeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.AttributeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.AttributeError.__str__" => "Return str(self).", + "builtins.AttributeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.AttributeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.AttributeError.name" => "attribute name", + "builtins.AttributeError.obj" => "object", + "builtins.AttributeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BaseException" => "Common base class for all exceptions", + "builtins.BaseException.__cause__" => "exception cause", + "builtins.BaseException.__context__" => "exception context", + "builtins.BaseException.__delattr__" => "Implement delattr(self, name).", + "builtins.BaseException.__eq__" => "Return self==value.", + "builtins.BaseException.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BaseException.__ge__" => "Return self>=value.", + "builtins.BaseException.__getattribute__" => "Return getattr(self, name).", + "builtins.BaseException.__getstate__" => "Helper for pickle.", + "builtins.BaseException.__gt__" => "Return self>value.", + "builtins.BaseException.__hash__" => "Return hash(self).", + "builtins.BaseException.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BaseException.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BaseException.__le__" => "Return self<=value.", + "builtins.BaseException.__lt__" => "Return self "Return self!=value.", + "builtins.BaseException.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BaseException.__reduce_ex__" => "Helper for pickle.", + "builtins.BaseException.__repr__" => "Return repr(self).", + "builtins.BaseException.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BaseException.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BaseException.__str__" => "Return str(self).", + "builtins.BaseException.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BaseException.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BaseException.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BaseExceptionGroup" => "A combination of multiple unrelated exceptions.", + "builtins.BaseExceptionGroup.__cause__" => "exception cause", + "builtins.BaseExceptionGroup.__class_getitem__" => "See PEP 585", + "builtins.BaseExceptionGroup.__context__" => "exception context", + "builtins.BaseExceptionGroup.__delattr__" => "Implement delattr(self, name).", + "builtins.BaseExceptionGroup.__eq__" => "Return self==value.", + "builtins.BaseExceptionGroup.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BaseExceptionGroup.__ge__" => "Return self>=value.", + "builtins.BaseExceptionGroup.__getattribute__" => "Return getattr(self, name).", + "builtins.BaseExceptionGroup.__getstate__" => "Helper for pickle.", + "builtins.BaseExceptionGroup.__gt__" => "Return self>value.", + "builtins.BaseExceptionGroup.__hash__" => "Return hash(self).", + "builtins.BaseExceptionGroup.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BaseExceptionGroup.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BaseExceptionGroup.__le__" => "Return self<=value.", + "builtins.BaseExceptionGroup.__lt__" => "Return self "Return self!=value.", + "builtins.BaseExceptionGroup.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BaseExceptionGroup.__reduce_ex__" => "Helper for pickle.", + "builtins.BaseExceptionGroup.__repr__" => "Return repr(self).", + "builtins.BaseExceptionGroup.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BaseExceptionGroup.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BaseExceptionGroup.__str__" => "Return str(self).", + "builtins.BaseExceptionGroup.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BaseExceptionGroup.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BaseExceptionGroup.exceptions" => "nested exceptions", + "builtins.BaseExceptionGroup.message" => "exception message", + "builtins.BaseExceptionGroup.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BlockingIOError" => "I/O operation would block.", + "builtins.BlockingIOError.__cause__" => "exception cause", + "builtins.BlockingIOError.__context__" => "exception context", + "builtins.BlockingIOError.__delattr__" => "Implement delattr(self, name).", + "builtins.BlockingIOError.__eq__" => "Return self==value.", + "builtins.BlockingIOError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BlockingIOError.__ge__" => "Return self>=value.", + "builtins.BlockingIOError.__getattribute__" => "Return getattr(self, name).", + "builtins.BlockingIOError.__getstate__" => "Helper for pickle.", + "builtins.BlockingIOError.__gt__" => "Return self>value.", + "builtins.BlockingIOError.__hash__" => "Return hash(self).", + "builtins.BlockingIOError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BlockingIOError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BlockingIOError.__le__" => "Return self<=value.", + "builtins.BlockingIOError.__lt__" => "Return self "Return self!=value.", + "builtins.BlockingIOError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BlockingIOError.__reduce_ex__" => "Helper for pickle.", + "builtins.BlockingIOError.__repr__" => "Return repr(self).", + "builtins.BlockingIOError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BlockingIOError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BlockingIOError.__str__" => "Return str(self).", + "builtins.BlockingIOError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BlockingIOError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BlockingIOError.errno" => "POSIX exception code", + "builtins.BlockingIOError.filename" => "exception filename", + "builtins.BlockingIOError.filename2" => "second exception filename", + "builtins.BlockingIOError.strerror" => "exception strerror", + "builtins.BlockingIOError.winerror" => "Win32 exception code", + "builtins.BlockingIOError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BrokenPipeError" => "Broken pipe.", + "builtins.BrokenPipeError.__cause__" => "exception cause", + "builtins.BrokenPipeError.__context__" => "exception context", + "builtins.BrokenPipeError.__delattr__" => "Implement delattr(self, name).", + "builtins.BrokenPipeError.__eq__" => "Return self==value.", + "builtins.BrokenPipeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BrokenPipeError.__ge__" => "Return self>=value.", + "builtins.BrokenPipeError.__getattribute__" => "Return getattr(self, name).", + "builtins.BrokenPipeError.__getstate__" => "Helper for pickle.", + "builtins.BrokenPipeError.__gt__" => "Return self>value.", + "builtins.BrokenPipeError.__hash__" => "Return hash(self).", + "builtins.BrokenPipeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BrokenPipeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BrokenPipeError.__le__" => "Return self<=value.", + "builtins.BrokenPipeError.__lt__" => "Return self "Return self!=value.", + "builtins.BrokenPipeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BrokenPipeError.__reduce_ex__" => "Helper for pickle.", + "builtins.BrokenPipeError.__repr__" => "Return repr(self).", + "builtins.BrokenPipeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BrokenPipeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BrokenPipeError.__str__" => "Return str(self).", + "builtins.BrokenPipeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BrokenPipeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BrokenPipeError.errno" => "POSIX exception code", + "builtins.BrokenPipeError.filename" => "exception filename", + "builtins.BrokenPipeError.filename2" => "second exception filename", + "builtins.BrokenPipeError.strerror" => "exception strerror", + "builtins.BrokenPipeError.winerror" => "Win32 exception code", + "builtins.BrokenPipeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BufferError" => "Buffer error.", + "builtins.BufferError.__cause__" => "exception cause", + "builtins.BufferError.__context__" => "exception context", + "builtins.BufferError.__delattr__" => "Implement delattr(self, name).", + "builtins.BufferError.__eq__" => "Return self==value.", + "builtins.BufferError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BufferError.__ge__" => "Return self>=value.", + "builtins.BufferError.__getattribute__" => "Return getattr(self, name).", + "builtins.BufferError.__getstate__" => "Helper for pickle.", + "builtins.BufferError.__gt__" => "Return self>value.", + "builtins.BufferError.__hash__" => "Return hash(self).", + "builtins.BufferError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BufferError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BufferError.__le__" => "Return self<=value.", + "builtins.BufferError.__lt__" => "Return self "Return self!=value.", + "builtins.BufferError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BufferError.__reduce_ex__" => "Helper for pickle.", + "builtins.BufferError.__repr__" => "Return repr(self).", + "builtins.BufferError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BufferError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BufferError.__str__" => "Return str(self).", + "builtins.BufferError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BufferError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BufferError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.BytesWarning" => "Base class for warnings about bytes and buffer related problems, mostly\nrelated to conversion from str or comparing to str.", + "builtins.BytesWarning.__cause__" => "exception cause", + "builtins.BytesWarning.__context__" => "exception context", + "builtins.BytesWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.BytesWarning.__eq__" => "Return self==value.", + "builtins.BytesWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.BytesWarning.__ge__" => "Return self>=value.", + "builtins.BytesWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.BytesWarning.__getstate__" => "Helper for pickle.", + "builtins.BytesWarning.__gt__" => "Return self>value.", + "builtins.BytesWarning.__hash__" => "Return hash(self).", + "builtins.BytesWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.BytesWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.BytesWarning.__le__" => "Return self<=value.", + "builtins.BytesWarning.__lt__" => "Return self "Return self!=value.", + "builtins.BytesWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.BytesWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.BytesWarning.__repr__" => "Return repr(self).", + "builtins.BytesWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.BytesWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.BytesWarning.__str__" => "Return str(self).", + "builtins.BytesWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.BytesWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.BytesWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ChildProcessError" => "Child process error.", + "builtins.ChildProcessError.__cause__" => "exception cause", + "builtins.ChildProcessError.__context__" => "exception context", + "builtins.ChildProcessError.__delattr__" => "Implement delattr(self, name).", + "builtins.ChildProcessError.__eq__" => "Return self==value.", + "builtins.ChildProcessError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ChildProcessError.__ge__" => "Return self>=value.", + "builtins.ChildProcessError.__getattribute__" => "Return getattr(self, name).", + "builtins.ChildProcessError.__getstate__" => "Helper for pickle.", + "builtins.ChildProcessError.__gt__" => "Return self>value.", + "builtins.ChildProcessError.__hash__" => "Return hash(self).", + "builtins.ChildProcessError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ChildProcessError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ChildProcessError.__le__" => "Return self<=value.", + "builtins.ChildProcessError.__lt__" => "Return self "Return self!=value.", + "builtins.ChildProcessError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ChildProcessError.__reduce_ex__" => "Helper for pickle.", + "builtins.ChildProcessError.__repr__" => "Return repr(self).", + "builtins.ChildProcessError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ChildProcessError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ChildProcessError.__str__" => "Return str(self).", + "builtins.ChildProcessError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ChildProcessError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ChildProcessError.errno" => "POSIX exception code", + "builtins.ChildProcessError.filename" => "exception filename", + "builtins.ChildProcessError.filename2" => "second exception filename", + "builtins.ChildProcessError.strerror" => "exception strerror", + "builtins.ChildProcessError.winerror" => "Win32 exception code", + "builtins.ChildProcessError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ConnectionAbortedError" => "Connection aborted.", + "builtins.ConnectionAbortedError.__cause__" => "exception cause", + "builtins.ConnectionAbortedError.__context__" => "exception context", + "builtins.ConnectionAbortedError.__delattr__" => "Implement delattr(self, name).", + "builtins.ConnectionAbortedError.__eq__" => "Return self==value.", + "builtins.ConnectionAbortedError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ConnectionAbortedError.__ge__" => "Return self>=value.", + "builtins.ConnectionAbortedError.__getattribute__" => "Return getattr(self, name).", + "builtins.ConnectionAbortedError.__getstate__" => "Helper for pickle.", + "builtins.ConnectionAbortedError.__gt__" => "Return self>value.", + "builtins.ConnectionAbortedError.__hash__" => "Return hash(self).", + "builtins.ConnectionAbortedError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ConnectionAbortedError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ConnectionAbortedError.__le__" => "Return self<=value.", + "builtins.ConnectionAbortedError.__lt__" => "Return self "Return self!=value.", + "builtins.ConnectionAbortedError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ConnectionAbortedError.__reduce_ex__" => "Helper for pickle.", + "builtins.ConnectionAbortedError.__repr__" => "Return repr(self).", + "builtins.ConnectionAbortedError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ConnectionAbortedError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ConnectionAbortedError.__str__" => "Return str(self).", + "builtins.ConnectionAbortedError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ConnectionAbortedError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ConnectionAbortedError.errno" => "POSIX exception code", + "builtins.ConnectionAbortedError.filename" => "exception filename", + "builtins.ConnectionAbortedError.filename2" => "second exception filename", + "builtins.ConnectionAbortedError.strerror" => "exception strerror", + "builtins.ConnectionAbortedError.winerror" => "Win32 exception code", + "builtins.ConnectionAbortedError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ConnectionError" => "Connection error.", + "builtins.ConnectionError.__cause__" => "exception cause", + "builtins.ConnectionError.__context__" => "exception context", + "builtins.ConnectionError.__delattr__" => "Implement delattr(self, name).", + "builtins.ConnectionError.__eq__" => "Return self==value.", + "builtins.ConnectionError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ConnectionError.__ge__" => "Return self>=value.", + "builtins.ConnectionError.__getattribute__" => "Return getattr(self, name).", + "builtins.ConnectionError.__getstate__" => "Helper for pickle.", + "builtins.ConnectionError.__gt__" => "Return self>value.", + "builtins.ConnectionError.__hash__" => "Return hash(self).", + "builtins.ConnectionError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ConnectionError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ConnectionError.__le__" => "Return self<=value.", + "builtins.ConnectionError.__lt__" => "Return self "Return self!=value.", + "builtins.ConnectionError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ConnectionError.__reduce_ex__" => "Helper for pickle.", + "builtins.ConnectionError.__repr__" => "Return repr(self).", + "builtins.ConnectionError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ConnectionError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ConnectionError.__str__" => "Return str(self).", + "builtins.ConnectionError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ConnectionError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ConnectionError.errno" => "POSIX exception code", + "builtins.ConnectionError.filename" => "exception filename", + "builtins.ConnectionError.filename2" => "second exception filename", + "builtins.ConnectionError.strerror" => "exception strerror", + "builtins.ConnectionError.winerror" => "Win32 exception code", + "builtins.ConnectionError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ConnectionRefusedError" => "Connection refused.", + "builtins.ConnectionRefusedError.__cause__" => "exception cause", + "builtins.ConnectionRefusedError.__context__" => "exception context", + "builtins.ConnectionRefusedError.__delattr__" => "Implement delattr(self, name).", + "builtins.ConnectionRefusedError.__eq__" => "Return self==value.", + "builtins.ConnectionRefusedError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ConnectionRefusedError.__ge__" => "Return self>=value.", + "builtins.ConnectionRefusedError.__getattribute__" => "Return getattr(self, name).", + "builtins.ConnectionRefusedError.__getstate__" => "Helper for pickle.", + "builtins.ConnectionRefusedError.__gt__" => "Return self>value.", + "builtins.ConnectionRefusedError.__hash__" => "Return hash(self).", + "builtins.ConnectionRefusedError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ConnectionRefusedError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ConnectionRefusedError.__le__" => "Return self<=value.", + "builtins.ConnectionRefusedError.__lt__" => "Return self "Return self!=value.", + "builtins.ConnectionRefusedError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ConnectionRefusedError.__reduce_ex__" => "Helper for pickle.", + "builtins.ConnectionRefusedError.__repr__" => "Return repr(self).", + "builtins.ConnectionRefusedError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ConnectionRefusedError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ConnectionRefusedError.__str__" => "Return str(self).", + "builtins.ConnectionRefusedError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ConnectionRefusedError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ConnectionRefusedError.errno" => "POSIX exception code", + "builtins.ConnectionRefusedError.filename" => "exception filename", + "builtins.ConnectionRefusedError.filename2" => "second exception filename", + "builtins.ConnectionRefusedError.strerror" => "exception strerror", + "builtins.ConnectionRefusedError.winerror" => "Win32 exception code", + "builtins.ConnectionRefusedError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ConnectionResetError" => "Connection reset.", + "builtins.ConnectionResetError.__cause__" => "exception cause", + "builtins.ConnectionResetError.__context__" => "exception context", + "builtins.ConnectionResetError.__delattr__" => "Implement delattr(self, name).", + "builtins.ConnectionResetError.__eq__" => "Return self==value.", + "builtins.ConnectionResetError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ConnectionResetError.__ge__" => "Return self>=value.", + "builtins.ConnectionResetError.__getattribute__" => "Return getattr(self, name).", + "builtins.ConnectionResetError.__getstate__" => "Helper for pickle.", + "builtins.ConnectionResetError.__gt__" => "Return self>value.", + "builtins.ConnectionResetError.__hash__" => "Return hash(self).", + "builtins.ConnectionResetError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ConnectionResetError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ConnectionResetError.__le__" => "Return self<=value.", + "builtins.ConnectionResetError.__lt__" => "Return self "Return self!=value.", + "builtins.ConnectionResetError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ConnectionResetError.__reduce_ex__" => "Helper for pickle.", + "builtins.ConnectionResetError.__repr__" => "Return repr(self).", + "builtins.ConnectionResetError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ConnectionResetError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ConnectionResetError.__str__" => "Return str(self).", + "builtins.ConnectionResetError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ConnectionResetError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ConnectionResetError.errno" => "POSIX exception code", + "builtins.ConnectionResetError.filename" => "exception filename", + "builtins.ConnectionResetError.filename2" => "second exception filename", + "builtins.ConnectionResetError.strerror" => "exception strerror", + "builtins.ConnectionResetError.winerror" => "Win32 exception code", + "builtins.ConnectionResetError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.DeprecationWarning" => "Base class for warnings about deprecated features.", + "builtins.DeprecationWarning.__cause__" => "exception cause", + "builtins.DeprecationWarning.__context__" => "exception context", + "builtins.DeprecationWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.DeprecationWarning.__eq__" => "Return self==value.", + "builtins.DeprecationWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.DeprecationWarning.__ge__" => "Return self>=value.", + "builtins.DeprecationWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.DeprecationWarning.__getstate__" => "Helper for pickle.", + "builtins.DeprecationWarning.__gt__" => "Return self>value.", + "builtins.DeprecationWarning.__hash__" => "Return hash(self).", + "builtins.DeprecationWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.DeprecationWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.DeprecationWarning.__le__" => "Return self<=value.", + "builtins.DeprecationWarning.__lt__" => "Return self "Return self!=value.", + "builtins.DeprecationWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.DeprecationWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.DeprecationWarning.__repr__" => "Return repr(self).", + "builtins.DeprecationWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.DeprecationWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.DeprecationWarning.__str__" => "Return str(self).", + "builtins.DeprecationWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.DeprecationWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.DeprecationWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.EOFError" => "Read beyond end of file.", + "builtins.EOFError.__cause__" => "exception cause", + "builtins.EOFError.__context__" => "exception context", + "builtins.EOFError.__delattr__" => "Implement delattr(self, name).", + "builtins.EOFError.__eq__" => "Return self==value.", + "builtins.EOFError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.EOFError.__ge__" => "Return self>=value.", + "builtins.EOFError.__getattribute__" => "Return getattr(self, name).", + "builtins.EOFError.__getstate__" => "Helper for pickle.", + "builtins.EOFError.__gt__" => "Return self>value.", + "builtins.EOFError.__hash__" => "Return hash(self).", + "builtins.EOFError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.EOFError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.EOFError.__le__" => "Return self<=value.", + "builtins.EOFError.__lt__" => "Return self "Return self!=value.", + "builtins.EOFError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.EOFError.__reduce_ex__" => "Helper for pickle.", + "builtins.EOFError.__repr__" => "Return repr(self).", + "builtins.EOFError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.EOFError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.EOFError.__str__" => "Return str(self).", + "builtins.EOFError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.EOFError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.EOFError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.EncodingWarning" => "Base class for warnings about encodings.", + "builtins.EncodingWarning.__cause__" => "exception cause", + "builtins.EncodingWarning.__context__" => "exception context", + "builtins.EncodingWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.EncodingWarning.__eq__" => "Return self==value.", + "builtins.EncodingWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.EncodingWarning.__ge__" => "Return self>=value.", + "builtins.EncodingWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.EncodingWarning.__getstate__" => "Helper for pickle.", + "builtins.EncodingWarning.__gt__" => "Return self>value.", + "builtins.EncodingWarning.__hash__" => "Return hash(self).", + "builtins.EncodingWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.EncodingWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.EncodingWarning.__le__" => "Return self<=value.", + "builtins.EncodingWarning.__lt__" => "Return self "Return self!=value.", + "builtins.EncodingWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.EncodingWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.EncodingWarning.__repr__" => "Return repr(self).", + "builtins.EncodingWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.EncodingWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.EncodingWarning.__str__" => "Return str(self).", + "builtins.EncodingWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.EncodingWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.EncodingWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.EnvironmentError" => "Base class for I/O related errors.", + "builtins.EnvironmentError.__cause__" => "exception cause", + "builtins.EnvironmentError.__context__" => "exception context", + "builtins.EnvironmentError.__delattr__" => "Implement delattr(self, name).", + "builtins.EnvironmentError.__eq__" => "Return self==value.", + "builtins.EnvironmentError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.EnvironmentError.__ge__" => "Return self>=value.", + "builtins.EnvironmentError.__getattribute__" => "Return getattr(self, name).", + "builtins.EnvironmentError.__getstate__" => "Helper for pickle.", + "builtins.EnvironmentError.__gt__" => "Return self>value.", + "builtins.EnvironmentError.__hash__" => "Return hash(self).", + "builtins.EnvironmentError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.EnvironmentError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.EnvironmentError.__le__" => "Return self<=value.", + "builtins.EnvironmentError.__lt__" => "Return self "Return self!=value.", + "builtins.EnvironmentError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.EnvironmentError.__reduce_ex__" => "Helper for pickle.", + "builtins.EnvironmentError.__repr__" => "Return repr(self).", + "builtins.EnvironmentError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.EnvironmentError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.EnvironmentError.__str__" => "Return str(self).", + "builtins.EnvironmentError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.EnvironmentError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.EnvironmentError.errno" => "POSIX exception code", + "builtins.EnvironmentError.filename" => "exception filename", + "builtins.EnvironmentError.filename2" => "second exception filename", + "builtins.EnvironmentError.strerror" => "exception strerror", + "builtins.EnvironmentError.winerror" => "Win32 exception code", + "builtins.EnvironmentError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.Exception" => "Common base class for all non-exit exceptions.", + "builtins.Exception.__cause__" => "exception cause", + "builtins.Exception.__context__" => "exception context", + "builtins.Exception.__delattr__" => "Implement delattr(self, name).", + "builtins.Exception.__eq__" => "Return self==value.", + "builtins.Exception.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.Exception.__ge__" => "Return self>=value.", + "builtins.Exception.__getattribute__" => "Return getattr(self, name).", + "builtins.Exception.__getstate__" => "Helper for pickle.", + "builtins.Exception.__gt__" => "Return self>value.", + "builtins.Exception.__hash__" => "Return hash(self).", + "builtins.Exception.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.Exception.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.Exception.__le__" => "Return self<=value.", + "builtins.Exception.__lt__" => "Return self "Return self!=value.", + "builtins.Exception.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.Exception.__reduce_ex__" => "Helper for pickle.", + "builtins.Exception.__repr__" => "Return repr(self).", + "builtins.Exception.__setattr__" => "Implement setattr(self, name, value).", + "builtins.Exception.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.Exception.__str__" => "Return str(self).", + "builtins.Exception.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.Exception.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.Exception.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ExceptionGroup.__cause__" => "exception cause", + "builtins.ExceptionGroup.__class_getitem__" => "See PEP 585", + "builtins.ExceptionGroup.__context__" => "exception context", + "builtins.ExceptionGroup.__delattr__" => "Implement delattr(self, name).", + "builtins.ExceptionGroup.__eq__" => "Return self==value.", + "builtins.ExceptionGroup.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ExceptionGroup.__ge__" => "Return self>=value.", + "builtins.ExceptionGroup.__getattribute__" => "Return getattr(self, name).", + "builtins.ExceptionGroup.__getstate__" => "Helper for pickle.", + "builtins.ExceptionGroup.__gt__" => "Return self>value.", + "builtins.ExceptionGroup.__hash__" => "Return hash(self).", + "builtins.ExceptionGroup.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ExceptionGroup.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ExceptionGroup.__le__" => "Return self<=value.", + "builtins.ExceptionGroup.__lt__" => "Return self "Return self!=value.", + "builtins.ExceptionGroup.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ExceptionGroup.__reduce_ex__" => "Helper for pickle.", + "builtins.ExceptionGroup.__repr__" => "Return repr(self).", + "builtins.ExceptionGroup.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ExceptionGroup.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ExceptionGroup.__str__" => "Return str(self).", + "builtins.ExceptionGroup.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ExceptionGroup.__weakref__" => "list of weak references to the object", + "builtins.ExceptionGroup.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ExceptionGroup.exceptions" => "nested exceptions", + "builtins.ExceptionGroup.message" => "exception message", + "builtins.ExceptionGroup.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.FileExistsError" => "File already exists.", + "builtins.FileExistsError.__cause__" => "exception cause", + "builtins.FileExistsError.__context__" => "exception context", + "builtins.FileExistsError.__delattr__" => "Implement delattr(self, name).", + "builtins.FileExistsError.__eq__" => "Return self==value.", + "builtins.FileExistsError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.FileExistsError.__ge__" => "Return self>=value.", + "builtins.FileExistsError.__getattribute__" => "Return getattr(self, name).", + "builtins.FileExistsError.__getstate__" => "Helper for pickle.", + "builtins.FileExistsError.__gt__" => "Return self>value.", + "builtins.FileExistsError.__hash__" => "Return hash(self).", + "builtins.FileExistsError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.FileExistsError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.FileExistsError.__le__" => "Return self<=value.", + "builtins.FileExistsError.__lt__" => "Return self "Return self!=value.", + "builtins.FileExistsError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.FileExistsError.__reduce_ex__" => "Helper for pickle.", + "builtins.FileExistsError.__repr__" => "Return repr(self).", + "builtins.FileExistsError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.FileExistsError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.FileExistsError.__str__" => "Return str(self).", + "builtins.FileExistsError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.FileExistsError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.FileExistsError.errno" => "POSIX exception code", + "builtins.FileExistsError.filename" => "exception filename", + "builtins.FileExistsError.filename2" => "second exception filename", + "builtins.FileExistsError.strerror" => "exception strerror", + "builtins.FileExistsError.winerror" => "Win32 exception code", + "builtins.FileExistsError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.FileNotFoundError" => "File not found.", + "builtins.FileNotFoundError.__cause__" => "exception cause", + "builtins.FileNotFoundError.__context__" => "exception context", + "builtins.FileNotFoundError.__delattr__" => "Implement delattr(self, name).", + "builtins.FileNotFoundError.__eq__" => "Return self==value.", + "builtins.FileNotFoundError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.FileNotFoundError.__ge__" => "Return self>=value.", + "builtins.FileNotFoundError.__getattribute__" => "Return getattr(self, name).", + "builtins.FileNotFoundError.__getstate__" => "Helper for pickle.", + "builtins.FileNotFoundError.__gt__" => "Return self>value.", + "builtins.FileNotFoundError.__hash__" => "Return hash(self).", + "builtins.FileNotFoundError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.FileNotFoundError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.FileNotFoundError.__le__" => "Return self<=value.", + "builtins.FileNotFoundError.__lt__" => "Return self "Return self!=value.", + "builtins.FileNotFoundError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.FileNotFoundError.__reduce_ex__" => "Helper for pickle.", + "builtins.FileNotFoundError.__repr__" => "Return repr(self).", + "builtins.FileNotFoundError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.FileNotFoundError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.FileNotFoundError.__str__" => "Return str(self).", + "builtins.FileNotFoundError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.FileNotFoundError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.FileNotFoundError.errno" => "POSIX exception code", + "builtins.FileNotFoundError.filename" => "exception filename", + "builtins.FileNotFoundError.filename2" => "second exception filename", + "builtins.FileNotFoundError.strerror" => "exception strerror", + "builtins.FileNotFoundError.winerror" => "Win32 exception code", + "builtins.FileNotFoundError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.FloatingPointError" => "Floating-point operation failed.", + "builtins.FloatingPointError.__cause__" => "exception cause", + "builtins.FloatingPointError.__context__" => "exception context", + "builtins.FloatingPointError.__delattr__" => "Implement delattr(self, name).", + "builtins.FloatingPointError.__eq__" => "Return self==value.", + "builtins.FloatingPointError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.FloatingPointError.__ge__" => "Return self>=value.", + "builtins.FloatingPointError.__getattribute__" => "Return getattr(self, name).", + "builtins.FloatingPointError.__getstate__" => "Helper for pickle.", + "builtins.FloatingPointError.__gt__" => "Return self>value.", + "builtins.FloatingPointError.__hash__" => "Return hash(self).", + "builtins.FloatingPointError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.FloatingPointError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.FloatingPointError.__le__" => "Return self<=value.", + "builtins.FloatingPointError.__lt__" => "Return self "Return self!=value.", + "builtins.FloatingPointError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.FloatingPointError.__reduce_ex__" => "Helper for pickle.", + "builtins.FloatingPointError.__repr__" => "Return repr(self).", + "builtins.FloatingPointError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.FloatingPointError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.FloatingPointError.__str__" => "Return str(self).", + "builtins.FloatingPointError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.FloatingPointError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.FloatingPointError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.FutureWarning" => "Base class for warnings about constructs that will change semantically\nin the future.", + "builtins.FutureWarning.__cause__" => "exception cause", + "builtins.FutureWarning.__context__" => "exception context", + "builtins.FutureWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.FutureWarning.__eq__" => "Return self==value.", + "builtins.FutureWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.FutureWarning.__ge__" => "Return self>=value.", + "builtins.FutureWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.FutureWarning.__getstate__" => "Helper for pickle.", + "builtins.FutureWarning.__gt__" => "Return self>value.", + "builtins.FutureWarning.__hash__" => "Return hash(self).", + "builtins.FutureWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.FutureWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.FutureWarning.__le__" => "Return self<=value.", + "builtins.FutureWarning.__lt__" => "Return self "Return self!=value.", + "builtins.FutureWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.FutureWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.FutureWarning.__repr__" => "Return repr(self).", + "builtins.FutureWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.FutureWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.FutureWarning.__str__" => "Return str(self).", + "builtins.FutureWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.FutureWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.FutureWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.GeneratorExit" => "Request that a generator exit.", + "builtins.GeneratorExit.__cause__" => "exception cause", + "builtins.GeneratorExit.__context__" => "exception context", + "builtins.GeneratorExit.__delattr__" => "Implement delattr(self, name).", + "builtins.GeneratorExit.__eq__" => "Return self==value.", + "builtins.GeneratorExit.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.GeneratorExit.__ge__" => "Return self>=value.", + "builtins.GeneratorExit.__getattribute__" => "Return getattr(self, name).", + "builtins.GeneratorExit.__getstate__" => "Helper for pickle.", + "builtins.GeneratorExit.__gt__" => "Return self>value.", + "builtins.GeneratorExit.__hash__" => "Return hash(self).", + "builtins.GeneratorExit.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.GeneratorExit.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.GeneratorExit.__le__" => "Return self<=value.", + "builtins.GeneratorExit.__lt__" => "Return self "Return self!=value.", + "builtins.GeneratorExit.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.GeneratorExit.__reduce_ex__" => "Helper for pickle.", + "builtins.GeneratorExit.__repr__" => "Return repr(self).", + "builtins.GeneratorExit.__setattr__" => "Implement setattr(self, name, value).", + "builtins.GeneratorExit.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.GeneratorExit.__str__" => "Return str(self).", + "builtins.GeneratorExit.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.GeneratorExit.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.GeneratorExit.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.IOError" => "Base class for I/O related errors.", + "builtins.IOError.__cause__" => "exception cause", + "builtins.IOError.__context__" => "exception context", + "builtins.IOError.__delattr__" => "Implement delattr(self, name).", + "builtins.IOError.__eq__" => "Return self==value.", + "builtins.IOError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.IOError.__ge__" => "Return self>=value.", + "builtins.IOError.__getattribute__" => "Return getattr(self, name).", + "builtins.IOError.__getstate__" => "Helper for pickle.", + "builtins.IOError.__gt__" => "Return self>value.", + "builtins.IOError.__hash__" => "Return hash(self).", + "builtins.IOError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.IOError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.IOError.__le__" => "Return self<=value.", + "builtins.IOError.__lt__" => "Return self "Return self!=value.", + "builtins.IOError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.IOError.__reduce_ex__" => "Helper for pickle.", + "builtins.IOError.__repr__" => "Return repr(self).", + "builtins.IOError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.IOError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.IOError.__str__" => "Return str(self).", + "builtins.IOError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.IOError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.IOError.errno" => "POSIX exception code", + "builtins.IOError.filename" => "exception filename", + "builtins.IOError.filename2" => "second exception filename", + "builtins.IOError.strerror" => "exception strerror", + "builtins.IOError.winerror" => "Win32 exception code", + "builtins.IOError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ImportError" => "Import can't find module, or can't find name in module.", + "builtins.ImportError.__cause__" => "exception cause", + "builtins.ImportError.__context__" => "exception context", + "builtins.ImportError.__delattr__" => "Implement delattr(self, name).", + "builtins.ImportError.__eq__" => "Return self==value.", + "builtins.ImportError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ImportError.__ge__" => "Return self>=value.", + "builtins.ImportError.__getattribute__" => "Return getattr(self, name).", + "builtins.ImportError.__getstate__" => "Helper for pickle.", + "builtins.ImportError.__gt__" => "Return self>value.", + "builtins.ImportError.__hash__" => "Return hash(self).", + "builtins.ImportError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ImportError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ImportError.__le__" => "Return self<=value.", + "builtins.ImportError.__lt__" => "Return self "Return self!=value.", + "builtins.ImportError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ImportError.__reduce_ex__" => "Helper for pickle.", + "builtins.ImportError.__repr__" => "Return repr(self).", + "builtins.ImportError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ImportError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ImportError.__str__" => "Return str(self).", + "builtins.ImportError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ImportError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ImportError.msg" => "exception message", + "builtins.ImportError.name" => "module name", + "builtins.ImportError.name_from" => "name imported from module", + "builtins.ImportError.path" => "module path", + "builtins.ImportError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ImportWarning" => "Base class for warnings about probable mistakes in module imports", + "builtins.ImportWarning.__cause__" => "exception cause", + "builtins.ImportWarning.__context__" => "exception context", + "builtins.ImportWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.ImportWarning.__eq__" => "Return self==value.", + "builtins.ImportWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ImportWarning.__ge__" => "Return self>=value.", + "builtins.ImportWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.ImportWarning.__getstate__" => "Helper for pickle.", + "builtins.ImportWarning.__gt__" => "Return self>value.", + "builtins.ImportWarning.__hash__" => "Return hash(self).", + "builtins.ImportWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ImportWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ImportWarning.__le__" => "Return self<=value.", + "builtins.ImportWarning.__lt__" => "Return self "Return self!=value.", + "builtins.ImportWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ImportWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.ImportWarning.__repr__" => "Return repr(self).", + "builtins.ImportWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ImportWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ImportWarning.__str__" => "Return str(self).", + "builtins.ImportWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ImportWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ImportWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.IndentationError" => "Improper indentation.", + "builtins.IndentationError.__cause__" => "exception cause", + "builtins.IndentationError.__context__" => "exception context", + "builtins.IndentationError.__delattr__" => "Implement delattr(self, name).", + "builtins.IndentationError.__eq__" => "Return self==value.", + "builtins.IndentationError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.IndentationError.__ge__" => "Return self>=value.", + "builtins.IndentationError.__getattribute__" => "Return getattr(self, name).", + "builtins.IndentationError.__getstate__" => "Helper for pickle.", + "builtins.IndentationError.__gt__" => "Return self>value.", + "builtins.IndentationError.__hash__" => "Return hash(self).", + "builtins.IndentationError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.IndentationError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.IndentationError.__le__" => "Return self<=value.", + "builtins.IndentationError.__lt__" => "Return self "Return self!=value.", + "builtins.IndentationError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.IndentationError.__reduce_ex__" => "Helper for pickle.", + "builtins.IndentationError.__repr__" => "Return repr(self).", + "builtins.IndentationError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.IndentationError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.IndentationError.__str__" => "Return str(self).", + "builtins.IndentationError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.IndentationError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.IndentationError.end_lineno" => "exception end lineno", + "builtins.IndentationError.end_offset" => "exception end offset", + "builtins.IndentationError.filename" => "exception filename", + "builtins.IndentationError.lineno" => "exception lineno", + "builtins.IndentationError.msg" => "exception msg", + "builtins.IndentationError.offset" => "exception offset", + "builtins.IndentationError.print_file_and_line" => "exception print_file_and_line", + "builtins.IndentationError.text" => "exception text", + "builtins.IndentationError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.IndexError" => "Sequence index out of range.", + "builtins.IndexError.__cause__" => "exception cause", + "builtins.IndexError.__context__" => "exception context", + "builtins.IndexError.__delattr__" => "Implement delattr(self, name).", + "builtins.IndexError.__eq__" => "Return self==value.", + "builtins.IndexError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.IndexError.__ge__" => "Return self>=value.", + "builtins.IndexError.__getattribute__" => "Return getattr(self, name).", + "builtins.IndexError.__getstate__" => "Helper for pickle.", + "builtins.IndexError.__gt__" => "Return self>value.", + "builtins.IndexError.__hash__" => "Return hash(self).", + "builtins.IndexError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.IndexError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.IndexError.__le__" => "Return self<=value.", + "builtins.IndexError.__lt__" => "Return self "Return self!=value.", + "builtins.IndexError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.IndexError.__reduce_ex__" => "Helper for pickle.", + "builtins.IndexError.__repr__" => "Return repr(self).", + "builtins.IndexError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.IndexError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.IndexError.__str__" => "Return str(self).", + "builtins.IndexError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.IndexError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.IndexError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.InterruptedError" => "Interrupted by signal.", + "builtins.InterruptedError.__cause__" => "exception cause", + "builtins.InterruptedError.__context__" => "exception context", + "builtins.InterruptedError.__delattr__" => "Implement delattr(self, name).", + "builtins.InterruptedError.__eq__" => "Return self==value.", + "builtins.InterruptedError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.InterruptedError.__ge__" => "Return self>=value.", + "builtins.InterruptedError.__getattribute__" => "Return getattr(self, name).", + "builtins.InterruptedError.__getstate__" => "Helper for pickle.", + "builtins.InterruptedError.__gt__" => "Return self>value.", + "builtins.InterruptedError.__hash__" => "Return hash(self).", + "builtins.InterruptedError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.InterruptedError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.InterruptedError.__le__" => "Return self<=value.", + "builtins.InterruptedError.__lt__" => "Return self "Return self!=value.", + "builtins.InterruptedError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.InterruptedError.__reduce_ex__" => "Helper for pickle.", + "builtins.InterruptedError.__repr__" => "Return repr(self).", + "builtins.InterruptedError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.InterruptedError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.InterruptedError.__str__" => "Return str(self).", + "builtins.InterruptedError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.InterruptedError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.InterruptedError.errno" => "POSIX exception code", + "builtins.InterruptedError.filename" => "exception filename", + "builtins.InterruptedError.filename2" => "second exception filename", + "builtins.InterruptedError.strerror" => "exception strerror", + "builtins.InterruptedError.winerror" => "Win32 exception code", + "builtins.InterruptedError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.IsADirectoryError" => "Operation doesn't work on directories.", + "builtins.IsADirectoryError.__cause__" => "exception cause", + "builtins.IsADirectoryError.__context__" => "exception context", + "builtins.IsADirectoryError.__delattr__" => "Implement delattr(self, name).", + "builtins.IsADirectoryError.__eq__" => "Return self==value.", + "builtins.IsADirectoryError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.IsADirectoryError.__ge__" => "Return self>=value.", + "builtins.IsADirectoryError.__getattribute__" => "Return getattr(self, name).", + "builtins.IsADirectoryError.__getstate__" => "Helper for pickle.", + "builtins.IsADirectoryError.__gt__" => "Return self>value.", + "builtins.IsADirectoryError.__hash__" => "Return hash(self).", + "builtins.IsADirectoryError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.IsADirectoryError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.IsADirectoryError.__le__" => "Return self<=value.", + "builtins.IsADirectoryError.__lt__" => "Return self "Return self!=value.", + "builtins.IsADirectoryError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.IsADirectoryError.__reduce_ex__" => "Helper for pickle.", + "builtins.IsADirectoryError.__repr__" => "Return repr(self).", + "builtins.IsADirectoryError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.IsADirectoryError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.IsADirectoryError.__str__" => "Return str(self).", + "builtins.IsADirectoryError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.IsADirectoryError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.IsADirectoryError.errno" => "POSIX exception code", + "builtins.IsADirectoryError.filename" => "exception filename", + "builtins.IsADirectoryError.filename2" => "second exception filename", + "builtins.IsADirectoryError.strerror" => "exception strerror", + "builtins.IsADirectoryError.winerror" => "Win32 exception code", + "builtins.IsADirectoryError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.KeyError" => "Mapping key not found.", + "builtins.KeyError.__cause__" => "exception cause", + "builtins.KeyError.__context__" => "exception context", + "builtins.KeyError.__delattr__" => "Implement delattr(self, name).", + "builtins.KeyError.__eq__" => "Return self==value.", + "builtins.KeyError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.KeyError.__ge__" => "Return self>=value.", + "builtins.KeyError.__getattribute__" => "Return getattr(self, name).", + "builtins.KeyError.__getstate__" => "Helper for pickle.", + "builtins.KeyError.__gt__" => "Return self>value.", + "builtins.KeyError.__hash__" => "Return hash(self).", + "builtins.KeyError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.KeyError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.KeyError.__le__" => "Return self<=value.", + "builtins.KeyError.__lt__" => "Return self "Return self!=value.", + "builtins.KeyError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.KeyError.__reduce_ex__" => "Helper for pickle.", + "builtins.KeyError.__repr__" => "Return repr(self).", + "builtins.KeyError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.KeyError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.KeyError.__str__" => "Return str(self).", + "builtins.KeyError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.KeyError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.KeyError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.KeyboardInterrupt" => "Program interrupted by user.", + "builtins.KeyboardInterrupt.__cause__" => "exception cause", + "builtins.KeyboardInterrupt.__context__" => "exception context", + "builtins.KeyboardInterrupt.__delattr__" => "Implement delattr(self, name).", + "builtins.KeyboardInterrupt.__eq__" => "Return self==value.", + "builtins.KeyboardInterrupt.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.KeyboardInterrupt.__ge__" => "Return self>=value.", + "builtins.KeyboardInterrupt.__getattribute__" => "Return getattr(self, name).", + "builtins.KeyboardInterrupt.__getstate__" => "Helper for pickle.", + "builtins.KeyboardInterrupt.__gt__" => "Return self>value.", + "builtins.KeyboardInterrupt.__hash__" => "Return hash(self).", + "builtins.KeyboardInterrupt.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.KeyboardInterrupt.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.KeyboardInterrupt.__le__" => "Return self<=value.", + "builtins.KeyboardInterrupt.__lt__" => "Return self "Return self!=value.", + "builtins.KeyboardInterrupt.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.KeyboardInterrupt.__reduce_ex__" => "Helper for pickle.", + "builtins.KeyboardInterrupt.__repr__" => "Return repr(self).", + "builtins.KeyboardInterrupt.__setattr__" => "Implement setattr(self, name, value).", + "builtins.KeyboardInterrupt.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.KeyboardInterrupt.__str__" => "Return str(self).", + "builtins.KeyboardInterrupt.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.KeyboardInterrupt.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.KeyboardInterrupt.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.LookupError" => "Base class for lookup errors.", + "builtins.LookupError.__cause__" => "exception cause", + "builtins.LookupError.__context__" => "exception context", + "builtins.LookupError.__delattr__" => "Implement delattr(self, name).", + "builtins.LookupError.__eq__" => "Return self==value.", + "builtins.LookupError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.LookupError.__ge__" => "Return self>=value.", + "builtins.LookupError.__getattribute__" => "Return getattr(self, name).", + "builtins.LookupError.__getstate__" => "Helper for pickle.", + "builtins.LookupError.__gt__" => "Return self>value.", + "builtins.LookupError.__hash__" => "Return hash(self).", + "builtins.LookupError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.LookupError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.LookupError.__le__" => "Return self<=value.", + "builtins.LookupError.__lt__" => "Return self "Return self!=value.", + "builtins.LookupError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.LookupError.__reduce_ex__" => "Helper for pickle.", + "builtins.LookupError.__repr__" => "Return repr(self).", + "builtins.LookupError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.LookupError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.LookupError.__str__" => "Return str(self).", + "builtins.LookupError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.LookupError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.LookupError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.MemoryError" => "Out of memory.", + "builtins.MemoryError.__cause__" => "exception cause", + "builtins.MemoryError.__context__" => "exception context", + "builtins.MemoryError.__delattr__" => "Implement delattr(self, name).", + "builtins.MemoryError.__eq__" => "Return self==value.", + "builtins.MemoryError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.MemoryError.__ge__" => "Return self>=value.", + "builtins.MemoryError.__getattribute__" => "Return getattr(self, name).", + "builtins.MemoryError.__getstate__" => "Helper for pickle.", + "builtins.MemoryError.__gt__" => "Return self>value.", + "builtins.MemoryError.__hash__" => "Return hash(self).", + "builtins.MemoryError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.MemoryError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.MemoryError.__le__" => "Return self<=value.", + "builtins.MemoryError.__lt__" => "Return self "Return self!=value.", + "builtins.MemoryError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.MemoryError.__reduce_ex__" => "Helper for pickle.", + "builtins.MemoryError.__repr__" => "Return repr(self).", + "builtins.MemoryError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.MemoryError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.MemoryError.__str__" => "Return str(self).", + "builtins.MemoryError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.MemoryError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.MemoryError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ModuleNotFoundError" => "Module not found.", + "builtins.ModuleNotFoundError.__cause__" => "exception cause", + "builtins.ModuleNotFoundError.__context__" => "exception context", + "builtins.ModuleNotFoundError.__delattr__" => "Implement delattr(self, name).", + "builtins.ModuleNotFoundError.__eq__" => "Return self==value.", + "builtins.ModuleNotFoundError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ModuleNotFoundError.__ge__" => "Return self>=value.", + "builtins.ModuleNotFoundError.__getattribute__" => "Return getattr(self, name).", + "builtins.ModuleNotFoundError.__getstate__" => "Helper for pickle.", + "builtins.ModuleNotFoundError.__gt__" => "Return self>value.", + "builtins.ModuleNotFoundError.__hash__" => "Return hash(self).", + "builtins.ModuleNotFoundError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ModuleNotFoundError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ModuleNotFoundError.__le__" => "Return self<=value.", + "builtins.ModuleNotFoundError.__lt__" => "Return self "Return self!=value.", + "builtins.ModuleNotFoundError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ModuleNotFoundError.__reduce_ex__" => "Helper for pickle.", + "builtins.ModuleNotFoundError.__repr__" => "Return repr(self).", + "builtins.ModuleNotFoundError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ModuleNotFoundError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ModuleNotFoundError.__str__" => "Return str(self).", + "builtins.ModuleNotFoundError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ModuleNotFoundError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ModuleNotFoundError.msg" => "exception message", + "builtins.ModuleNotFoundError.name" => "module name", + "builtins.ModuleNotFoundError.name_from" => "name imported from module", + "builtins.ModuleNotFoundError.path" => "module path", + "builtins.ModuleNotFoundError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.NameError" => "Name not found globally.", + "builtins.NameError.__cause__" => "exception cause", + "builtins.NameError.__context__" => "exception context", + "builtins.NameError.__delattr__" => "Implement delattr(self, name).", + "builtins.NameError.__eq__" => "Return self==value.", + "builtins.NameError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.NameError.__ge__" => "Return self>=value.", + "builtins.NameError.__getattribute__" => "Return getattr(self, name).", + "builtins.NameError.__getstate__" => "Helper for pickle.", + "builtins.NameError.__gt__" => "Return self>value.", + "builtins.NameError.__hash__" => "Return hash(self).", + "builtins.NameError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.NameError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.NameError.__le__" => "Return self<=value.", + "builtins.NameError.__lt__" => "Return self "Return self!=value.", + "builtins.NameError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.NameError.__reduce_ex__" => "Helper for pickle.", + "builtins.NameError.__repr__" => "Return repr(self).", + "builtins.NameError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.NameError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.NameError.__str__" => "Return str(self).", + "builtins.NameError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.NameError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.NameError.name" => "name", + "builtins.NameError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.NoneType" => "The type of the None singleton.", + "builtins.NoneType.__bool__" => "True if self else False", + "builtins.NoneType.__delattr__" => "Implement delattr(self, name).", + "builtins.NoneType.__eq__" => "Return self==value.", + "builtins.NoneType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.NoneType.__ge__" => "Return self>=value.", + "builtins.NoneType.__getattribute__" => "Return getattr(self, name).", + "builtins.NoneType.__getstate__" => "Helper for pickle.", + "builtins.NoneType.__gt__" => "Return self>value.", + "builtins.NoneType.__hash__" => "Return hash(self).", + "builtins.NoneType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.NoneType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.NoneType.__le__" => "Return self<=value.", + "builtins.NoneType.__lt__" => "Return self "Return self!=value.", + "builtins.NoneType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.NoneType.__reduce__" => "Helper for pickle.", + "builtins.NoneType.__reduce_ex__" => "Helper for pickle.", + "builtins.NoneType.__repr__" => "Return repr(self).", + "builtins.NoneType.__setattr__" => "Implement setattr(self, name, value).", + "builtins.NoneType.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.NoneType.__str__" => "Return str(self).", + "builtins.NoneType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.NotADirectoryError" => "Operation only works on directories.", + "builtins.NotADirectoryError.__cause__" => "exception cause", + "builtins.NotADirectoryError.__context__" => "exception context", + "builtins.NotADirectoryError.__delattr__" => "Implement delattr(self, name).", + "builtins.NotADirectoryError.__eq__" => "Return self==value.", + "builtins.NotADirectoryError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.NotADirectoryError.__ge__" => "Return self>=value.", + "builtins.NotADirectoryError.__getattribute__" => "Return getattr(self, name).", + "builtins.NotADirectoryError.__getstate__" => "Helper for pickle.", + "builtins.NotADirectoryError.__gt__" => "Return self>value.", + "builtins.NotADirectoryError.__hash__" => "Return hash(self).", + "builtins.NotADirectoryError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.NotADirectoryError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.NotADirectoryError.__le__" => "Return self<=value.", + "builtins.NotADirectoryError.__lt__" => "Return self "Return self!=value.", + "builtins.NotADirectoryError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.NotADirectoryError.__reduce_ex__" => "Helper for pickle.", + "builtins.NotADirectoryError.__repr__" => "Return repr(self).", + "builtins.NotADirectoryError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.NotADirectoryError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.NotADirectoryError.__str__" => "Return str(self).", + "builtins.NotADirectoryError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.NotADirectoryError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.NotADirectoryError.errno" => "POSIX exception code", + "builtins.NotADirectoryError.filename" => "exception filename", + "builtins.NotADirectoryError.filename2" => "second exception filename", + "builtins.NotADirectoryError.strerror" => "exception strerror", + "builtins.NotADirectoryError.winerror" => "Win32 exception code", + "builtins.NotADirectoryError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.NotImplementedError" => "Method or function hasn't been implemented yet.", + "builtins.NotImplementedError.__cause__" => "exception cause", + "builtins.NotImplementedError.__context__" => "exception context", + "builtins.NotImplementedError.__delattr__" => "Implement delattr(self, name).", + "builtins.NotImplementedError.__eq__" => "Return self==value.", + "builtins.NotImplementedError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.NotImplementedError.__ge__" => "Return self>=value.", + "builtins.NotImplementedError.__getattribute__" => "Return getattr(self, name).", + "builtins.NotImplementedError.__getstate__" => "Helper for pickle.", + "builtins.NotImplementedError.__gt__" => "Return self>value.", + "builtins.NotImplementedError.__hash__" => "Return hash(self).", + "builtins.NotImplementedError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.NotImplementedError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.NotImplementedError.__le__" => "Return self<=value.", + "builtins.NotImplementedError.__lt__" => "Return self "Return self!=value.", + "builtins.NotImplementedError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.NotImplementedError.__reduce_ex__" => "Helper for pickle.", + "builtins.NotImplementedError.__repr__" => "Return repr(self).", + "builtins.NotImplementedError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.NotImplementedError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.NotImplementedError.__str__" => "Return str(self).", + "builtins.NotImplementedError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.NotImplementedError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.NotImplementedError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.OSError" => "Base class for I/O related errors.", + "builtins.OSError.__cause__" => "exception cause", + "builtins.OSError.__context__" => "exception context", + "builtins.OSError.__delattr__" => "Implement delattr(self, name).", + "builtins.OSError.__eq__" => "Return self==value.", + "builtins.OSError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.OSError.__ge__" => "Return self>=value.", + "builtins.OSError.__getattribute__" => "Return getattr(self, name).", + "builtins.OSError.__getstate__" => "Helper for pickle.", + "builtins.OSError.__gt__" => "Return self>value.", + "builtins.OSError.__hash__" => "Return hash(self).", + "builtins.OSError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.OSError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.OSError.__le__" => "Return self<=value.", + "builtins.OSError.__lt__" => "Return self "Return self!=value.", + "builtins.OSError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.OSError.__reduce_ex__" => "Helper for pickle.", + "builtins.OSError.__repr__" => "Return repr(self).", + "builtins.OSError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.OSError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.OSError.__str__" => "Return str(self).", + "builtins.OSError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.OSError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.OSError.errno" => "POSIX exception code", + "builtins.OSError.filename" => "exception filename", + "builtins.OSError.filename2" => "second exception filename", + "builtins.OSError.strerror" => "exception strerror", + "builtins.OSError.winerror" => "Win32 exception code", + "builtins.OSError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.OverflowError" => "Result too large to be represented.", + "builtins.OverflowError.__cause__" => "exception cause", + "builtins.OverflowError.__context__" => "exception context", + "builtins.OverflowError.__delattr__" => "Implement delattr(self, name).", + "builtins.OverflowError.__eq__" => "Return self==value.", + "builtins.OverflowError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.OverflowError.__ge__" => "Return self>=value.", + "builtins.OverflowError.__getattribute__" => "Return getattr(self, name).", + "builtins.OverflowError.__getstate__" => "Helper for pickle.", + "builtins.OverflowError.__gt__" => "Return self>value.", + "builtins.OverflowError.__hash__" => "Return hash(self).", + "builtins.OverflowError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.OverflowError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.OverflowError.__le__" => "Return self<=value.", + "builtins.OverflowError.__lt__" => "Return self "Return self!=value.", + "builtins.OverflowError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.OverflowError.__reduce_ex__" => "Helper for pickle.", + "builtins.OverflowError.__repr__" => "Return repr(self).", + "builtins.OverflowError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.OverflowError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.OverflowError.__str__" => "Return str(self).", + "builtins.OverflowError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.OverflowError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.OverflowError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.PendingDeprecationWarning" => "Base class for warnings about features which will be deprecated\nin the future.", + "builtins.PendingDeprecationWarning.__cause__" => "exception cause", + "builtins.PendingDeprecationWarning.__context__" => "exception context", + "builtins.PendingDeprecationWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.PendingDeprecationWarning.__eq__" => "Return self==value.", + "builtins.PendingDeprecationWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.PendingDeprecationWarning.__ge__" => "Return self>=value.", + "builtins.PendingDeprecationWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.PendingDeprecationWarning.__getstate__" => "Helper for pickle.", + "builtins.PendingDeprecationWarning.__gt__" => "Return self>value.", + "builtins.PendingDeprecationWarning.__hash__" => "Return hash(self).", + "builtins.PendingDeprecationWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.PendingDeprecationWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.PendingDeprecationWarning.__le__" => "Return self<=value.", + "builtins.PendingDeprecationWarning.__lt__" => "Return self "Return self!=value.", + "builtins.PendingDeprecationWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.PendingDeprecationWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.PendingDeprecationWarning.__repr__" => "Return repr(self).", + "builtins.PendingDeprecationWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.PendingDeprecationWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.PendingDeprecationWarning.__str__" => "Return str(self).", + "builtins.PendingDeprecationWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.PendingDeprecationWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.PendingDeprecationWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.PermissionError" => "Not enough permissions.", + "builtins.PermissionError.__cause__" => "exception cause", + "builtins.PermissionError.__context__" => "exception context", + "builtins.PermissionError.__delattr__" => "Implement delattr(self, name).", + "builtins.PermissionError.__eq__" => "Return self==value.", + "builtins.PermissionError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.PermissionError.__ge__" => "Return self>=value.", + "builtins.PermissionError.__getattribute__" => "Return getattr(self, name).", + "builtins.PermissionError.__getstate__" => "Helper for pickle.", + "builtins.PermissionError.__gt__" => "Return self>value.", + "builtins.PermissionError.__hash__" => "Return hash(self).", + "builtins.PermissionError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.PermissionError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.PermissionError.__le__" => "Return self<=value.", + "builtins.PermissionError.__lt__" => "Return self "Return self!=value.", + "builtins.PermissionError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.PermissionError.__reduce_ex__" => "Helper for pickle.", + "builtins.PermissionError.__repr__" => "Return repr(self).", + "builtins.PermissionError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.PermissionError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.PermissionError.__str__" => "Return str(self).", + "builtins.PermissionError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.PermissionError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.PermissionError.errno" => "POSIX exception code", + "builtins.PermissionError.filename" => "exception filename", + "builtins.PermissionError.filename2" => "second exception filename", + "builtins.PermissionError.strerror" => "exception strerror", + "builtins.PermissionError.winerror" => "Win32 exception code", + "builtins.PermissionError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ProcessLookupError" => "Process not found.", + "builtins.ProcessLookupError.__cause__" => "exception cause", + "builtins.ProcessLookupError.__context__" => "exception context", + "builtins.ProcessLookupError.__delattr__" => "Implement delattr(self, name).", + "builtins.ProcessLookupError.__eq__" => "Return self==value.", + "builtins.ProcessLookupError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ProcessLookupError.__ge__" => "Return self>=value.", + "builtins.ProcessLookupError.__getattribute__" => "Return getattr(self, name).", + "builtins.ProcessLookupError.__getstate__" => "Helper for pickle.", + "builtins.ProcessLookupError.__gt__" => "Return self>value.", + "builtins.ProcessLookupError.__hash__" => "Return hash(self).", + "builtins.ProcessLookupError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ProcessLookupError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ProcessLookupError.__le__" => "Return self<=value.", + "builtins.ProcessLookupError.__lt__" => "Return self "Return self!=value.", + "builtins.ProcessLookupError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ProcessLookupError.__reduce_ex__" => "Helper for pickle.", + "builtins.ProcessLookupError.__repr__" => "Return repr(self).", + "builtins.ProcessLookupError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ProcessLookupError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ProcessLookupError.__str__" => "Return str(self).", + "builtins.ProcessLookupError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ProcessLookupError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ProcessLookupError.errno" => "POSIX exception code", + "builtins.ProcessLookupError.filename" => "exception filename", + "builtins.ProcessLookupError.filename2" => "second exception filename", + "builtins.ProcessLookupError.strerror" => "exception strerror", + "builtins.ProcessLookupError.winerror" => "Win32 exception code", + "builtins.ProcessLookupError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.PythonFinalizationError" => "Operation blocked during Python finalization.", + "builtins.PythonFinalizationError.__cause__" => "exception cause", + "builtins.PythonFinalizationError.__context__" => "exception context", + "builtins.PythonFinalizationError.__delattr__" => "Implement delattr(self, name).", + "builtins.PythonFinalizationError.__eq__" => "Return self==value.", + "builtins.PythonFinalizationError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.PythonFinalizationError.__ge__" => "Return self>=value.", + "builtins.PythonFinalizationError.__getattribute__" => "Return getattr(self, name).", + "builtins.PythonFinalizationError.__getstate__" => "Helper for pickle.", + "builtins.PythonFinalizationError.__gt__" => "Return self>value.", + "builtins.PythonFinalizationError.__hash__" => "Return hash(self).", + "builtins.PythonFinalizationError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.PythonFinalizationError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.PythonFinalizationError.__le__" => "Return self<=value.", + "builtins.PythonFinalizationError.__lt__" => "Return self "Return self!=value.", + "builtins.PythonFinalizationError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.PythonFinalizationError.__reduce_ex__" => "Helper for pickle.", + "builtins.PythonFinalizationError.__repr__" => "Return repr(self).", + "builtins.PythonFinalizationError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.PythonFinalizationError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.PythonFinalizationError.__str__" => "Return str(self).", + "builtins.PythonFinalizationError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.PythonFinalizationError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.PythonFinalizationError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.RecursionError" => "Recursion limit exceeded.", + "builtins.RecursionError.__cause__" => "exception cause", + "builtins.RecursionError.__context__" => "exception context", + "builtins.RecursionError.__delattr__" => "Implement delattr(self, name).", + "builtins.RecursionError.__eq__" => "Return self==value.", + "builtins.RecursionError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.RecursionError.__ge__" => "Return self>=value.", + "builtins.RecursionError.__getattribute__" => "Return getattr(self, name).", + "builtins.RecursionError.__getstate__" => "Helper for pickle.", + "builtins.RecursionError.__gt__" => "Return self>value.", + "builtins.RecursionError.__hash__" => "Return hash(self).", + "builtins.RecursionError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.RecursionError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.RecursionError.__le__" => "Return self<=value.", + "builtins.RecursionError.__lt__" => "Return self "Return self!=value.", + "builtins.RecursionError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.RecursionError.__reduce_ex__" => "Helper for pickle.", + "builtins.RecursionError.__repr__" => "Return repr(self).", + "builtins.RecursionError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.RecursionError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.RecursionError.__str__" => "Return str(self).", + "builtins.RecursionError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.RecursionError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.RecursionError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ReferenceError" => "Weak ref proxy used after referent went away.", + "builtins.ReferenceError.__cause__" => "exception cause", + "builtins.ReferenceError.__context__" => "exception context", + "builtins.ReferenceError.__delattr__" => "Implement delattr(self, name).", + "builtins.ReferenceError.__eq__" => "Return self==value.", + "builtins.ReferenceError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ReferenceError.__ge__" => "Return self>=value.", + "builtins.ReferenceError.__getattribute__" => "Return getattr(self, name).", + "builtins.ReferenceError.__getstate__" => "Helper for pickle.", + "builtins.ReferenceError.__gt__" => "Return self>value.", + "builtins.ReferenceError.__hash__" => "Return hash(self).", + "builtins.ReferenceError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ReferenceError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ReferenceError.__le__" => "Return self<=value.", + "builtins.ReferenceError.__lt__" => "Return self "Return self!=value.", + "builtins.ReferenceError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ReferenceError.__reduce_ex__" => "Helper for pickle.", + "builtins.ReferenceError.__repr__" => "Return repr(self).", + "builtins.ReferenceError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ReferenceError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ReferenceError.__str__" => "Return str(self).", + "builtins.ReferenceError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ReferenceError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ReferenceError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ResourceWarning" => "Base class for warnings about resource usage.", + "builtins.ResourceWarning.__cause__" => "exception cause", + "builtins.ResourceWarning.__context__" => "exception context", + "builtins.ResourceWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.ResourceWarning.__eq__" => "Return self==value.", + "builtins.ResourceWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ResourceWarning.__ge__" => "Return self>=value.", + "builtins.ResourceWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.ResourceWarning.__getstate__" => "Helper for pickle.", + "builtins.ResourceWarning.__gt__" => "Return self>value.", + "builtins.ResourceWarning.__hash__" => "Return hash(self).", + "builtins.ResourceWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ResourceWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ResourceWarning.__le__" => "Return self<=value.", + "builtins.ResourceWarning.__lt__" => "Return self "Return self!=value.", + "builtins.ResourceWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ResourceWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.ResourceWarning.__repr__" => "Return repr(self).", + "builtins.ResourceWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ResourceWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ResourceWarning.__str__" => "Return str(self).", + "builtins.ResourceWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ResourceWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ResourceWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.RuntimeError" => "Unspecified run-time error.", + "builtins.RuntimeError.__cause__" => "exception cause", + "builtins.RuntimeError.__context__" => "exception context", + "builtins.RuntimeError.__delattr__" => "Implement delattr(self, name).", + "builtins.RuntimeError.__eq__" => "Return self==value.", + "builtins.RuntimeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.RuntimeError.__ge__" => "Return self>=value.", + "builtins.RuntimeError.__getattribute__" => "Return getattr(self, name).", + "builtins.RuntimeError.__getstate__" => "Helper for pickle.", + "builtins.RuntimeError.__gt__" => "Return self>value.", + "builtins.RuntimeError.__hash__" => "Return hash(self).", + "builtins.RuntimeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.RuntimeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.RuntimeError.__le__" => "Return self<=value.", + "builtins.RuntimeError.__lt__" => "Return self "Return self!=value.", + "builtins.RuntimeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.RuntimeError.__reduce_ex__" => "Helper for pickle.", + "builtins.RuntimeError.__repr__" => "Return repr(self).", + "builtins.RuntimeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.RuntimeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.RuntimeError.__str__" => "Return str(self).", + "builtins.RuntimeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.RuntimeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.RuntimeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.RuntimeWarning" => "Base class for warnings about dubious runtime behavior.", + "builtins.RuntimeWarning.__cause__" => "exception cause", + "builtins.RuntimeWarning.__context__" => "exception context", + "builtins.RuntimeWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.RuntimeWarning.__eq__" => "Return self==value.", + "builtins.RuntimeWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.RuntimeWarning.__ge__" => "Return self>=value.", + "builtins.RuntimeWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.RuntimeWarning.__getstate__" => "Helper for pickle.", + "builtins.RuntimeWarning.__gt__" => "Return self>value.", + "builtins.RuntimeWarning.__hash__" => "Return hash(self).", + "builtins.RuntimeWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.RuntimeWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.RuntimeWarning.__le__" => "Return self<=value.", + "builtins.RuntimeWarning.__lt__" => "Return self "Return self!=value.", + "builtins.RuntimeWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.RuntimeWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.RuntimeWarning.__repr__" => "Return repr(self).", + "builtins.RuntimeWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.RuntimeWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.RuntimeWarning.__str__" => "Return str(self).", + "builtins.RuntimeWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.RuntimeWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.RuntimeWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.StopAsyncIteration" => "Signal the end from iterator.__anext__().", + "builtins.StopAsyncIteration.__cause__" => "exception cause", + "builtins.StopAsyncIteration.__context__" => "exception context", + "builtins.StopAsyncIteration.__delattr__" => "Implement delattr(self, name).", + "builtins.StopAsyncIteration.__eq__" => "Return self==value.", + "builtins.StopAsyncIteration.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.StopAsyncIteration.__ge__" => "Return self>=value.", + "builtins.StopAsyncIteration.__getattribute__" => "Return getattr(self, name).", + "builtins.StopAsyncIteration.__getstate__" => "Helper for pickle.", + "builtins.StopAsyncIteration.__gt__" => "Return self>value.", + "builtins.StopAsyncIteration.__hash__" => "Return hash(self).", + "builtins.StopAsyncIteration.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.StopAsyncIteration.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.StopAsyncIteration.__le__" => "Return self<=value.", + "builtins.StopAsyncIteration.__lt__" => "Return self "Return self!=value.", + "builtins.StopAsyncIteration.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.StopAsyncIteration.__reduce_ex__" => "Helper for pickle.", + "builtins.StopAsyncIteration.__repr__" => "Return repr(self).", + "builtins.StopAsyncIteration.__setattr__" => "Implement setattr(self, name, value).", + "builtins.StopAsyncIteration.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.StopAsyncIteration.__str__" => "Return str(self).", + "builtins.StopAsyncIteration.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.StopAsyncIteration.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.StopAsyncIteration.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.StopIteration" => "Signal the end from iterator.__next__().", + "builtins.StopIteration.__cause__" => "exception cause", + "builtins.StopIteration.__context__" => "exception context", + "builtins.StopIteration.__delattr__" => "Implement delattr(self, name).", + "builtins.StopIteration.__eq__" => "Return self==value.", + "builtins.StopIteration.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.StopIteration.__ge__" => "Return self>=value.", + "builtins.StopIteration.__getattribute__" => "Return getattr(self, name).", + "builtins.StopIteration.__getstate__" => "Helper for pickle.", + "builtins.StopIteration.__gt__" => "Return self>value.", + "builtins.StopIteration.__hash__" => "Return hash(self).", + "builtins.StopIteration.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.StopIteration.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.StopIteration.__le__" => "Return self<=value.", + "builtins.StopIteration.__lt__" => "Return self "Return self!=value.", + "builtins.StopIteration.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.StopIteration.__reduce_ex__" => "Helper for pickle.", + "builtins.StopIteration.__repr__" => "Return repr(self).", + "builtins.StopIteration.__setattr__" => "Implement setattr(self, name, value).", + "builtins.StopIteration.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.StopIteration.__str__" => "Return str(self).", + "builtins.StopIteration.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.StopIteration.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.StopIteration.value" => "generator return value", + "builtins.StopIteration.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.SyntaxError" => "Invalid syntax.", + "builtins.SyntaxError.__cause__" => "exception cause", + "builtins.SyntaxError.__context__" => "exception context", + "builtins.SyntaxError.__delattr__" => "Implement delattr(self, name).", + "builtins.SyntaxError.__eq__" => "Return self==value.", + "builtins.SyntaxError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.SyntaxError.__ge__" => "Return self>=value.", + "builtins.SyntaxError.__getattribute__" => "Return getattr(self, name).", + "builtins.SyntaxError.__getstate__" => "Helper for pickle.", + "builtins.SyntaxError.__gt__" => "Return self>value.", + "builtins.SyntaxError.__hash__" => "Return hash(self).", + "builtins.SyntaxError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.SyntaxError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.SyntaxError.__le__" => "Return self<=value.", + "builtins.SyntaxError.__lt__" => "Return self "Return self!=value.", + "builtins.SyntaxError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.SyntaxError.__reduce_ex__" => "Helper for pickle.", + "builtins.SyntaxError.__repr__" => "Return repr(self).", + "builtins.SyntaxError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.SyntaxError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.SyntaxError.__str__" => "Return str(self).", + "builtins.SyntaxError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.SyntaxError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.SyntaxError.end_lineno" => "exception end lineno", + "builtins.SyntaxError.end_offset" => "exception end offset", + "builtins.SyntaxError.filename" => "exception filename", + "builtins.SyntaxError.lineno" => "exception lineno", + "builtins.SyntaxError.msg" => "exception msg", + "builtins.SyntaxError.offset" => "exception offset", + "builtins.SyntaxError.print_file_and_line" => "exception print_file_and_line", + "builtins.SyntaxError.text" => "exception text", + "builtins.SyntaxError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.SyntaxWarning" => "Base class for warnings about dubious syntax.", + "builtins.SyntaxWarning.__cause__" => "exception cause", + "builtins.SyntaxWarning.__context__" => "exception context", + "builtins.SyntaxWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.SyntaxWarning.__eq__" => "Return self==value.", + "builtins.SyntaxWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.SyntaxWarning.__ge__" => "Return self>=value.", + "builtins.SyntaxWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.SyntaxWarning.__getstate__" => "Helper for pickle.", + "builtins.SyntaxWarning.__gt__" => "Return self>value.", + "builtins.SyntaxWarning.__hash__" => "Return hash(self).", + "builtins.SyntaxWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.SyntaxWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.SyntaxWarning.__le__" => "Return self<=value.", + "builtins.SyntaxWarning.__lt__" => "Return self "Return self!=value.", + "builtins.SyntaxWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.SyntaxWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.SyntaxWarning.__repr__" => "Return repr(self).", + "builtins.SyntaxWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.SyntaxWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.SyntaxWarning.__str__" => "Return str(self).", + "builtins.SyntaxWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.SyntaxWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.SyntaxWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.SystemError" => "Internal error in the Python interpreter.\n\nPlease report this to the Python maintainer, along with the traceback,\nthe Python version, and the hardware/OS platform and version.", + "builtins.SystemError.__cause__" => "exception cause", + "builtins.SystemError.__context__" => "exception context", + "builtins.SystemError.__delattr__" => "Implement delattr(self, name).", + "builtins.SystemError.__eq__" => "Return self==value.", + "builtins.SystemError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.SystemError.__ge__" => "Return self>=value.", + "builtins.SystemError.__getattribute__" => "Return getattr(self, name).", + "builtins.SystemError.__getstate__" => "Helper for pickle.", + "builtins.SystemError.__gt__" => "Return self>value.", + "builtins.SystemError.__hash__" => "Return hash(self).", + "builtins.SystemError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.SystemError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.SystemError.__le__" => "Return self<=value.", + "builtins.SystemError.__lt__" => "Return self "Return self!=value.", + "builtins.SystemError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.SystemError.__reduce_ex__" => "Helper for pickle.", + "builtins.SystemError.__repr__" => "Return repr(self).", + "builtins.SystemError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.SystemError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.SystemError.__str__" => "Return str(self).", + "builtins.SystemError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.SystemError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.SystemError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.SystemExit" => "Request to exit from the interpreter.", + "builtins.SystemExit.__cause__" => "exception cause", + "builtins.SystemExit.__context__" => "exception context", + "builtins.SystemExit.__delattr__" => "Implement delattr(self, name).", + "builtins.SystemExit.__eq__" => "Return self==value.", + "builtins.SystemExit.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.SystemExit.__ge__" => "Return self>=value.", + "builtins.SystemExit.__getattribute__" => "Return getattr(self, name).", + "builtins.SystemExit.__getstate__" => "Helper for pickle.", + "builtins.SystemExit.__gt__" => "Return self>value.", + "builtins.SystemExit.__hash__" => "Return hash(self).", + "builtins.SystemExit.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.SystemExit.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.SystemExit.__le__" => "Return self<=value.", + "builtins.SystemExit.__lt__" => "Return self "Return self!=value.", + "builtins.SystemExit.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.SystemExit.__reduce_ex__" => "Helper for pickle.", + "builtins.SystemExit.__repr__" => "Return repr(self).", + "builtins.SystemExit.__setattr__" => "Implement setattr(self, name, value).", + "builtins.SystemExit.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.SystemExit.__str__" => "Return str(self).", + "builtins.SystemExit.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.SystemExit.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.SystemExit.code" => "exception code", + "builtins.SystemExit.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.TabError" => "Improper mixture of spaces and tabs.", + "builtins.TabError.__cause__" => "exception cause", + "builtins.TabError.__context__" => "exception context", + "builtins.TabError.__delattr__" => "Implement delattr(self, name).", + "builtins.TabError.__eq__" => "Return self==value.", + "builtins.TabError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.TabError.__ge__" => "Return self>=value.", + "builtins.TabError.__getattribute__" => "Return getattr(self, name).", + "builtins.TabError.__getstate__" => "Helper for pickle.", + "builtins.TabError.__gt__" => "Return self>value.", + "builtins.TabError.__hash__" => "Return hash(self).", + "builtins.TabError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.TabError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.TabError.__le__" => "Return self<=value.", + "builtins.TabError.__lt__" => "Return self "Return self!=value.", + "builtins.TabError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.TabError.__reduce_ex__" => "Helper for pickle.", + "builtins.TabError.__repr__" => "Return repr(self).", + "builtins.TabError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.TabError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.TabError.__str__" => "Return str(self).", + "builtins.TabError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.TabError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.TabError.end_lineno" => "exception end lineno", + "builtins.TabError.end_offset" => "exception end offset", + "builtins.TabError.filename" => "exception filename", + "builtins.TabError.lineno" => "exception lineno", + "builtins.TabError.msg" => "exception msg", + "builtins.TabError.offset" => "exception offset", + "builtins.TabError.print_file_and_line" => "exception print_file_and_line", + "builtins.TabError.text" => "exception text", + "builtins.TabError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.TimeoutError" => "Timeout expired.", + "builtins.TimeoutError.__cause__" => "exception cause", + "builtins.TimeoutError.__context__" => "exception context", + "builtins.TimeoutError.__delattr__" => "Implement delattr(self, name).", + "builtins.TimeoutError.__eq__" => "Return self==value.", + "builtins.TimeoutError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.TimeoutError.__ge__" => "Return self>=value.", + "builtins.TimeoutError.__getattribute__" => "Return getattr(self, name).", + "builtins.TimeoutError.__getstate__" => "Helper for pickle.", + "builtins.TimeoutError.__gt__" => "Return self>value.", + "builtins.TimeoutError.__hash__" => "Return hash(self).", + "builtins.TimeoutError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.TimeoutError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.TimeoutError.__le__" => "Return self<=value.", + "builtins.TimeoutError.__lt__" => "Return self "Return self!=value.", + "builtins.TimeoutError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.TimeoutError.__reduce_ex__" => "Helper for pickle.", + "builtins.TimeoutError.__repr__" => "Return repr(self).", + "builtins.TimeoutError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.TimeoutError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.TimeoutError.__str__" => "Return str(self).", + "builtins.TimeoutError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.TimeoutError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.TimeoutError.errno" => "POSIX exception code", + "builtins.TimeoutError.filename" => "exception filename", + "builtins.TimeoutError.filename2" => "second exception filename", + "builtins.TimeoutError.strerror" => "exception strerror", + "builtins.TimeoutError.winerror" => "Win32 exception code", + "builtins.TimeoutError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.TypeError" => "Inappropriate argument type.", + "builtins.TypeError.__cause__" => "exception cause", + "builtins.TypeError.__context__" => "exception context", + "builtins.TypeError.__delattr__" => "Implement delattr(self, name).", + "builtins.TypeError.__eq__" => "Return self==value.", + "builtins.TypeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.TypeError.__ge__" => "Return self>=value.", + "builtins.TypeError.__getattribute__" => "Return getattr(self, name).", + "builtins.TypeError.__getstate__" => "Helper for pickle.", + "builtins.TypeError.__gt__" => "Return self>value.", + "builtins.TypeError.__hash__" => "Return hash(self).", + "builtins.TypeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.TypeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.TypeError.__le__" => "Return self<=value.", + "builtins.TypeError.__lt__" => "Return self "Return self!=value.", + "builtins.TypeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.TypeError.__reduce_ex__" => "Helper for pickle.", + "builtins.TypeError.__repr__" => "Return repr(self).", + "builtins.TypeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.TypeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.TypeError.__str__" => "Return str(self).", + "builtins.TypeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.TypeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.TypeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnboundLocalError" => "Local name referenced but not bound to a value.", + "builtins.UnboundLocalError.__cause__" => "exception cause", + "builtins.UnboundLocalError.__context__" => "exception context", + "builtins.UnboundLocalError.__delattr__" => "Implement delattr(self, name).", + "builtins.UnboundLocalError.__eq__" => "Return self==value.", + "builtins.UnboundLocalError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnboundLocalError.__ge__" => "Return self>=value.", + "builtins.UnboundLocalError.__getattribute__" => "Return getattr(self, name).", + "builtins.UnboundLocalError.__getstate__" => "Helper for pickle.", + "builtins.UnboundLocalError.__gt__" => "Return self>value.", + "builtins.UnboundLocalError.__hash__" => "Return hash(self).", + "builtins.UnboundLocalError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnboundLocalError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnboundLocalError.__le__" => "Return self<=value.", + "builtins.UnboundLocalError.__lt__" => "Return self "Return self!=value.", + "builtins.UnboundLocalError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnboundLocalError.__reduce_ex__" => "Helper for pickle.", + "builtins.UnboundLocalError.__repr__" => "Return repr(self).", + "builtins.UnboundLocalError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnboundLocalError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnboundLocalError.__str__" => "Return str(self).", + "builtins.UnboundLocalError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnboundLocalError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnboundLocalError.name" => "name", + "builtins.UnboundLocalError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnicodeDecodeError" => "Unicode decoding error.", + "builtins.UnicodeDecodeError.__cause__" => "exception cause", + "builtins.UnicodeDecodeError.__context__" => "exception context", + "builtins.UnicodeDecodeError.__delattr__" => "Implement delattr(self, name).", + "builtins.UnicodeDecodeError.__eq__" => "Return self==value.", + "builtins.UnicodeDecodeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnicodeDecodeError.__ge__" => "Return self>=value.", + "builtins.UnicodeDecodeError.__getattribute__" => "Return getattr(self, name).", + "builtins.UnicodeDecodeError.__getstate__" => "Helper for pickle.", + "builtins.UnicodeDecodeError.__gt__" => "Return self>value.", + "builtins.UnicodeDecodeError.__hash__" => "Return hash(self).", + "builtins.UnicodeDecodeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnicodeDecodeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnicodeDecodeError.__le__" => "Return self<=value.", + "builtins.UnicodeDecodeError.__lt__" => "Return self "Return self!=value.", + "builtins.UnicodeDecodeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnicodeDecodeError.__reduce_ex__" => "Helper for pickle.", + "builtins.UnicodeDecodeError.__repr__" => "Return repr(self).", + "builtins.UnicodeDecodeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnicodeDecodeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnicodeDecodeError.__str__" => "Return str(self).", + "builtins.UnicodeDecodeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnicodeDecodeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnicodeDecodeError.encoding" => "exception encoding", + "builtins.UnicodeDecodeError.end" => "exception end", + "builtins.UnicodeDecodeError.object" => "exception object", + "builtins.UnicodeDecodeError.reason" => "exception reason", + "builtins.UnicodeDecodeError.start" => "exception start", + "builtins.UnicodeDecodeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnicodeEncodeError" => "Unicode encoding error.", + "builtins.UnicodeEncodeError.__cause__" => "exception cause", + "builtins.UnicodeEncodeError.__context__" => "exception context", + "builtins.UnicodeEncodeError.__delattr__" => "Implement delattr(self, name).", + "builtins.UnicodeEncodeError.__eq__" => "Return self==value.", + "builtins.UnicodeEncodeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnicodeEncodeError.__ge__" => "Return self>=value.", + "builtins.UnicodeEncodeError.__getattribute__" => "Return getattr(self, name).", + "builtins.UnicodeEncodeError.__getstate__" => "Helper for pickle.", + "builtins.UnicodeEncodeError.__gt__" => "Return self>value.", + "builtins.UnicodeEncodeError.__hash__" => "Return hash(self).", + "builtins.UnicodeEncodeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnicodeEncodeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnicodeEncodeError.__le__" => "Return self<=value.", + "builtins.UnicodeEncodeError.__lt__" => "Return self "Return self!=value.", + "builtins.UnicodeEncodeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnicodeEncodeError.__reduce_ex__" => "Helper for pickle.", + "builtins.UnicodeEncodeError.__repr__" => "Return repr(self).", + "builtins.UnicodeEncodeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnicodeEncodeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnicodeEncodeError.__str__" => "Return str(self).", + "builtins.UnicodeEncodeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnicodeEncodeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnicodeEncodeError.encoding" => "exception encoding", + "builtins.UnicodeEncodeError.end" => "exception end", + "builtins.UnicodeEncodeError.object" => "exception object", + "builtins.UnicodeEncodeError.reason" => "exception reason", + "builtins.UnicodeEncodeError.start" => "exception start", + "builtins.UnicodeEncodeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnicodeError" => "Unicode related error.", + "builtins.UnicodeError.__cause__" => "exception cause", + "builtins.UnicodeError.__context__" => "exception context", + "builtins.UnicodeError.__delattr__" => "Implement delattr(self, name).", + "builtins.UnicodeError.__eq__" => "Return self==value.", + "builtins.UnicodeError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnicodeError.__ge__" => "Return self>=value.", + "builtins.UnicodeError.__getattribute__" => "Return getattr(self, name).", + "builtins.UnicodeError.__getstate__" => "Helper for pickle.", + "builtins.UnicodeError.__gt__" => "Return self>value.", + "builtins.UnicodeError.__hash__" => "Return hash(self).", + "builtins.UnicodeError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnicodeError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnicodeError.__le__" => "Return self<=value.", + "builtins.UnicodeError.__lt__" => "Return self "Return self!=value.", + "builtins.UnicodeError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnicodeError.__reduce_ex__" => "Helper for pickle.", + "builtins.UnicodeError.__repr__" => "Return repr(self).", + "builtins.UnicodeError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnicodeError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnicodeError.__str__" => "Return str(self).", + "builtins.UnicodeError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnicodeError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnicodeError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnicodeTranslateError" => "Unicode translation error.", + "builtins.UnicodeTranslateError.__cause__" => "exception cause", + "builtins.UnicodeTranslateError.__context__" => "exception context", + "builtins.UnicodeTranslateError.__delattr__" => "Implement delattr(self, name).", + "builtins.UnicodeTranslateError.__eq__" => "Return self==value.", + "builtins.UnicodeTranslateError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnicodeTranslateError.__ge__" => "Return self>=value.", + "builtins.UnicodeTranslateError.__getattribute__" => "Return getattr(self, name).", + "builtins.UnicodeTranslateError.__getstate__" => "Helper for pickle.", + "builtins.UnicodeTranslateError.__gt__" => "Return self>value.", + "builtins.UnicodeTranslateError.__hash__" => "Return hash(self).", + "builtins.UnicodeTranslateError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnicodeTranslateError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnicodeTranslateError.__le__" => "Return self<=value.", + "builtins.UnicodeTranslateError.__lt__" => "Return self "Return self!=value.", + "builtins.UnicodeTranslateError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnicodeTranslateError.__reduce_ex__" => "Helper for pickle.", + "builtins.UnicodeTranslateError.__repr__" => "Return repr(self).", + "builtins.UnicodeTranslateError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnicodeTranslateError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnicodeTranslateError.__str__" => "Return str(self).", + "builtins.UnicodeTranslateError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnicodeTranslateError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnicodeTranslateError.encoding" => "exception encoding", + "builtins.UnicodeTranslateError.end" => "exception end", + "builtins.UnicodeTranslateError.object" => "exception object", + "builtins.UnicodeTranslateError.reason" => "exception reason", + "builtins.UnicodeTranslateError.start" => "exception start", + "builtins.UnicodeTranslateError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UnicodeWarning" => "Base class for warnings about Unicode related problems, mostly\nrelated to conversion problems.", + "builtins.UnicodeWarning.__cause__" => "exception cause", + "builtins.UnicodeWarning.__context__" => "exception context", + "builtins.UnicodeWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.UnicodeWarning.__eq__" => "Return self==value.", + "builtins.UnicodeWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UnicodeWarning.__ge__" => "Return self>=value.", + "builtins.UnicodeWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.UnicodeWarning.__getstate__" => "Helper for pickle.", + "builtins.UnicodeWarning.__gt__" => "Return self>value.", + "builtins.UnicodeWarning.__hash__" => "Return hash(self).", + "builtins.UnicodeWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UnicodeWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UnicodeWarning.__le__" => "Return self<=value.", + "builtins.UnicodeWarning.__lt__" => "Return self "Return self!=value.", + "builtins.UnicodeWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UnicodeWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.UnicodeWarning.__repr__" => "Return repr(self).", + "builtins.UnicodeWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UnicodeWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UnicodeWarning.__str__" => "Return str(self).", + "builtins.UnicodeWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UnicodeWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UnicodeWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.UserWarning" => "Base class for warnings generated by user code.", + "builtins.UserWarning.__cause__" => "exception cause", + "builtins.UserWarning.__context__" => "exception context", + "builtins.UserWarning.__delattr__" => "Implement delattr(self, name).", + "builtins.UserWarning.__eq__" => "Return self==value.", + "builtins.UserWarning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.UserWarning.__ge__" => "Return self>=value.", + "builtins.UserWarning.__getattribute__" => "Return getattr(self, name).", + "builtins.UserWarning.__getstate__" => "Helper for pickle.", + "builtins.UserWarning.__gt__" => "Return self>value.", + "builtins.UserWarning.__hash__" => "Return hash(self).", + "builtins.UserWarning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.UserWarning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.UserWarning.__le__" => "Return self<=value.", + "builtins.UserWarning.__lt__" => "Return self "Return self!=value.", + "builtins.UserWarning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.UserWarning.__reduce_ex__" => "Helper for pickle.", + "builtins.UserWarning.__repr__" => "Return repr(self).", + "builtins.UserWarning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.UserWarning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.UserWarning.__str__" => "Return str(self).", + "builtins.UserWarning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.UserWarning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.UserWarning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ValueError" => "Inappropriate argument value (of correct type).", + "builtins.ValueError.__cause__" => "exception cause", + "builtins.ValueError.__context__" => "exception context", + "builtins.ValueError.__delattr__" => "Implement delattr(self, name).", + "builtins.ValueError.__eq__" => "Return self==value.", + "builtins.ValueError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ValueError.__ge__" => "Return self>=value.", + "builtins.ValueError.__getattribute__" => "Return getattr(self, name).", + "builtins.ValueError.__getstate__" => "Helper for pickle.", + "builtins.ValueError.__gt__" => "Return self>value.", + "builtins.ValueError.__hash__" => "Return hash(self).", + "builtins.ValueError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ValueError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ValueError.__le__" => "Return self<=value.", + "builtins.ValueError.__lt__" => "Return self "Return self!=value.", + "builtins.ValueError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ValueError.__reduce_ex__" => "Helper for pickle.", + "builtins.ValueError.__repr__" => "Return repr(self).", + "builtins.ValueError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ValueError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ValueError.__str__" => "Return str(self).", + "builtins.ValueError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ValueError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ValueError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.Warning" => "Base class for warning categories.", + "builtins.Warning.__cause__" => "exception cause", + "builtins.Warning.__context__" => "exception context", + "builtins.Warning.__delattr__" => "Implement delattr(self, name).", + "builtins.Warning.__eq__" => "Return self==value.", + "builtins.Warning.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.Warning.__ge__" => "Return self>=value.", + "builtins.Warning.__getattribute__" => "Return getattr(self, name).", + "builtins.Warning.__getstate__" => "Helper for pickle.", + "builtins.Warning.__gt__" => "Return self>value.", + "builtins.Warning.__hash__" => "Return hash(self).", + "builtins.Warning.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.Warning.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.Warning.__le__" => "Return self<=value.", + "builtins.Warning.__lt__" => "Return self "Return self!=value.", + "builtins.Warning.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.Warning.__reduce_ex__" => "Helper for pickle.", + "builtins.Warning.__repr__" => "Return repr(self).", + "builtins.Warning.__setattr__" => "Implement setattr(self, name, value).", + "builtins.Warning.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.Warning.__str__" => "Return str(self).", + "builtins.Warning.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.Warning.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.Warning.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.WindowsError" => "Base class for I/O related errors.", + "builtins.WindowsError.__cause__" => "exception cause", + "builtins.WindowsError.__context__" => "exception context", + "builtins.WindowsError.__delattr__" => "Implement delattr(self, name).", + "builtins.WindowsError.__eq__" => "Return self==value.", + "builtins.WindowsError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.WindowsError.__ge__" => "Return self>=value.", + "builtins.WindowsError.__getattribute__" => "Return getattr(self, name).", + "builtins.WindowsError.__getstate__" => "Helper for pickle.", + "builtins.WindowsError.__gt__" => "Return self>value.", + "builtins.WindowsError.__hash__" => "Return hash(self).", + "builtins.WindowsError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.WindowsError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.WindowsError.__le__" => "Return self<=value.", + "builtins.WindowsError.__lt__" => "Return self "Return self!=value.", + "builtins.WindowsError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.WindowsError.__reduce_ex__" => "Helper for pickle.", + "builtins.WindowsError.__repr__" => "Return repr(self).", + "builtins.WindowsError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.WindowsError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.WindowsError.__str__" => "Return str(self).", + "builtins.WindowsError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.WindowsError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.WindowsError.errno" => "POSIX exception code", + "builtins.WindowsError.filename" => "exception filename", + "builtins.WindowsError.filename2" => "second exception filename", + "builtins.WindowsError.strerror" => "exception strerror", + "builtins.WindowsError.winerror" => "Win32 exception code", + "builtins.WindowsError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.ZeroDivisionError" => "Second argument to a division or modulo operation was zero.", + "builtins.ZeroDivisionError.__cause__" => "exception cause", + "builtins.ZeroDivisionError.__context__" => "exception context", + "builtins.ZeroDivisionError.__delattr__" => "Implement delattr(self, name).", + "builtins.ZeroDivisionError.__eq__" => "Return self==value.", + "builtins.ZeroDivisionError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.ZeroDivisionError.__ge__" => "Return self>=value.", + "builtins.ZeroDivisionError.__getattribute__" => "Return getattr(self, name).", + "builtins.ZeroDivisionError.__getstate__" => "Helper for pickle.", + "builtins.ZeroDivisionError.__gt__" => "Return self>value.", + "builtins.ZeroDivisionError.__hash__" => "Return hash(self).", + "builtins.ZeroDivisionError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.ZeroDivisionError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.ZeroDivisionError.__le__" => "Return self<=value.", + "builtins.ZeroDivisionError.__lt__" => "Return self "Return self!=value.", + "builtins.ZeroDivisionError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.ZeroDivisionError.__reduce_ex__" => "Helper for pickle.", + "builtins.ZeroDivisionError.__repr__" => "Return repr(self).", + "builtins.ZeroDivisionError.__setattr__" => "Implement setattr(self, name, value).", + "builtins.ZeroDivisionError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.ZeroDivisionError.__str__" => "Return str(self).", + "builtins.ZeroDivisionError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.ZeroDivisionError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins.ZeroDivisionError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins._IncompleteInputError" => "incomplete input.", + "builtins._IncompleteInputError.__cause__" => "exception cause", + "builtins._IncompleteInputError.__context__" => "exception context", + "builtins._IncompleteInputError.__delattr__" => "Implement delattr(self, name).", + "builtins._IncompleteInputError.__eq__" => "Return self==value.", + "builtins._IncompleteInputError.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins._IncompleteInputError.__ge__" => "Return self>=value.", + "builtins._IncompleteInputError.__getattribute__" => "Return getattr(self, name).", + "builtins._IncompleteInputError.__getstate__" => "Helper for pickle.", + "builtins._IncompleteInputError.__gt__" => "Return self>value.", + "builtins._IncompleteInputError.__hash__" => "Return hash(self).", + "builtins._IncompleteInputError.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins._IncompleteInputError.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins._IncompleteInputError.__le__" => "Return self<=value.", + "builtins._IncompleteInputError.__lt__" => "Return self "Return self!=value.", + "builtins._IncompleteInputError.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins._IncompleteInputError.__reduce_ex__" => "Helper for pickle.", + "builtins._IncompleteInputError.__repr__" => "Return repr(self).", + "builtins._IncompleteInputError.__setattr__" => "Implement setattr(self, name, value).", + "builtins._IncompleteInputError.__sizeof__" => "Size of object in memory, in bytes.", + "builtins._IncompleteInputError.__str__" => "Return str(self).", + "builtins._IncompleteInputError.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins._IncompleteInputError.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "builtins._IncompleteInputError.end_lineno" => "exception end lineno", + "builtins._IncompleteInputError.end_offset" => "exception end offset", + "builtins._IncompleteInputError.filename" => "exception filename", + "builtins._IncompleteInputError.lineno" => "exception lineno", + "builtins._IncompleteInputError.msg" => "exception msg", + "builtins._IncompleteInputError.offset" => "exception offset", + "builtins._IncompleteInputError.print_file_and_line" => "exception print_file_and_line", + "builtins._IncompleteInputError.text" => "exception text", + "builtins._IncompleteInputError.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "builtins.__build_class__" => "__build_class__(func, name, /, *bases, [metaclass], **kwds) -> class\n\nInternal helper function used by the class statement.", + "builtins.__import__" => "Import a module.\n\nBecause this function is meant for use by the Python\ninterpreter and not for general use, it is better to use\nimportlib.import_module() to programmatically import a module.\n\nThe globals argument is only used to determine the context;\nthey are not modified. The locals argument is unused. The fromlist\nshould be a list of names to emulate ``from name import ...``, or an\nempty list to emulate ``import name``.\nWhen importing a module from a package, note that __import__('A.B', ...)\nreturns package A when fromlist is empty, but its submodule B when\nfromlist is not empty. The level argument is used to determine whether to\nperform absolute or relative imports: 0 is absolute, while a positive number\nis the number of parent directories to search relative to the current module.", + "builtins.abs" => "Return the absolute value of the argument.", + "builtins.aiter" => "Return an AsyncIterator for an AsyncIterable object.", + "builtins.all" => "Return True if bool(x) is True for all values x in the iterable.\n\nIf the iterable is empty, return True.", + "builtins.anext" => "Return the next item from the async iterator.\n\nIf default is given and the async iterator is exhausted,\nit is returned instead of raising StopAsyncIteration.", + "builtins.any" => "Return True if bool(x) is True for any x in the iterable.\n\nIf the iterable is empty, return False.", + "builtins.ascii" => "Return an ASCII-only representation of an object.\n\nAs repr(), return a string containing a printable representation of an\nobject, but escape the non-ASCII characters in the string returned by\nrepr() using \\\\x, \\\\u or \\\\U escapes. This generates a string similar\nto that returned by repr() in Python 2.", + "builtins.bin" => "Return the binary representation of an integer.\n\n>>> bin(2796202)\n'0b1010101010101010101010'", + "builtins.bool" => "Returns True when the argument is true, False otherwise.\nThe builtins True and False are the only two instances of the class bool.\nThe class bool is a subclass of the class int, and cannot be subclassed.", + "builtins.bool.__abs__" => "abs(self)", + "builtins.bool.__add__" => "Return self+value.", + "builtins.bool.__and__" => "Return self&value.", + "builtins.bool.__bool__" => "True if self else False", + "builtins.bool.__ceil__" => "Ceiling of an Integral returns itself.", + "builtins.bool.__delattr__" => "Implement delattr(self, name).", + "builtins.bool.__divmod__" => "Return divmod(self, value).", + "builtins.bool.__eq__" => "Return self==value.", + "builtins.bool.__float__" => "float(self)", + "builtins.bool.__floor__" => "Flooring an Integral returns itself.", + "builtins.bool.__floordiv__" => "Return self//value.", + "builtins.bool.__format__" => "Convert to a string according to format_spec.", + "builtins.bool.__ge__" => "Return self>=value.", + "builtins.bool.__getattribute__" => "Return getattr(self, name).", + "builtins.bool.__getstate__" => "Helper for pickle.", + "builtins.bool.__gt__" => "Return self>value.", + "builtins.bool.__hash__" => "Return hash(self).", + "builtins.bool.__index__" => "Return self converted to an integer, if self is suitable for use as an index into a list.", + "builtins.bool.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.bool.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.bool.__int__" => "int(self)", + "builtins.bool.__invert__" => "~self", + "builtins.bool.__le__" => "Return self<=value.", + "builtins.bool.__lshift__" => "Return self< "Return self "Return self%value.", + "builtins.bool.__mul__" => "Return self*value.", + "builtins.bool.__ne__" => "Return self!=value.", + "builtins.bool.__neg__" => "-self", + "builtins.bool.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.bool.__or__" => "Return self|value.", + "builtins.bool.__pos__" => "+self", + "builtins.bool.__pow__" => "Return pow(self, value, mod).", + "builtins.bool.__radd__" => "Return value+self.", + "builtins.bool.__rand__" => "Return value&self.", + "builtins.bool.__rdivmod__" => "Return divmod(value, self).", + "builtins.bool.__reduce__" => "Helper for pickle.", + "builtins.bool.__reduce_ex__" => "Helper for pickle.", + "builtins.bool.__repr__" => "Return repr(self).", + "builtins.bool.__rfloordiv__" => "Return value//self.", + "builtins.bool.__rlshift__" => "Return value< "Return value%self.", + "builtins.bool.__rmul__" => "Return value*self.", + "builtins.bool.__ror__" => "Return value|self.", + "builtins.bool.__round__" => "Rounding an Integral returns itself.\n\nRounding with an ndigits argument also returns an integer.", + "builtins.bool.__rpow__" => "Return pow(value, self, mod).", + "builtins.bool.__rrshift__" => "Return value>>self.", + "builtins.bool.__rshift__" => "Return self>>value.", + "builtins.bool.__rsub__" => "Return value-self.", + "builtins.bool.__rtruediv__" => "Return value/self.", + "builtins.bool.__rxor__" => "Return value^self.", + "builtins.bool.__setattr__" => "Implement setattr(self, name, value).", + "builtins.bool.__sizeof__" => "Returns size in memory, in bytes.", + "builtins.bool.__str__" => "Return str(self).", + "builtins.bool.__sub__" => "Return self-value.", + "builtins.bool.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.bool.__truediv__" => "Return self/value.", + "builtins.bool.__trunc__" => "Truncating an Integral returns itself.", + "builtins.bool.__xor__" => "Return self^value.", + "builtins.bool.as_integer_ratio" => "Return a pair of integers, whose ratio is equal to the original int.\n\nThe ratio is in lowest terms and has a positive denominator.\n\n>>> (10).as_integer_ratio()\n(10, 1)\n>>> (-10).as_integer_ratio()\n(-10, 1)\n>>> (0).as_integer_ratio()\n(0, 1)", + "builtins.bool.bit_count" => "Number of ones in the binary representation of the absolute value of self.\n\nAlso known as the population count.\n\n>>> bin(13)\n'0b1101'\n>>> (13).bit_count()\n3", + "builtins.bool.bit_length" => "Number of bits necessary to represent self in binary.\n\n>>> bin(37)\n'0b100101'\n>>> (37).bit_length()\n6", + "builtins.bool.conjugate" => "Returns self, the complex conjugate of any int.", + "builtins.bool.denominator" => "the denominator of a rational number in lowest terms", + "builtins.bool.from_bytes" => "Return the integer represented by the given array of bytes.\n\nbytes\n Holds the array of bytes to convert. The argument must either\n support the buffer protocol or be an iterable object producing bytes.\n Bytes and bytearray are examples of built-in objects that support the\n buffer protocol.\nbyteorder\n The byte order used to represent the integer. If byteorder is 'big',\n the most significant byte is at the beginning of the byte array. If\n byteorder is 'little', the most significant byte is at the end of the\n byte array. To request the native byte order of the host system, use\n sys.byteorder as the byte order value. Default is to use 'big'.\nsigned\n Indicates whether two's complement is used to represent the integer.", + "builtins.bool.imag" => "the imaginary part of a complex number", + "builtins.bool.is_integer" => "Returns True. Exists for duck type compatibility with float.is_integer.", + "builtins.bool.numerator" => "the numerator of a rational number in lowest terms", + "builtins.bool.real" => "the real part of a complex number", + "builtins.bool.to_bytes" => "Return an array of bytes representing an integer.\n\nlength\n Length of bytes object to use. An OverflowError is raised if the\n integer is not representable with the given number of bytes. Default\n is length 1.\nbyteorder\n The byte order used to represent the integer. If byteorder is 'big',\n the most significant byte is at the beginning of the byte array. If\n byteorder is 'little', the most significant byte is at the end of the\n byte array. To request the native byte order of the host system, use\n sys.byteorder as the byte order value. Default is to use 'big'.\nsigned\n Determines whether two's complement is used to represent the integer.\n If signed is False and a negative integer is given, an OverflowError\n is raised.", + "builtins.breakpoint" => "Call sys.breakpointhook(*args, **kws). sys.breakpointhook() must accept\nwhatever arguments are passed.\n\nBy default, this drops you into the pdb debugger.", + "builtins.bytearray" => "bytearray(iterable_of_ints) -> bytearray\nbytearray(string, encoding[, errors]) -> bytearray\nbytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\nbytearray(int) -> bytes array of size given by the parameter initialized with null bytes\nbytearray() -> empty bytes array\n\nConstruct a mutable bytearray object from:\n - an iterable yielding integers in range(256)\n - a text string encoded using the specified encoding\n - a bytes or a buffer object\n - any object implementing the buffer API.\n - an integer", + "builtins.bytearray.__add__" => "Return self+value.", + "builtins.bytearray.__alloc__" => "B.__alloc__() -> int\n\nReturn the number of bytes actually allocated.", + "builtins.bytearray.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "builtins.bytearray.__contains__" => "Return bool(key in self).", + "builtins.bytearray.__delattr__" => "Implement delattr(self, name).", + "builtins.bytearray.__delitem__" => "Delete self[key].", + "builtins.bytearray.__eq__" => "Return self==value.", + "builtins.bytearray.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.bytearray.__ge__" => "Return self>=value.", + "builtins.bytearray.__getattribute__" => "Return getattr(self, name).", + "builtins.bytearray.__getitem__" => "Return self[key].", + "builtins.bytearray.__getstate__" => "Helper for pickle.", + "builtins.bytearray.__gt__" => "Return self>value.", + "builtins.bytearray.__iadd__" => "Implement self+=value.", + "builtins.bytearray.__imul__" => "Implement self*=value.", + "builtins.bytearray.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.bytearray.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.bytearray.__iter__" => "Implement iter(self).", + "builtins.bytearray.__le__" => "Return self<=value.", + "builtins.bytearray.__len__" => "Return len(self).", + "builtins.bytearray.__lt__" => "Return self "Return self%value.", + "builtins.bytearray.__mul__" => "Return self*value.", + "builtins.bytearray.__ne__" => "Return self!=value.", + "builtins.bytearray.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.bytearray.__reduce__" => "Return state information for pickling.", + "builtins.bytearray.__reduce_ex__" => "Return state information for pickling.", + "builtins.bytearray.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "builtins.bytearray.__repr__" => "Return repr(self).", + "builtins.bytearray.__rmod__" => "Return value%self.", + "builtins.bytearray.__rmul__" => "Return value*self.", + "builtins.bytearray.__setattr__" => "Implement setattr(self, name, value).", + "builtins.bytearray.__setitem__" => "Set self[key] to value.", + "builtins.bytearray.__sizeof__" => "Returns the size of the bytearray object in memory, in bytes.", + "builtins.bytearray.__str__" => "Return str(self).", + "builtins.bytearray.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.bytearray.append" => "Append a single item to the end of the bytearray.\n\nitem\n The item to be appended.", + "builtins.bytearray.capitalize" => "B.capitalize() -> copy of B\n\nReturn a copy of B with only its first character capitalized (ASCII)\nand the rest lower-cased.", + "builtins.bytearray.center" => "Return a centered string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytearray.clear" => "Remove all items from the bytearray.", + "builtins.bytearray.copy" => "Return a copy of B.", + "builtins.bytearray.count" => "Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[start:end].\n\nstart\n Optional start position. Default: start of the bytes.\nend\n Optional stop position. Default: end of the bytes.", + "builtins.bytearray.decode" => "Decode the bytearray using the codec registered for encoding.\n\nencoding\n The encoding with which to decode the bytearray.\nerrors\n The error handling scheme to use for the handling of decoding errors.\n The default is 'strict' meaning that decoding errors raise a\n UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n as well as any other name registered with codecs.register_error that\n can handle UnicodeDecodeErrors.", + "builtins.bytearray.endswith" => "Return True if the bytearray ends with the specified suffix, False otherwise.\n\nsuffix\n A bytes or a tuple of bytes to try.\nstart\n Optional start position. Default: start of the bytearray.\nend\n Optional stop position. Default: end of the bytearray.", + "builtins.bytearray.expandtabs" => "Return a copy where all tab characters are expanded using spaces.\n\nIf tabsize is not given, a tab size of 8 characters is assumed.", + "builtins.bytearray.extend" => "Append all the items from the iterator or sequence to the end of the bytearray.\n\niterable_of_ints\n The iterable of items to append.", + "builtins.bytearray.find" => "Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nReturn -1 on failure.", + "builtins.bytearray.fromhex" => "Create a bytearray object from a string of hexadecimal numbers.\n\nSpaces between two numbers are accepted.\nExample: bytearray.fromhex('B9 01EF') -> bytearray(b'\\\\xb9\\\\x01\\\\xef')", + "builtins.bytearray.hex" => "Create a string of hexadecimal numbers from a bytearray object.\n\n sep\n An optional single character or byte to separate hex bytes.\n bytes_per_sep\n How many bytes between separators. Positive values count from the\n right, negative values count from the left.\n\nExample:\n>>> value = bytearray([0xb9, 0x01, 0xef])\n>>> value.hex()\n'b901ef'\n>>> value.hex(':')\n'b9:01:ef'\n>>> value.hex(':', 2)\n'b9:01ef'\n>>> value.hex(':', -2)\n'b901:ef'", + "builtins.bytearray.index" => "Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nRaise ValueError if the subsection is not found.", + "builtins.bytearray.insert" => "Insert a single item into the bytearray before the given index.\n\nindex\n The index where the value is to be inserted.\nitem\n The item to be inserted.", + "builtins.bytearray.isalnum" => "B.isalnum() -> bool\n\nReturn True if all characters in B are alphanumeric\nand there is at least one character in B, False otherwise.", + "builtins.bytearray.isalpha" => "B.isalpha() -> bool\n\nReturn True if all characters in B are alphabetic\nand there is at least one character in B, False otherwise.", + "builtins.bytearray.isascii" => "B.isascii() -> bool\n\nReturn True if B is empty or all characters in B are ASCII,\nFalse otherwise.", + "builtins.bytearray.isdigit" => "B.isdigit() -> bool\n\nReturn True if all characters in B are digits\nand there is at least one character in B, False otherwise.", + "builtins.bytearray.islower" => "B.islower() -> bool\n\nReturn True if all cased characters in B are lowercase and there is\nat least one cased character in B, False otherwise.", + "builtins.bytearray.isspace" => "B.isspace() -> bool\n\nReturn True if all characters in B are whitespace\nand there is at least one character in B, False otherwise.", + "builtins.bytearray.istitle" => "B.istitle() -> bool\n\nReturn True if B is a titlecased string and there is at least one\ncharacter in B, i.e. uppercase characters may only follow uncased\ncharacters and lowercase characters only cased ones. Return False\notherwise.", + "builtins.bytearray.isupper" => "B.isupper() -> bool\n\nReturn True if all cased characters in B are uppercase and there is\nat least one cased character in B, False otherwise.", + "builtins.bytearray.join" => "Concatenate any number of bytes/bytearray objects.\n\nThe bytearray whose method is called is inserted in between each pair.\n\nThe result is returned as a new bytearray object.", + "builtins.bytearray.ljust" => "Return a left-justified string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytearray.lower" => "B.lower() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to lowercase.", + "builtins.bytearray.lstrip" => "Strip leading bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading ASCII whitespace.", + "builtins.bytearray.maketrans" => "Return a translation table usable for the bytes or bytearray translate method.\n\nThe returned table will be one where each byte in frm is mapped to the byte at\nthe same position in to.\n\nThe bytes objects frm and to must be of the same length.", + "builtins.bytearray.partition" => "Partition the bytearray into three parts using the given separator.\n\nThis will search for the separator sep in the bytearray. If the separator is\nfound, returns a 3-tuple containing the part before the separator, the\nseparator itself, and the part after it as new bytearray objects.\n\nIf the separator is not found, returns a 3-tuple containing the copy of the\noriginal bytearray object and two empty bytearray objects.", + "builtins.bytearray.pop" => "Remove and return a single item from B.\n\n index\n The index from where to remove the item.\n -1 (the default value) means remove the last item.\n\nIf no index argument is given, will pop the last item.", + "builtins.bytearray.remove" => "Remove the first occurrence of a value in the bytearray.\n\nvalue\n The value to remove.", + "builtins.bytearray.removeprefix" => "Return a bytearray with the given prefix string removed if present.\n\nIf the bytearray starts with the prefix string, return\nbytearray[len(prefix):]. Otherwise, return a copy of the original\nbytearray.", + "builtins.bytearray.removesuffix" => "Return a bytearray with the given suffix string removed if present.\n\nIf the bytearray ends with the suffix string and that suffix is not\nempty, return bytearray[:-len(suffix)]. Otherwise, return a copy of\nthe original bytearray.", + "builtins.bytearray.replace" => "Return a copy with all occurrences of substring old replaced by new.\n\n count\n Maximum number of occurrences to replace.\n -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.", + "builtins.bytearray.reverse" => "Reverse the order of the values in B in place.", + "builtins.bytearray.rfind" => "Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nReturn -1 on failure.", + "builtins.bytearray.rindex" => "Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nRaise ValueError if the subsection is not found.", + "builtins.bytearray.rjust" => "Return a right-justified string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytearray.rpartition" => "Partition the bytearray into three parts using the given separator.\n\nThis will search for the separator sep in the bytearray, starting at the end.\nIf the separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it as new bytearray\nobjects.\n\nIf the separator is not found, returns a 3-tuple containing two empty bytearray\nobjects and the copy of the original bytearray object.", + "builtins.bytearray.rsplit" => "Return a list of the sections in the bytearray, using sep as the delimiter.\n\n sep\n The delimiter according which to split the bytearray.\n None (the default value) means split on ASCII whitespace characters\n (space, tab, return, newline, formfeed, vertical tab).\n maxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.\n\nSplitting is done starting at the end of the bytearray and working to the front.", + "builtins.bytearray.rstrip" => "Strip trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip trailing ASCII whitespace.", + "builtins.bytearray.split" => "Return a list of the sections in the bytearray, using sep as the delimiter.\n\nsep\n The delimiter according which to split the bytearray.\n None (the default value) means split on ASCII whitespace characters\n (space, tab, return, newline, formfeed, vertical tab).\nmaxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.", + "builtins.bytearray.splitlines" => "Return a list of the lines in the bytearray, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.", + "builtins.bytearray.startswith" => "Return True if the bytearray starts with the specified prefix, False otherwise.\n\nprefix\n A bytes or a tuple of bytes to try.\nstart\n Optional start position. Default: start of the bytearray.\nend\n Optional stop position. Default: end of the bytearray.", + "builtins.bytearray.strip" => "Strip leading and trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading and trailing ASCII whitespace.", + "builtins.bytearray.swapcase" => "B.swapcase() -> copy of B\n\nReturn a copy of B with uppercase ASCII characters converted\nto lowercase ASCII and vice versa.", + "builtins.bytearray.title" => "B.title() -> copy of B\n\nReturn a titlecased version of B, i.e. ASCII words start with uppercase\ncharacters, all remaining cased characters have lowercase.", + "builtins.bytearray.translate" => "Return a copy with each character mapped by the given translation table.\n\n table\n Translation table, which must be a bytes object of length 256.\n\nAll characters occurring in the optional argument delete are removed.\nThe remaining characters are mapped through the given translation table.", + "builtins.bytearray.upper" => "B.upper() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to uppercase.", + "builtins.bytearray.zfill" => "Pad a numeric string with zeros on the left, to fill a field of the given width.\n\nThe original string is never truncated.", + "builtins.bytearray_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.bytearray_iterator.__eq__" => "Return self==value.", + "builtins.bytearray_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.bytearray_iterator.__ge__" => "Return self>=value.", + "builtins.bytearray_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.bytearray_iterator.__getstate__" => "Helper for pickle.", + "builtins.bytearray_iterator.__gt__" => "Return self>value.", + "builtins.bytearray_iterator.__hash__" => "Return hash(self).", + "builtins.bytearray_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.bytearray_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.bytearray_iterator.__iter__" => "Implement iter(self).", + "builtins.bytearray_iterator.__le__" => "Return self<=value.", + "builtins.bytearray_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.bytearray_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.bytearray_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.bytearray_iterator.__next__" => "Implement next(self).", + "builtins.bytearray_iterator.__reduce__" => "Return state information for pickling.", + "builtins.bytearray_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.bytearray_iterator.__repr__" => "Return repr(self).", + "builtins.bytearray_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.bytearray_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.bytearray_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.bytearray_iterator.__str__" => "Return str(self).", + "builtins.bytearray_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.bytes" => "bytes(iterable_of_ints) -> bytes\nbytes(string, encoding[, errors]) -> bytes\nbytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\nbytes(int) -> bytes object of size given by the parameter initialized with null bytes\nbytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n - an iterable yielding integers in range(256)\n - a text string encoded using the specified encoding\n - any object implementing the buffer API.\n - an integer", + "builtins.bytes.__add__" => "Return self+value.", + "builtins.bytes.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "builtins.bytes.__bytes__" => "Convert this value to exact type bytes.", + "builtins.bytes.__contains__" => "Return bool(key in self).", + "builtins.bytes.__delattr__" => "Implement delattr(self, name).", + "builtins.bytes.__eq__" => "Return self==value.", + "builtins.bytes.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.bytes.__ge__" => "Return self>=value.", + "builtins.bytes.__getattribute__" => "Return getattr(self, name).", + "builtins.bytes.__getitem__" => "Return self[key].", + "builtins.bytes.__getstate__" => "Helper for pickle.", + "builtins.bytes.__gt__" => "Return self>value.", + "builtins.bytes.__hash__" => "Return hash(self).", + "builtins.bytes.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.bytes.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.bytes.__iter__" => "Implement iter(self).", + "builtins.bytes.__le__" => "Return self<=value.", + "builtins.bytes.__len__" => "Return len(self).", + "builtins.bytes.__lt__" => "Return self "Return self%value.", + "builtins.bytes.__mul__" => "Return self*value.", + "builtins.bytes.__ne__" => "Return self!=value.", + "builtins.bytes.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.bytes.__reduce__" => "Helper for pickle.", + "builtins.bytes.__reduce_ex__" => "Helper for pickle.", + "builtins.bytes.__repr__" => "Return repr(self).", + "builtins.bytes.__rmod__" => "Return value%self.", + "builtins.bytes.__rmul__" => "Return value*self.", + "builtins.bytes.__setattr__" => "Implement setattr(self, name, value).", + "builtins.bytes.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.bytes.__str__" => "Return str(self).", + "builtins.bytes.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.bytes.capitalize" => "B.capitalize() -> copy of B\n\nReturn a copy of B with only its first character capitalized (ASCII)\nand the rest lower-cased.", + "builtins.bytes.center" => "Return a centered string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytes.count" => "Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[start:end].\n\nstart\n Optional start position. Default: start of the bytes.\nend\n Optional stop position. Default: end of the bytes.", + "builtins.bytes.decode" => "Decode the bytes using the codec registered for encoding.\n\nencoding\n The encoding with which to decode the bytes.\nerrors\n The error handling scheme to use for the handling of decoding errors.\n The default is 'strict' meaning that decoding errors raise a\n UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n as well as any other name registered with codecs.register_error that\n can handle UnicodeDecodeErrors.", + "builtins.bytes.endswith" => "Return True if the bytes ends with the specified suffix, False otherwise.\n\nsuffix\n A bytes or a tuple of bytes to try.\nstart\n Optional start position. Default: start of the bytes.\nend\n Optional stop position. Default: end of the bytes.", + "builtins.bytes.expandtabs" => "Return a copy where all tab characters are expanded using spaces.\n\nIf tabsize is not given, a tab size of 8 characters is assumed.", + "builtins.bytes.find" => "Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nReturn -1 on failure.", + "builtins.bytes.fromhex" => "Create a bytes object from a string of hexadecimal numbers.\n\nSpaces between two numbers are accepted.\nExample: bytes.fromhex('B9 01EF') -> b'\\\\xb9\\\\x01\\\\xef'.", + "builtins.bytes.hex" => "Create a string of hexadecimal numbers from a bytes object.\n\n sep\n An optional single character or byte to separate hex bytes.\n bytes_per_sep\n How many bytes between separators. Positive values count from the\n right, negative values count from the left.\n\nExample:\n>>> value = b'\\xb9\\x01\\xef'\n>>> value.hex()\n'b901ef'\n>>> value.hex(':')\n'b9:01:ef'\n>>> value.hex(':', 2)\n'b9:01ef'\n>>> value.hex(':', -2)\n'b901:ef'", + "builtins.bytes.index" => "Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nRaise ValueError if the subsection is not found.", + "builtins.bytes.isalnum" => "B.isalnum() -> bool\n\nReturn True if all characters in B are alphanumeric\nand there is at least one character in B, False otherwise.", + "builtins.bytes.isalpha" => "B.isalpha() -> bool\n\nReturn True if all characters in B are alphabetic\nand there is at least one character in B, False otherwise.", + "builtins.bytes.isascii" => "B.isascii() -> bool\n\nReturn True if B is empty or all characters in B are ASCII,\nFalse otherwise.", + "builtins.bytes.isdigit" => "B.isdigit() -> bool\n\nReturn True if all characters in B are digits\nand there is at least one character in B, False otherwise.", + "builtins.bytes.islower" => "B.islower() -> bool\n\nReturn True if all cased characters in B are lowercase and there is\nat least one cased character in B, False otherwise.", + "builtins.bytes.isspace" => "B.isspace() -> bool\n\nReturn True if all characters in B are whitespace\nand there is at least one character in B, False otherwise.", + "builtins.bytes.istitle" => "B.istitle() -> bool\n\nReturn True if B is a titlecased string and there is at least one\ncharacter in B, i.e. uppercase characters may only follow uncased\ncharacters and lowercase characters only cased ones. Return False\notherwise.", + "builtins.bytes.isupper" => "B.isupper() -> bool\n\nReturn True if all cased characters in B are uppercase and there is\nat least one cased character in B, False otherwise.", + "builtins.bytes.join" => "Concatenate any number of bytes objects.\n\nThe bytes whose method is called is inserted in between each pair.\n\nThe result is returned as a new bytes object.\n\nExample: b'.'.join([b'ab', b'pq', b'rs']) -> b'ab.pq.rs'.", + "builtins.bytes.ljust" => "Return a left-justified string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytes.lower" => "B.lower() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to lowercase.", + "builtins.bytes.lstrip" => "Strip leading bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading ASCII whitespace.", + "builtins.bytes.maketrans" => "Return a translation table usable for the bytes or bytearray translate method.\n\nThe returned table will be one where each byte in frm is mapped to the byte at\nthe same position in to.\n\nThe bytes objects frm and to must be of the same length.", + "builtins.bytes.partition" => "Partition the bytes into three parts using the given separator.\n\nThis will search for the separator sep in the bytes. If the separator is found,\nreturns a 3-tuple containing the part before the separator, the separator\nitself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing the original bytes\nobject and two empty bytes objects.", + "builtins.bytes.removeprefix" => "Return a bytes object with the given prefix string removed if present.\n\nIf the bytes starts with the prefix string, return bytes[len(prefix):].\nOtherwise, return a copy of the original bytes.", + "builtins.bytes.removesuffix" => "Return a bytes object with the given suffix string removed if present.\n\nIf the bytes ends with the suffix string and that suffix is not empty,\nreturn bytes[:-len(prefix)]. Otherwise, return a copy of the original\nbytes.", + "builtins.bytes.replace" => "Return a copy with all occurrences of substring old replaced by new.\n\n count\n Maximum number of occurrences to replace.\n -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.", + "builtins.bytes.rfind" => "Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nReturn -1 on failure.", + "builtins.bytes.rindex" => "Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].\n\n start\n Optional start position. Default: start of the bytes.\n end\n Optional stop position. Default: end of the bytes.\n\nRaise ValueError if the subsection is not found.", + "builtins.bytes.rjust" => "Return a right-justified string of length width.\n\nPadding is done using the specified fill character.", + "builtins.bytes.rpartition" => "Partition the bytes into three parts using the given separator.\n\nThis will search for the separator sep in the bytes, starting at the end. If\nthe separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing two empty bytes\nobjects and the original bytes object.", + "builtins.bytes.rsplit" => "Return a list of the sections in the bytes, using sep as the delimiter.\n\n sep\n The delimiter according which to split the bytes.\n None (the default value) means split on ASCII whitespace characters\n (space, tab, return, newline, formfeed, vertical tab).\n maxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.\n\nSplitting is done starting at the end of the bytes and working to the front.", + "builtins.bytes.rstrip" => "Strip trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip trailing ASCII whitespace.", + "builtins.bytes.split" => "Return a list of the sections in the bytes, using sep as the delimiter.\n\nsep\n The delimiter according which to split the bytes.\n None (the default value) means split on ASCII whitespace characters\n (space, tab, return, newline, formfeed, vertical tab).\nmaxsplit\n Maximum number of splits to do.\n -1 (the default value) means no limit.", + "builtins.bytes.splitlines" => "Return a list of the lines in the bytes, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.", + "builtins.bytes.startswith" => "Return True if the bytes starts with the specified prefix, False otherwise.\n\nprefix\n A bytes or a tuple of bytes to try.\nstart\n Optional start position. Default: start of the bytes.\nend\n Optional stop position. Default: end of the bytes.", + "builtins.bytes.strip" => "Strip leading and trailing bytes contained in the argument.\n\nIf the argument is omitted or None, strip leading and trailing ASCII whitespace.", + "builtins.bytes.swapcase" => "B.swapcase() -> copy of B\n\nReturn a copy of B with uppercase ASCII characters converted\nto lowercase ASCII and vice versa.", + "builtins.bytes.title" => "B.title() -> copy of B\n\nReturn a titlecased version of B, i.e. ASCII words start with uppercase\ncharacters, all remaining cased characters have lowercase.", + "builtins.bytes.translate" => "Return a copy with each character mapped by the given translation table.\n\n table\n Translation table, which must be a bytes object of length 256.\n\nAll characters occurring in the optional argument delete are removed.\nThe remaining characters are mapped through the given translation table.", + "builtins.bytes.upper" => "B.upper() -> copy of B\n\nReturn a copy of B with all ASCII characters converted to uppercase.", + "builtins.bytes.zfill" => "Pad a numeric string with zeros on the left, to fill a field of the given width.\n\nThe original string is never truncated.", + "builtins.bytes_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.bytes_iterator.__eq__" => "Return self==value.", + "builtins.bytes_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.bytes_iterator.__ge__" => "Return self>=value.", + "builtins.bytes_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.bytes_iterator.__getstate__" => "Helper for pickle.", + "builtins.bytes_iterator.__gt__" => "Return self>value.", + "builtins.bytes_iterator.__hash__" => "Return hash(self).", + "builtins.bytes_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.bytes_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.bytes_iterator.__iter__" => "Implement iter(self).", + "builtins.bytes_iterator.__le__" => "Return self<=value.", + "builtins.bytes_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.bytes_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.bytes_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.bytes_iterator.__next__" => "Implement next(self).", + "builtins.bytes_iterator.__reduce__" => "Return state information for pickling.", + "builtins.bytes_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.bytes_iterator.__repr__" => "Return repr(self).", + "builtins.bytes_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.bytes_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.bytes_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.bytes_iterator.__str__" => "Return str(self).", + "builtins.bytes_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.callable" => "Return whether the object is callable (i.e., some kind of function).\n\nNote that classes are callable, as are instances of classes with a\n__call__() method.", + "builtins.chr" => "Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.", + "builtins.classmethod" => "Convert a function to be a class method.\n\nA class method receives the class as implicit first argument,\njust like an instance method receives the instance.\nTo declare a class method, use this idiom:\n\n class C:\n @classmethod\n def f(cls, arg1, arg2, argN):\n ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()). The instance is ignored except for its class.\nIf a class method is called for a derived class, the derived class\nobject is passed as the implied first argument.\n\nClass methods are different than C++ or Java static methods.\nIf you want those, see the staticmethod builtin.", + "builtins.classmethod.__delattr__" => "Implement delattr(self, name).", + "builtins.classmethod.__eq__" => "Return self==value.", + "builtins.classmethod.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.classmethod.__ge__" => "Return self>=value.", + "builtins.classmethod.__get__" => "Return an attribute of instance, which is of type owner.", + "builtins.classmethod.__getattribute__" => "Return getattr(self, name).", + "builtins.classmethod.__getstate__" => "Helper for pickle.", + "builtins.classmethod.__gt__" => "Return self>value.", + "builtins.classmethod.__hash__" => "Return hash(self).", + "builtins.classmethod.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.classmethod.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.classmethod.__le__" => "Return self<=value.", + "builtins.classmethod.__lt__" => "Return self "Return self!=value.", + "builtins.classmethod.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.classmethod.__reduce__" => "Helper for pickle.", + "builtins.classmethod.__reduce_ex__" => "Helper for pickle.", + "builtins.classmethod.__repr__" => "Return repr(self).", + "builtins.classmethod.__setattr__" => "Implement setattr(self, name, value).", + "builtins.classmethod.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.classmethod.__str__" => "Return str(self).", + "builtins.classmethod.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.compile" => "Compile source into a code object that can be executed by exec() or eval().\n\nThe source code may represent a Python module, statement or expression.\nThe filename will be used for run-time error messages.\nThe mode must be 'exec' to compile a module, 'single' to compile a\nsingle (interactive) statement, or 'eval' to compile an expression.\nThe flags argument, if present, controls which future statements influence\nthe compilation of the code.\nThe dont_inherit argument, if true, stops the compilation inheriting\nthe effects of any future statements in effect in the code calling\ncompile; if absent or false these statements do influence the compilation,\nin addition to any features explicitly specified.", + "builtins.complex" => "Create a complex number from a string or numbers.\n\nIf a string is given, parse it as a complex number.\nIf a single number is given, convert it to a complex number.\nIf the 'real' or 'imag' arguments are given, create a complex number\nwith the specified real and imaginary components.", + "builtins.complex.__abs__" => "abs(self)", + "builtins.complex.__add__" => "Return self+value.", + "builtins.complex.__bool__" => "True if self else False", + "builtins.complex.__complex__" => "Convert this value to exact type complex.", + "builtins.complex.__delattr__" => "Implement delattr(self, name).", + "builtins.complex.__eq__" => "Return self==value.", + "builtins.complex.__format__" => "Convert to a string according to format_spec.", + "builtins.complex.__ge__" => "Return self>=value.", + "builtins.complex.__getattribute__" => "Return getattr(self, name).", + "builtins.complex.__getstate__" => "Helper for pickle.", + "builtins.complex.__gt__" => "Return self>value.", + "builtins.complex.__hash__" => "Return hash(self).", + "builtins.complex.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.complex.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.complex.__le__" => "Return self<=value.", + "builtins.complex.__lt__" => "Return self "Return self*value.", + "builtins.complex.__ne__" => "Return self!=value.", + "builtins.complex.__neg__" => "-self", + "builtins.complex.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.complex.__pos__" => "+self", + "builtins.complex.__pow__" => "Return pow(self, value, mod).", + "builtins.complex.__radd__" => "Return value+self.", + "builtins.complex.__reduce__" => "Helper for pickle.", + "builtins.complex.__reduce_ex__" => "Helper for pickle.", + "builtins.complex.__repr__" => "Return repr(self).", + "builtins.complex.__rmul__" => "Return value*self.", + "builtins.complex.__rpow__" => "Return pow(value, self, mod).", + "builtins.complex.__rsub__" => "Return value-self.", + "builtins.complex.__rtruediv__" => "Return value/self.", + "builtins.complex.__setattr__" => "Implement setattr(self, name, value).", + "builtins.complex.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.complex.__str__" => "Return str(self).", + "builtins.complex.__sub__" => "Return self-value.", + "builtins.complex.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.complex.__truediv__" => "Return self/value.", + "builtins.complex.conjugate" => "Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.", + "builtins.complex.imag" => "the imaginary part of a complex number", + "builtins.complex.real" => "the real part of a complex number", + "builtins.delattr" => "Deletes the named attribute from the given object.\n\ndelattr(x, 'y') is equivalent to ``del x.y``", + "builtins.dict" => "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)", + "builtins.dict.__class_getitem__" => "See PEP 585", + "builtins.dict.__contains__" => "True if the dictionary has the specified key, else False.", + "builtins.dict.__delattr__" => "Implement delattr(self, name).", + "builtins.dict.__delitem__" => "Delete self[key].", + "builtins.dict.__eq__" => "Return self==value.", + "builtins.dict.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict.__ge__" => "Return self>=value.", + "builtins.dict.__getattribute__" => "Return getattr(self, name).", + "builtins.dict.__getitem__" => "Return self[key].", + "builtins.dict.__getstate__" => "Helper for pickle.", + "builtins.dict.__gt__" => "Return self>value.", + "builtins.dict.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict.__ior__" => "Return self|=value.", + "builtins.dict.__iter__" => "Implement iter(self).", + "builtins.dict.__le__" => "Return self<=value.", + "builtins.dict.__len__" => "Return len(self).", + "builtins.dict.__lt__" => "Return self "Return self!=value.", + "builtins.dict.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict.__or__" => "Return self|value.", + "builtins.dict.__reduce__" => "Helper for pickle.", + "builtins.dict.__reduce_ex__" => "Helper for pickle.", + "builtins.dict.__repr__" => "Return repr(self).", + "builtins.dict.__reversed__" => "Return a reverse iterator over the dict keys.", + "builtins.dict.__ror__" => "Return value|self.", + "builtins.dict.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict.__setitem__" => "Set self[key] to value.", + "builtins.dict.__sizeof__" => "Return the size of the dict in memory, in bytes.", + "builtins.dict.__str__" => "Return str(self).", + "builtins.dict.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict.clear" => "Remove all items from the dict.", + "builtins.dict.copy" => "Return a shallow copy of the dict.", + "builtins.dict.fromkeys" => "Create a new dictionary with keys from iterable and values set to value.", + "builtins.dict.get" => "Return the value for key if key is in the dictionary, else default.", + "builtins.dict.items" => "Return a set-like object providing a view on the dict's items.", + "builtins.dict.keys" => "Return a set-like object providing a view on the dict's keys.", + "builtins.dict.pop" => "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\nIf the key is not found, return the default if given; otherwise,\nraise a KeyError.", + "builtins.dict.popitem" => "Remove and return a (key, value) pair as a 2-tuple.\n\nPairs are returned in LIFO (last-in, first-out) order.\nRaises KeyError if the dict is empty.", + "builtins.dict.setdefault" => "Insert key with a value of default if key is not in the dictionary.\n\nReturn the value for key if key is in the dictionary, else default.", + "builtins.dict.update" => "D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.\nIf E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k]\nIf E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\nIn either case, this is followed by: for k in F: D[k] = F[k]", + "builtins.dict.values" => "Return an object providing a view on the dict's values.", + "builtins.dict_itemiterator.__delattr__" => "Implement delattr(self, name).", + "builtins.dict_itemiterator.__eq__" => "Return self==value.", + "builtins.dict_itemiterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict_itemiterator.__ge__" => "Return self>=value.", + "builtins.dict_itemiterator.__getattribute__" => "Return getattr(self, name).", + "builtins.dict_itemiterator.__getstate__" => "Helper for pickle.", + "builtins.dict_itemiterator.__gt__" => "Return self>value.", + "builtins.dict_itemiterator.__hash__" => "Return hash(self).", + "builtins.dict_itemiterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict_itemiterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict_itemiterator.__iter__" => "Implement iter(self).", + "builtins.dict_itemiterator.__le__" => "Return self<=value.", + "builtins.dict_itemiterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.dict_itemiterator.__lt__" => "Return self "Return self!=value.", + "builtins.dict_itemiterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict_itemiterator.__next__" => "Implement next(self).", + "builtins.dict_itemiterator.__reduce__" => "Return state information for pickling.", + "builtins.dict_itemiterator.__reduce_ex__" => "Helper for pickle.", + "builtins.dict_itemiterator.__repr__" => "Return repr(self).", + "builtins.dict_itemiterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict_itemiterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.dict_itemiterator.__str__" => "Return str(self).", + "builtins.dict_itemiterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict_items.__and__" => "Return self&value.", + "builtins.dict_items.__contains__" => "Return bool(key in self).", + "builtins.dict_items.__delattr__" => "Implement delattr(self, name).", + "builtins.dict_items.__eq__" => "Return self==value.", + "builtins.dict_items.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict_items.__ge__" => "Return self>=value.", + "builtins.dict_items.__getattribute__" => "Return getattr(self, name).", + "builtins.dict_items.__getstate__" => "Helper for pickle.", + "builtins.dict_items.__gt__" => "Return self>value.", + "builtins.dict_items.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict_items.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict_items.__iter__" => "Implement iter(self).", + "builtins.dict_items.__le__" => "Return self<=value.", + "builtins.dict_items.__len__" => "Return len(self).", + "builtins.dict_items.__lt__" => "Return self "Return self!=value.", + "builtins.dict_items.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict_items.__or__" => "Return self|value.", + "builtins.dict_items.__rand__" => "Return value&self.", + "builtins.dict_items.__reduce__" => "Helper for pickle.", + "builtins.dict_items.__reduce_ex__" => "Helper for pickle.", + "builtins.dict_items.__repr__" => "Return repr(self).", + "builtins.dict_items.__reversed__" => "Return a reverse iterator over the dict items.", + "builtins.dict_items.__ror__" => "Return value|self.", + "builtins.dict_items.__rsub__" => "Return value-self.", + "builtins.dict_items.__rxor__" => "Return value^self.", + "builtins.dict_items.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict_items.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.dict_items.__str__" => "Return str(self).", + "builtins.dict_items.__sub__" => "Return self-value.", + "builtins.dict_items.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict_items.__xor__" => "Return self^value.", + "builtins.dict_items.isdisjoint" => "Return True if the view and the given iterable have a null intersection.", + "builtins.dict_items.mapping" => "dictionary that this view refers to", + "builtins.dict_keyiterator.__delattr__" => "Implement delattr(self, name).", + "builtins.dict_keyiterator.__eq__" => "Return self==value.", + "builtins.dict_keyiterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict_keyiterator.__ge__" => "Return self>=value.", + "builtins.dict_keyiterator.__getattribute__" => "Return getattr(self, name).", + "builtins.dict_keyiterator.__getstate__" => "Helper for pickle.", + "builtins.dict_keyiterator.__gt__" => "Return self>value.", + "builtins.dict_keyiterator.__hash__" => "Return hash(self).", + "builtins.dict_keyiterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict_keyiterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict_keyiterator.__iter__" => "Implement iter(self).", + "builtins.dict_keyiterator.__le__" => "Return self<=value.", + "builtins.dict_keyiterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.dict_keyiterator.__lt__" => "Return self "Return self!=value.", + "builtins.dict_keyiterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict_keyiterator.__next__" => "Implement next(self).", + "builtins.dict_keyiterator.__reduce__" => "Return state information for pickling.", + "builtins.dict_keyiterator.__reduce_ex__" => "Helper for pickle.", + "builtins.dict_keyiterator.__repr__" => "Return repr(self).", + "builtins.dict_keyiterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict_keyiterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.dict_keyiterator.__str__" => "Return str(self).", + "builtins.dict_keyiterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict_valueiterator.__delattr__" => "Implement delattr(self, name).", + "builtins.dict_valueiterator.__eq__" => "Return self==value.", + "builtins.dict_valueiterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict_valueiterator.__ge__" => "Return self>=value.", + "builtins.dict_valueiterator.__getattribute__" => "Return getattr(self, name).", + "builtins.dict_valueiterator.__getstate__" => "Helper for pickle.", + "builtins.dict_valueiterator.__gt__" => "Return self>value.", + "builtins.dict_valueiterator.__hash__" => "Return hash(self).", + "builtins.dict_valueiterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict_valueiterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict_valueiterator.__iter__" => "Implement iter(self).", + "builtins.dict_valueiterator.__le__" => "Return self<=value.", + "builtins.dict_valueiterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.dict_valueiterator.__lt__" => "Return self "Return self!=value.", + "builtins.dict_valueiterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict_valueiterator.__next__" => "Implement next(self).", + "builtins.dict_valueiterator.__reduce__" => "Return state information for pickling.", + "builtins.dict_valueiterator.__reduce_ex__" => "Helper for pickle.", + "builtins.dict_valueiterator.__repr__" => "Return repr(self).", + "builtins.dict_valueiterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict_valueiterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.dict_valueiterator.__str__" => "Return str(self).", + "builtins.dict_valueiterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict_values.__delattr__" => "Implement delattr(self, name).", + "builtins.dict_values.__eq__" => "Return self==value.", + "builtins.dict_values.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.dict_values.__ge__" => "Return self>=value.", + "builtins.dict_values.__getattribute__" => "Return getattr(self, name).", + "builtins.dict_values.__getstate__" => "Helper for pickle.", + "builtins.dict_values.__gt__" => "Return self>value.", + "builtins.dict_values.__hash__" => "Return hash(self).", + "builtins.dict_values.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.dict_values.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.dict_values.__iter__" => "Implement iter(self).", + "builtins.dict_values.__le__" => "Return self<=value.", + "builtins.dict_values.__len__" => "Return len(self).", + "builtins.dict_values.__lt__" => "Return self "Return self!=value.", + "builtins.dict_values.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.dict_values.__reduce__" => "Helper for pickle.", + "builtins.dict_values.__reduce_ex__" => "Helper for pickle.", + "builtins.dict_values.__repr__" => "Return repr(self).", + "builtins.dict_values.__reversed__" => "Return a reverse iterator over the dict values.", + "builtins.dict_values.__setattr__" => "Implement setattr(self, name, value).", + "builtins.dict_values.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.dict_values.__str__" => "Return str(self).", + "builtins.dict_values.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.dict_values.mapping" => "dictionary that this view refers to", + "builtins.dir" => "dir([object]) -> list of strings\n\nIf called without an argument, return the names in the current scope.\nElse, return an alphabetized list of names comprising (some of) the attributes\nof the given object, and of attributes reachable from it.\nIf the object supplies a method named __dir__, it will be used; otherwise\nthe default dir() logic is used and returns:\n for a module object: the module's attributes.\n for a class object: its attributes, and recursively the attributes\n of its bases.\n for any other object: its attributes, its class's attributes, and\n recursively the attributes of its class's base classes.", + "builtins.divmod" => "Return the tuple (x//y, x%y). Invariant: div*y + mod == x.", + "builtins.enumerate" => "Return an enumerate object.\n\n iterable\n an object supporting iteration\n\nThe enumerate object yields pairs containing a count (from start, which\ndefaults to zero) and a value yielded by the iterable argument.\n\nenumerate is useful for obtaining an indexed list:\n (0, seq[0]), (1, seq[1]), (2, seq[2]), ...", + "builtins.enumerate.__class_getitem__" => "See PEP 585", + "builtins.enumerate.__delattr__" => "Implement delattr(self, name).", + "builtins.enumerate.__eq__" => "Return self==value.", + "builtins.enumerate.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.enumerate.__ge__" => "Return self>=value.", + "builtins.enumerate.__getattribute__" => "Return getattr(self, name).", + "builtins.enumerate.__getstate__" => "Helper for pickle.", + "builtins.enumerate.__gt__" => "Return self>value.", + "builtins.enumerate.__hash__" => "Return hash(self).", + "builtins.enumerate.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.enumerate.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.enumerate.__iter__" => "Implement iter(self).", + "builtins.enumerate.__le__" => "Return self<=value.", + "builtins.enumerate.__lt__" => "Return self "Return self!=value.", + "builtins.enumerate.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.enumerate.__next__" => "Implement next(self).", + "builtins.enumerate.__reduce__" => "Return state information for pickling.", + "builtins.enumerate.__reduce_ex__" => "Helper for pickle.", + "builtins.enumerate.__repr__" => "Return repr(self).", + "builtins.enumerate.__setattr__" => "Implement setattr(self, name, value).", + "builtins.enumerate.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.enumerate.__str__" => "Return str(self).", + "builtins.enumerate.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.eval" => "Evaluate the given source in the context of globals and locals.\n\nThe source may be a string representing a Python expression\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it.", + "builtins.exec" => "Execute the given source in the context of globals and locals.\n\nThe source may be a string representing one or more Python statements\nor a code object as returned by compile().\nThe globals must be a dictionary and locals can be any mapping,\ndefaulting to the current globals and locals.\nIf only globals is given, locals defaults to it.\nThe closure must be a tuple of cellvars, and can only be used\nwhen source is a code object requiring exactly that many cellvars.", + "builtins.filter" => "Return an iterator yielding those items of iterable for which function(item)\nis true. If function is None, return the items that are true.", + "builtins.filter.__delattr__" => "Implement delattr(self, name).", + "builtins.filter.__eq__" => "Return self==value.", + "builtins.filter.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.filter.__ge__" => "Return self>=value.", + "builtins.filter.__getattribute__" => "Return getattr(self, name).", + "builtins.filter.__getstate__" => "Helper for pickle.", + "builtins.filter.__gt__" => "Return self>value.", + "builtins.filter.__hash__" => "Return hash(self).", + "builtins.filter.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.filter.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.filter.__iter__" => "Implement iter(self).", + "builtins.filter.__le__" => "Return self<=value.", + "builtins.filter.__lt__" => "Return self "Return self!=value.", + "builtins.filter.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.filter.__next__" => "Implement next(self).", + "builtins.filter.__reduce__" => "Return state information for pickling.", + "builtins.filter.__reduce_ex__" => "Helper for pickle.", + "builtins.filter.__repr__" => "Return repr(self).", + "builtins.filter.__setattr__" => "Implement setattr(self, name, value).", + "builtins.filter.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.filter.__str__" => "Return str(self).", + "builtins.filter.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.float" => "Convert a string or number to a floating-point number, if possible.", + "builtins.float.__abs__" => "abs(self)", + "builtins.float.__add__" => "Return self+value.", + "builtins.float.__bool__" => "True if self else False", + "builtins.float.__ceil__" => "Return the ceiling as an Integral.", + "builtins.float.__delattr__" => "Implement delattr(self, name).", + "builtins.float.__divmod__" => "Return divmod(self, value).", + "builtins.float.__eq__" => "Return self==value.", + "builtins.float.__float__" => "float(self)", + "builtins.float.__floor__" => "Return the floor as an Integral.", + "builtins.float.__floordiv__" => "Return self//value.", + "builtins.float.__format__" => "Formats the float according to format_spec.", + "builtins.float.__ge__" => "Return self>=value.", + "builtins.float.__getattribute__" => "Return getattr(self, name).", + "builtins.float.__getformat__" => "You probably don't want to use this function.\n\n typestr\n Must be 'double' or 'float'.\n\nIt exists mainly to be used in Python's test suite.\n\nThis function returns whichever of 'unknown', 'IEEE, big-endian' or 'IEEE,\nlittle-endian' best describes the format of floating-point numbers used by the\nC type named by typestr.", + "builtins.float.__getstate__" => "Helper for pickle.", + "builtins.float.__gt__" => "Return self>value.", + "builtins.float.__hash__" => "Return hash(self).", + "builtins.float.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.float.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.float.__int__" => "int(self)", + "builtins.float.__le__" => "Return self<=value.", + "builtins.float.__lt__" => "Return self "Return self%value.", + "builtins.float.__mul__" => "Return self*value.", + "builtins.float.__ne__" => "Return self!=value.", + "builtins.float.__neg__" => "-self", + "builtins.float.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.float.__pos__" => "+self", + "builtins.float.__pow__" => "Return pow(self, value, mod).", + "builtins.float.__radd__" => "Return value+self.", + "builtins.float.__rdivmod__" => "Return divmod(value, self).", + "builtins.float.__reduce__" => "Helper for pickle.", + "builtins.float.__reduce_ex__" => "Helper for pickle.", + "builtins.float.__repr__" => "Return repr(self).", + "builtins.float.__rfloordiv__" => "Return value//self.", + "builtins.float.__rmod__" => "Return value%self.", + "builtins.float.__rmul__" => "Return value*self.", + "builtins.float.__round__" => "Return the Integral closest to x, rounding half toward even.\n\nWhen an argument is passed, work like built-in round(x, ndigits).", + "builtins.float.__rpow__" => "Return pow(value, self, mod).", + "builtins.float.__rsub__" => "Return value-self.", + "builtins.float.__rtruediv__" => "Return value/self.", + "builtins.float.__setattr__" => "Implement setattr(self, name, value).", + "builtins.float.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.float.__str__" => "Return str(self).", + "builtins.float.__sub__" => "Return self-value.", + "builtins.float.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.float.__truediv__" => "Return self/value.", + "builtins.float.__trunc__" => "Return the Integral closest to x between 0 and x.", + "builtins.float.as_integer_ratio" => "Return a pair of integers, whose ratio is exactly equal to the original float.\n\nThe ratio is in lowest terms and has a positive denominator. Raise\nOverflowError on infinities and a ValueError on NaNs.\n\n>>> (10.0).as_integer_ratio()\n(10, 1)\n>>> (0.0).as_integer_ratio()\n(0, 1)\n>>> (-.25).as_integer_ratio()\n(-1, 4)", + "builtins.float.conjugate" => "Return self, the complex conjugate of any float.", + "builtins.float.fromhex" => "Create a floating-point number from a hexadecimal string.\n\n>>> float.fromhex('0x1.ffffp10')\n2047.984375\n>>> float.fromhex('-0x1p-1074')\n-5e-324", + "builtins.float.hex" => "Return a hexadecimal representation of a floating-point number.\n\n>>> (-0.1).hex()\n'-0x1.999999999999ap-4'\n>>> 3.14159.hex()\n'0x1.921f9f01b866ep+1'", + "builtins.float.imag" => "the imaginary part of a complex number", + "builtins.float.is_integer" => "Return True if the float is an integer.", + "builtins.float.real" => "the real part of a complex number", + "builtins.format" => "Return type(value).__format__(value, format_spec)\n\nMany built-in types implement format_spec according to the\nFormat Specification Mini-language. See help('FORMATTING').\n\nIf type(value) does not supply a method named __format__\nand format_spec is empty, then str(value) is returned.\nSee also help('SPECIALMETHODS').", + "builtins.frozenset" => "Build an immutable unordered collection of unique elements.", + "builtins.frozenset.__and__" => "Return self&value.", + "builtins.frozenset.__class_getitem__" => "See PEP 585", + "builtins.frozenset.__contains__" => "x.__contains__(y) <==> y in x.", + "builtins.frozenset.__delattr__" => "Implement delattr(self, name).", + "builtins.frozenset.__eq__" => "Return self==value.", + "builtins.frozenset.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.frozenset.__ge__" => "Return self>=value.", + "builtins.frozenset.__getattribute__" => "Return getattr(self, name).", + "builtins.frozenset.__getstate__" => "Helper for pickle.", + "builtins.frozenset.__gt__" => "Return self>value.", + "builtins.frozenset.__hash__" => "Return hash(self).", + "builtins.frozenset.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.frozenset.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.frozenset.__iter__" => "Implement iter(self).", + "builtins.frozenset.__le__" => "Return self<=value.", + "builtins.frozenset.__len__" => "Return len(self).", + "builtins.frozenset.__lt__" => "Return self "Return self!=value.", + "builtins.frozenset.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.frozenset.__or__" => "Return self|value.", + "builtins.frozenset.__rand__" => "Return value&self.", + "builtins.frozenset.__reduce__" => "Return state information for pickling.", + "builtins.frozenset.__reduce_ex__" => "Helper for pickle.", + "builtins.frozenset.__repr__" => "Return repr(self).", + "builtins.frozenset.__ror__" => "Return value|self.", + "builtins.frozenset.__rsub__" => "Return value-self.", + "builtins.frozenset.__rxor__" => "Return value^self.", + "builtins.frozenset.__setattr__" => "Implement setattr(self, name, value).", + "builtins.frozenset.__sizeof__" => "S.__sizeof__() -> size of S in memory, in bytes.", + "builtins.frozenset.__str__" => "Return str(self).", + "builtins.frozenset.__sub__" => "Return self-value.", + "builtins.frozenset.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.frozenset.__xor__" => "Return self^value.", + "builtins.frozenset.copy" => "Return a shallow copy of a set.", + "builtins.frozenset.difference" => "Return a new set with elements in the set that are not in the others.", + "builtins.frozenset.intersection" => "Return a new set with elements common to the set and all others.", + "builtins.frozenset.isdisjoint" => "Return True if two sets have a null intersection.", + "builtins.frozenset.issubset" => "Report whether another set contains this set.", + "builtins.frozenset.issuperset" => "Report whether this set contains another set.", + "builtins.frozenset.symmetric_difference" => "Return a new set with elements in either the set or other but not both.", + "builtins.frozenset.union" => "Return a new set with elements from the set and all others.", + "builtins.function" => "Create a function object.\n\ncode\n a code object\nglobals\n the globals dictionary\nname\n a string that overrides the name from the code object\nargdefs\n a tuple that specifies the default argument values\nclosure\n a tuple that supplies the bindings for free variables\nkwdefaults\n a dictionary that specifies the default keyword argument values", + "builtins.function.__call__" => "Call self as a function.", + "builtins.function.__delattr__" => "Implement delattr(self, name).", + "builtins.function.__eq__" => "Return self==value.", + "builtins.function.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.function.__ge__" => "Return self>=value.", + "builtins.function.__get__" => "Return an attribute of instance, which is of type owner.", + "builtins.function.__getattribute__" => "Return getattr(self, name).", + "builtins.function.__getstate__" => "Helper for pickle.", + "builtins.function.__gt__" => "Return self>value.", + "builtins.function.__hash__" => "Return hash(self).", + "builtins.function.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.function.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.function.__le__" => "Return self<=value.", + "builtins.function.__lt__" => "Return self "Return self!=value.", + "builtins.function.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.function.__reduce__" => "Helper for pickle.", + "builtins.function.__reduce_ex__" => "Helper for pickle.", + "builtins.function.__repr__" => "Return repr(self).", + "builtins.function.__setattr__" => "Implement setattr(self, name, value).", + "builtins.function.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.function.__str__" => "Return str(self).", + "builtins.function.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.function.__type_params__" => "Get the declared type parameters for a function.", + "builtins.getattr" => "getattr(object, name[, default]) -> value\n\nGet a named attribute from an object; getattr(x, 'y') is equivalent to x.y.\nWhen a default argument is given, it is returned when the attribute doesn't\nexist; without it, an exception is raised in that case.", + "builtins.globals" => "Return the dictionary containing the current scope's global variables.\n\nNOTE: Updates to this dictionary *will* affect name lookups in the current\nglobal scope and vice-versa.", + "builtins.hasattr" => "Return whether the object has an attribute with the given name.\n\nThis is done by calling getattr(obj, name) and catching AttributeError.", + "builtins.hash" => "Return the hash value for the given object.\n\nTwo objects that compare equal must also have the same hash value, but the\nreverse is not necessarily true.", + "builtins.hex" => "Return the hexadecimal representation of an integer.\n\n>>> hex(12648430)\n'0xc0ffee'", + "builtins.id" => "Return the identity of an object.\n\nThis is guaranteed to be unique among simultaneously existing objects.\n(CPython uses the object's memory address.)", + "builtins.input" => "Read a string from standard input. The trailing newline is stripped.\n\nThe prompt string, if given, is printed to standard output without a\ntrailing newline before reading input.\n\nIf the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.\nOn *nix systems, readline is used if available.", + "builtins.int" => "int([x]) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given. If x is a number, return x.__int__(). For floating-point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base. The literal can be preceded by '+' or '-' and be surrounded\nby whitespace. The base defaults to 10. Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4", + "builtins.int.__abs__" => "abs(self)", + "builtins.int.__add__" => "Return self+value.", + "builtins.int.__and__" => "Return self&value.", + "builtins.int.__bool__" => "True if self else False", + "builtins.int.__ceil__" => "Ceiling of an Integral returns itself.", + "builtins.int.__delattr__" => "Implement delattr(self, name).", + "builtins.int.__divmod__" => "Return divmod(self, value).", + "builtins.int.__eq__" => "Return self==value.", + "builtins.int.__float__" => "float(self)", + "builtins.int.__floor__" => "Flooring an Integral returns itself.", + "builtins.int.__floordiv__" => "Return self//value.", + "builtins.int.__format__" => "Convert to a string according to format_spec.", + "builtins.int.__ge__" => "Return self>=value.", + "builtins.int.__getattribute__" => "Return getattr(self, name).", + "builtins.int.__getstate__" => "Helper for pickle.", + "builtins.int.__gt__" => "Return self>value.", + "builtins.int.__hash__" => "Return hash(self).", + "builtins.int.__index__" => "Return self converted to an integer, if self is suitable for use as an index into a list.", + "builtins.int.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.int.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.int.__int__" => "int(self)", + "builtins.int.__invert__" => "~self", + "builtins.int.__le__" => "Return self<=value.", + "builtins.int.__lshift__" => "Return self< "Return self "Return self%value.", + "builtins.int.__mul__" => "Return self*value.", + "builtins.int.__ne__" => "Return self!=value.", + "builtins.int.__neg__" => "-self", + "builtins.int.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.int.__or__" => "Return self|value.", + "builtins.int.__pos__" => "+self", + "builtins.int.__pow__" => "Return pow(self, value, mod).", + "builtins.int.__radd__" => "Return value+self.", + "builtins.int.__rand__" => "Return value&self.", + "builtins.int.__rdivmod__" => "Return divmod(value, self).", + "builtins.int.__reduce__" => "Helper for pickle.", + "builtins.int.__reduce_ex__" => "Helper for pickle.", + "builtins.int.__repr__" => "Return repr(self).", + "builtins.int.__rfloordiv__" => "Return value//self.", + "builtins.int.__rlshift__" => "Return value< "Return value%self.", + "builtins.int.__rmul__" => "Return value*self.", + "builtins.int.__ror__" => "Return value|self.", + "builtins.int.__round__" => "Rounding an Integral returns itself.\n\nRounding with an ndigits argument also returns an integer.", + "builtins.int.__rpow__" => "Return pow(value, self, mod).", + "builtins.int.__rrshift__" => "Return value>>self.", + "builtins.int.__rshift__" => "Return self>>value.", + "builtins.int.__rsub__" => "Return value-self.", + "builtins.int.__rtruediv__" => "Return value/self.", + "builtins.int.__rxor__" => "Return value^self.", + "builtins.int.__setattr__" => "Implement setattr(self, name, value).", + "builtins.int.__sizeof__" => "Returns size in memory, in bytes.", + "builtins.int.__str__" => "Return str(self).", + "builtins.int.__sub__" => "Return self-value.", + "builtins.int.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.int.__truediv__" => "Return self/value.", + "builtins.int.__trunc__" => "Truncating an Integral returns itself.", + "builtins.int.__xor__" => "Return self^value.", + "builtins.int.as_integer_ratio" => "Return a pair of integers, whose ratio is equal to the original int.\n\nThe ratio is in lowest terms and has a positive denominator.\n\n>>> (10).as_integer_ratio()\n(10, 1)\n>>> (-10).as_integer_ratio()\n(-10, 1)\n>>> (0).as_integer_ratio()\n(0, 1)", + "builtins.int.bit_count" => "Number of ones in the binary representation of the absolute value of self.\n\nAlso known as the population count.\n\n>>> bin(13)\n'0b1101'\n>>> (13).bit_count()\n3", + "builtins.int.bit_length" => "Number of bits necessary to represent self in binary.\n\n>>> bin(37)\n'0b100101'\n>>> (37).bit_length()\n6", + "builtins.int.conjugate" => "Returns self, the complex conjugate of any int.", + "builtins.int.denominator" => "the denominator of a rational number in lowest terms", + "builtins.int.from_bytes" => "Return the integer represented by the given array of bytes.\n\nbytes\n Holds the array of bytes to convert. The argument must either\n support the buffer protocol or be an iterable object producing bytes.\n Bytes and bytearray are examples of built-in objects that support the\n buffer protocol.\nbyteorder\n The byte order used to represent the integer. If byteorder is 'big',\n the most significant byte is at the beginning of the byte array. If\n byteorder is 'little', the most significant byte is at the end of the\n byte array. To request the native byte order of the host system, use\n sys.byteorder as the byte order value. Default is to use 'big'.\nsigned\n Indicates whether two's complement is used to represent the integer.", + "builtins.int.imag" => "the imaginary part of a complex number", + "builtins.int.is_integer" => "Returns True. Exists for duck type compatibility with float.is_integer.", + "builtins.int.numerator" => "the numerator of a rational number in lowest terms", + "builtins.int.real" => "the real part of a complex number", + "builtins.int.to_bytes" => "Return an array of bytes representing an integer.\n\nlength\n Length of bytes object to use. An OverflowError is raised if the\n integer is not representable with the given number of bytes. Default\n is length 1.\nbyteorder\n The byte order used to represent the integer. If byteorder is 'big',\n the most significant byte is at the beginning of the byte array. If\n byteorder is 'little', the most significant byte is at the end of the\n byte array. To request the native byte order of the host system, use\n sys.byteorder as the byte order value. Default is to use 'big'.\nsigned\n Determines whether two's complement is used to represent the integer.\n If signed is False and a negative integer is given, an OverflowError\n is raised.", + "builtins.isinstance" => "Return whether an object is an instance of a class or of a subclass thereof.\n\nA tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\nor ...`` etc.", + "builtins.issubclass" => "Return whether 'cls' is derived from another class or is the same class.\n\nA tuple, as in ``issubclass(x, (A, B, ...))``, may be given as the target to\ncheck against. This is equivalent to ``issubclass(x, A) or issubclass(x, B)\nor ...``.", + "builtins.iter" => "iter(iterable) -> iterator\niter(callable, sentinel) -> iterator\n\nGet an iterator from an object. In the first form, the argument must\nsupply its own iterator, or be a sequence.\nIn the second form, the callable is called until it returns the sentinel.", + "builtins.len" => "Return the number of items in a container.", + "builtins.list" => "Built-in mutable sequence.\n\nIf no argument is given, the constructor creates a new empty list.\nThe argument must be an iterable if specified.", + "builtins.list.__add__" => "Return self+value.", + "builtins.list.__class_getitem__" => "See PEP 585", + "builtins.list.__contains__" => "Return bool(key in self).", + "builtins.list.__delattr__" => "Implement delattr(self, name).", + "builtins.list.__delitem__" => "Delete self[key].", + "builtins.list.__eq__" => "Return self==value.", + "builtins.list.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.list.__ge__" => "Return self>=value.", + "builtins.list.__getattribute__" => "Return getattr(self, name).", + "builtins.list.__getitem__" => "Return self[index].", + "builtins.list.__getstate__" => "Helper for pickle.", + "builtins.list.__gt__" => "Return self>value.", + "builtins.list.__iadd__" => "Implement self+=value.", + "builtins.list.__imul__" => "Implement self*=value.", + "builtins.list.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.list.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.list.__iter__" => "Implement iter(self).", + "builtins.list.__le__" => "Return self<=value.", + "builtins.list.__len__" => "Return len(self).", + "builtins.list.__lt__" => "Return self "Return self*value.", + "builtins.list.__ne__" => "Return self!=value.", + "builtins.list.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.list.__reduce__" => "Helper for pickle.", + "builtins.list.__reduce_ex__" => "Helper for pickle.", + "builtins.list.__repr__" => "Return repr(self).", + "builtins.list.__reversed__" => "Return a reverse iterator over the list.", + "builtins.list.__rmul__" => "Return value*self.", + "builtins.list.__setattr__" => "Implement setattr(self, name, value).", + "builtins.list.__setitem__" => "Set self[key] to value.", + "builtins.list.__sizeof__" => "Return the size of the list in memory, in bytes.", + "builtins.list.__str__" => "Return str(self).", + "builtins.list.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.list.append" => "Append object to the end of the list.", + "builtins.list.clear" => "Remove all items from list.", + "builtins.list.copy" => "Return a shallow copy of the list.", + "builtins.list.count" => "Return number of occurrences of value.", + "builtins.list.extend" => "Extend list by appending elements from the iterable.", + "builtins.list.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "builtins.list.insert" => "Insert object before index.", + "builtins.list.pop" => "Remove and return item at index (default last).\n\nRaises IndexError if list is empty or index is out of range.", + "builtins.list.remove" => "Remove first occurrence of value.\n\nRaises ValueError if the value is not present.", + "builtins.list.reverse" => "Reverse *IN PLACE*.", + "builtins.list.sort" => "Sort the list in ascending order and return None.\n\nThe sort is in-place (i.e. the list itself is modified) and stable (i.e. the\norder of two equal elements is maintained).\n\nIf a key function is given, apply it once to each list item and sort them,\nascending or descending, according to their function values.\n\nThe reverse flag can be set to sort in descending order.", + "builtins.list_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.list_iterator.__eq__" => "Return self==value.", + "builtins.list_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.list_iterator.__ge__" => "Return self>=value.", + "builtins.list_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.list_iterator.__getstate__" => "Helper for pickle.", + "builtins.list_iterator.__gt__" => "Return self>value.", + "builtins.list_iterator.__hash__" => "Return hash(self).", + "builtins.list_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.list_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.list_iterator.__iter__" => "Implement iter(self).", + "builtins.list_iterator.__le__" => "Return self<=value.", + "builtins.list_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.list_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.list_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.list_iterator.__next__" => "Implement next(self).", + "builtins.list_iterator.__reduce__" => "Return state information for pickling.", + "builtins.list_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.list_iterator.__repr__" => "Return repr(self).", + "builtins.list_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.list_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.list_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.list_iterator.__str__" => "Return str(self).", + "builtins.list_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.locals" => "Return a dictionary containing the current scope's local variables.\n\nNOTE: Whether or not updates to this dictionary will affect name lookups in\nthe local scope and vice-versa is *implementation dependent* and not\ncovered by any backwards compatibility guarantees.", + "builtins.map" => "Make an iterator that computes the function using arguments from\neach of the iterables. Stops when the shortest iterable is exhausted.", + "builtins.map.__delattr__" => "Implement delattr(self, name).", + "builtins.map.__eq__" => "Return self==value.", + "builtins.map.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.map.__ge__" => "Return self>=value.", + "builtins.map.__getattribute__" => "Return getattr(self, name).", + "builtins.map.__getstate__" => "Helper for pickle.", + "builtins.map.__gt__" => "Return self>value.", + "builtins.map.__hash__" => "Return hash(self).", + "builtins.map.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.map.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.map.__iter__" => "Implement iter(self).", + "builtins.map.__le__" => "Return self<=value.", + "builtins.map.__lt__" => "Return self "Return self!=value.", + "builtins.map.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.map.__next__" => "Implement next(self).", + "builtins.map.__reduce__" => "Return state information for pickling.", + "builtins.map.__reduce_ex__" => "Helper for pickle.", + "builtins.map.__repr__" => "Return repr(self).", + "builtins.map.__setattr__" => "Implement setattr(self, name, value).", + "builtins.map.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.map.__str__" => "Return str(self).", + "builtins.map.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.max" => "max(iterable, *[, default=obj, key=func]) -> value\nmax(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its biggest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more positional arguments, return the largest argument.", + "builtins.memory_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.memory_iterator.__eq__" => "Return self==value.", + "builtins.memory_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.memory_iterator.__ge__" => "Return self>=value.", + "builtins.memory_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.memory_iterator.__getstate__" => "Helper for pickle.", + "builtins.memory_iterator.__gt__" => "Return self>value.", + "builtins.memory_iterator.__hash__" => "Return hash(self).", + "builtins.memory_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.memory_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.memory_iterator.__iter__" => "Implement iter(self).", + "builtins.memory_iterator.__le__" => "Return self<=value.", + "builtins.memory_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.memory_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.memory_iterator.__next__" => "Implement next(self).", + "builtins.memory_iterator.__reduce__" => "Helper for pickle.", + "builtins.memory_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.memory_iterator.__repr__" => "Return repr(self).", + "builtins.memory_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.memory_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.memory_iterator.__str__" => "Return str(self).", + "builtins.memory_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.memoryview" => "Create a new memoryview object which references the given object.", + "builtins.memoryview.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "builtins.memoryview.__delattr__" => "Implement delattr(self, name).", + "builtins.memoryview.__delitem__" => "Delete self[key].", + "builtins.memoryview.__eq__" => "Return self==value.", + "builtins.memoryview.__exit__" => "Release the underlying buffer exposed by the memoryview object.", + "builtins.memoryview.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.memoryview.__ge__" => "Return self>=value.", + "builtins.memoryview.__getattribute__" => "Return getattr(self, name).", + "builtins.memoryview.__getitem__" => "Return self[key].", + "builtins.memoryview.__getstate__" => "Helper for pickle.", + "builtins.memoryview.__gt__" => "Return self>value.", + "builtins.memoryview.__hash__" => "Return hash(self).", + "builtins.memoryview.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.memoryview.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.memoryview.__iter__" => "Implement iter(self).", + "builtins.memoryview.__le__" => "Return self<=value.", + "builtins.memoryview.__len__" => "Return len(self).", + "builtins.memoryview.__lt__" => "Return self "Return self!=value.", + "builtins.memoryview.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.memoryview.__reduce__" => "Helper for pickle.", + "builtins.memoryview.__reduce_ex__" => "Helper for pickle.", + "builtins.memoryview.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "builtins.memoryview.__repr__" => "Return repr(self).", + "builtins.memoryview.__setattr__" => "Implement setattr(self, name, value).", + "builtins.memoryview.__setitem__" => "Set self[key] to value.", + "builtins.memoryview.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.memoryview.__str__" => "Return str(self).", + "builtins.memoryview.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.memoryview._from_flags" => "Create a new memoryview object which references the given object.", + "builtins.memoryview.c_contiguous" => "A bool indicating whether the memory is C contiguous.", + "builtins.memoryview.cast" => "Cast a memoryview to a new format or shape.", + "builtins.memoryview.contiguous" => "A bool indicating whether the memory is contiguous.", + "builtins.memoryview.f_contiguous" => "A bool indicating whether the memory is Fortran contiguous.", + "builtins.memoryview.format" => "A string containing the format (in struct module style)\nfor each element in the view.", + "builtins.memoryview.hex" => "Return the data in the buffer as a str of hexadecimal numbers.\n\n sep\n An optional single character or byte to separate hex bytes.\n bytes_per_sep\n How many bytes between separators. Positive values count from the\n right, negative values count from the left.\n\nExample:\n>>> value = memoryview(b'\\xb9\\x01\\xef')\n>>> value.hex()\n'b901ef'\n>>> value.hex(':')\n'b9:01:ef'\n>>> value.hex(':', 2)\n'b9:01ef'\n>>> value.hex(':', -2)\n'b901:ef'", + "builtins.memoryview.itemsize" => "The size in bytes of each element of the memoryview.", + "builtins.memoryview.nbytes" => "The amount of space in bytes that the array would use in\na contiguous representation.", + "builtins.memoryview.ndim" => "An integer indicating how many dimensions of a multi-dimensional\narray the memory represents.", + "builtins.memoryview.obj" => "The underlying object of the memoryview.", + "builtins.memoryview.readonly" => "A bool indicating whether the memory is read only.", + "builtins.memoryview.release" => "Release the underlying buffer exposed by the memoryview object.", + "builtins.memoryview.shape" => "A tuple of ndim integers giving the shape of the memory\nas an N-dimensional array.", + "builtins.memoryview.strides" => "A tuple of ndim integers giving the size in bytes to access\neach element for each dimension of the array.", + "builtins.memoryview.suboffsets" => "A tuple of integers used internally for PIL-style arrays.", + "builtins.memoryview.tobytes" => "Return the data in the buffer as a byte string.\n\nOrder can be {'C', 'F', 'A'}. When order is 'C' or 'F', the data of the\noriginal array is converted to C or Fortran order. For contiguous views,\n'A' returns an exact copy of the physical memory. In particular, in-memory\nFortran order is preserved. For non-contiguous views, the data is converted\nto C first. order=None is the same as order='C'.", + "builtins.memoryview.tolist" => "Return the data in the buffer as a list of elements.", + "builtins.memoryview.toreadonly" => "Return a readonly version of the memoryview.", + "builtins.min" => "min(iterable, *[, default=obj, key=func]) -> value\nmin(arg1, arg2, *args, *[, key=func]) -> value\n\nWith a single iterable argument, return its smallest item. The\ndefault keyword-only argument specifies an object to return if\nthe provided iterable is empty.\nWith two or more positional arguments, return the smallest argument.", + "builtins.next" => "next(iterator[, default])\n\nReturn the next item from the iterator. If default is given and the iterator\nis exhausted, it is returned instead of raising StopIteration.", + "builtins.object" => "The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.", + "builtins.object.__delattr__" => "Implement delattr(self, name).", + "builtins.object.__eq__" => "Return self==value.", + "builtins.object.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.object.__ge__" => "Return self>=value.", + "builtins.object.__getattribute__" => "Return getattr(self, name).", + "builtins.object.__getstate__" => "Helper for pickle.", + "builtins.object.__gt__" => "Return self>value.", + "builtins.object.__hash__" => "Return hash(self).", + "builtins.object.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.object.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.object.__le__" => "Return self<=value.", + "builtins.object.__lt__" => "Return self "Return self!=value.", + "builtins.object.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.object.__reduce__" => "Helper for pickle.", + "builtins.object.__reduce_ex__" => "Helper for pickle.", + "builtins.object.__repr__" => "Return repr(self).", + "builtins.object.__setattr__" => "Implement setattr(self, name, value).", + "builtins.object.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.object.__str__" => "Return str(self).", + "builtins.object.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.oct" => "Return the octal representation of an integer.\n\n>>> oct(342391)\n'0o1234567'", + "builtins.ord" => "Return the ordinal value of a character.\n\nIf the argument is a one-character string, return the Unicode code\npoint of that character.\n\nIf the argument is a bytes or bytearray object of length 1, return its\nsingle byte value.", + "builtins.pow" => "Equivalent to base**exp with 2 arguments or base**exp % mod with 3 arguments\n\nSome types, such as ints, are able to use a more efficient algorithm when\ninvoked using the three argument form.", + "builtins.print" => "Prints the values to a stream, or to sys.stdout by default.\n\nsep\n string inserted between values, default a space.\nend\n string appended after the last value, default a newline.\nfile\n a file-like object (stream); defaults to the current sys.stdout.\nflush\n whether to forcibly flush the stream.", + "builtins.property" => "Property attribute.\n\n fget\n function to be used for getting an attribute value\n fset\n function to be used for setting an attribute value\n fdel\n function to be used for del'ing an attribute\n doc\n docstring\n\nTypical use is to define a managed attribute x:\n\nclass C(object):\n def getx(self): return self._x\n def setx(self, value): self._x = value\n def delx(self): del self._x\n x = property(getx, setx, delx, \"I'm the 'x' property.\")\n\nDecorators make defining new properties or modifying existing ones easy:\n\nclass C(object):\n @property\n def x(self):\n \"I am the 'x' property.\"\n return self._x\n @x.setter\n def x(self, value):\n self._x = value\n @x.deleter\n def x(self):\n del self._x", + "builtins.property.__delattr__" => "Implement delattr(self, name).", + "builtins.property.__delete__" => "Delete an attribute of instance.", + "builtins.property.__eq__" => "Return self==value.", + "builtins.property.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.property.__ge__" => "Return self>=value.", + "builtins.property.__get__" => "Return an attribute of instance, which is of type owner.", + "builtins.property.__getattribute__" => "Return getattr(self, name).", + "builtins.property.__getstate__" => "Helper for pickle.", + "builtins.property.__gt__" => "Return self>value.", + "builtins.property.__hash__" => "Return hash(self).", + "builtins.property.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.property.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.property.__le__" => "Return self<=value.", + "builtins.property.__lt__" => "Return self "Return self!=value.", + "builtins.property.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.property.__reduce__" => "Helper for pickle.", + "builtins.property.__reduce_ex__" => "Helper for pickle.", + "builtins.property.__repr__" => "Return repr(self).", + "builtins.property.__set__" => "Set an attribute of instance to value.", + "builtins.property.__set_name__" => "Method to set name of a property.", + "builtins.property.__setattr__" => "Implement setattr(self, name, value).", + "builtins.property.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.property.__str__" => "Return str(self).", + "builtins.property.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.property.deleter" => "Descriptor to obtain a copy of the property with a different deleter.", + "builtins.property.getter" => "Descriptor to obtain a copy of the property with a different getter.", + "builtins.property.setter" => "Descriptor to obtain a copy of the property with a different setter.", + "builtins.range" => "range(stop) -> range object\nrange(start, stop[, step]) -> range object\n\nReturn an object that produces a sequence of integers from start (inclusive)\nto stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.\nstart defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.\nThese are exactly the valid indices for a list of 4 elements.\nWhen step is given, it specifies the increment (or decrement).", + "builtins.range.__bool__" => "True if self else False", + "builtins.range.__contains__" => "Return bool(key in self).", + "builtins.range.__delattr__" => "Implement delattr(self, name).", + "builtins.range.__eq__" => "Return self==value.", + "builtins.range.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.range.__ge__" => "Return self>=value.", + "builtins.range.__getattribute__" => "Return getattr(self, name).", + "builtins.range.__getitem__" => "Return self[key].", + "builtins.range.__getstate__" => "Helper for pickle.", + "builtins.range.__gt__" => "Return self>value.", + "builtins.range.__hash__" => "Return hash(self).", + "builtins.range.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.range.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.range.__iter__" => "Implement iter(self).", + "builtins.range.__le__" => "Return self<=value.", + "builtins.range.__len__" => "Return len(self).", + "builtins.range.__lt__" => "Return self "Return self!=value.", + "builtins.range.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.range.__reduce_ex__" => "Helper for pickle.", + "builtins.range.__repr__" => "Return repr(self).", + "builtins.range.__reversed__" => "Return a reverse iterator.", + "builtins.range.__setattr__" => "Implement setattr(self, name, value).", + "builtins.range.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.range.__str__" => "Return str(self).", + "builtins.range.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.range.count" => "rangeobject.count(value) -> integer -- return number of occurrences of value", + "builtins.range.index" => "rangeobject.index(value) -> integer -- return index of value.\nRaise ValueError if the value is not present.", + "builtins.range_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.range_iterator.__eq__" => "Return self==value.", + "builtins.range_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.range_iterator.__ge__" => "Return self>=value.", + "builtins.range_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.range_iterator.__getstate__" => "Helper for pickle.", + "builtins.range_iterator.__gt__" => "Return self>value.", + "builtins.range_iterator.__hash__" => "Return hash(self).", + "builtins.range_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.range_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.range_iterator.__iter__" => "Implement iter(self).", + "builtins.range_iterator.__le__" => "Return self<=value.", + "builtins.range_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.range_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.range_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.range_iterator.__next__" => "Implement next(self).", + "builtins.range_iterator.__reduce__" => "Return state information for pickling.", + "builtins.range_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.range_iterator.__repr__" => "Return repr(self).", + "builtins.range_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.range_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.range_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.range_iterator.__str__" => "Return str(self).", + "builtins.range_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.repr" => "Return the canonical string representation of the object.\n\nFor many object types, including most builtins, eval(repr(obj)) == obj.", + "builtins.reversed" => "Return a reverse iterator over the values of the given sequence.", + "builtins.reversed.__delattr__" => "Implement delattr(self, name).", + "builtins.reversed.__eq__" => "Return self==value.", + "builtins.reversed.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.reversed.__ge__" => "Return self>=value.", + "builtins.reversed.__getattribute__" => "Return getattr(self, name).", + "builtins.reversed.__getstate__" => "Helper for pickle.", + "builtins.reversed.__gt__" => "Return self>value.", + "builtins.reversed.__hash__" => "Return hash(self).", + "builtins.reversed.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.reversed.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.reversed.__iter__" => "Implement iter(self).", + "builtins.reversed.__le__" => "Return self<=value.", + "builtins.reversed.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.reversed.__lt__" => "Return self "Return self!=value.", + "builtins.reversed.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.reversed.__next__" => "Implement next(self).", + "builtins.reversed.__reduce__" => "Return state information for pickling.", + "builtins.reversed.__reduce_ex__" => "Helper for pickle.", + "builtins.reversed.__repr__" => "Return repr(self).", + "builtins.reversed.__setattr__" => "Implement setattr(self, name, value).", + "builtins.reversed.__setstate__" => "Set state information for unpickling.", + "builtins.reversed.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.reversed.__str__" => "Return str(self).", + "builtins.reversed.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.round" => "Round a number to a given precision in decimal digits.\n\nThe return value is an integer if ndigits is omitted or None. Otherwise\nthe return value has the same type as the number. ndigits may be negative.", + "builtins.set" => "Build an unordered collection of unique elements.", + "builtins.set.__and__" => "Return self&value.", + "builtins.set.__class_getitem__" => "See PEP 585", + "builtins.set.__contains__" => "x.__contains__(y) <==> y in x.", + "builtins.set.__delattr__" => "Implement delattr(self, name).", + "builtins.set.__eq__" => "Return self==value.", + "builtins.set.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.set.__ge__" => "Return self>=value.", + "builtins.set.__getattribute__" => "Return getattr(self, name).", + "builtins.set.__getstate__" => "Helper for pickle.", + "builtins.set.__gt__" => "Return self>value.", + "builtins.set.__iand__" => "Return self&=value.", + "builtins.set.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.set.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.set.__ior__" => "Return self|=value.", + "builtins.set.__isub__" => "Return self-=value.", + "builtins.set.__iter__" => "Implement iter(self).", + "builtins.set.__ixor__" => "Return self^=value.", + "builtins.set.__le__" => "Return self<=value.", + "builtins.set.__len__" => "Return len(self).", + "builtins.set.__lt__" => "Return self "Return self!=value.", + "builtins.set.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.set.__or__" => "Return self|value.", + "builtins.set.__rand__" => "Return value&self.", + "builtins.set.__reduce__" => "Return state information for pickling.", + "builtins.set.__reduce_ex__" => "Helper for pickle.", + "builtins.set.__repr__" => "Return repr(self).", + "builtins.set.__ror__" => "Return value|self.", + "builtins.set.__rsub__" => "Return value-self.", + "builtins.set.__rxor__" => "Return value^self.", + "builtins.set.__setattr__" => "Implement setattr(self, name, value).", + "builtins.set.__sizeof__" => "S.__sizeof__() -> size of S in memory, in bytes.", + "builtins.set.__str__" => "Return str(self).", + "builtins.set.__sub__" => "Return self-value.", + "builtins.set.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.set.__xor__" => "Return self^value.", + "builtins.set.add" => "Add an element to a set.\n\nThis has no effect if the element is already present.", + "builtins.set.clear" => "Remove all elements from this set.", + "builtins.set.copy" => "Return a shallow copy of a set.", + "builtins.set.difference" => "Return a new set with elements in the set that are not in the others.", + "builtins.set.difference_update" => "Update the set, removing elements found in others.", + "builtins.set.discard" => "Remove an element from a set if it is a member.\n\nUnlike set.remove(), the discard() method does not raise\nan exception when an element is missing from the set.", + "builtins.set.intersection" => "Return a new set with elements common to the set and all others.", + "builtins.set.intersection_update" => "Update the set, keeping only elements found in it and all others.", + "builtins.set.isdisjoint" => "Return True if two sets have a null intersection.", + "builtins.set.issubset" => "Report whether another set contains this set.", + "builtins.set.issuperset" => "Report whether this set contains another set.", + "builtins.set.pop" => "Remove and return an arbitrary set element.\n\nRaises KeyError if the set is empty.", + "builtins.set.remove" => "Remove an element from a set; it must be a member.\n\nIf the element is not a member, raise a KeyError.", + "builtins.set.symmetric_difference" => "Return a new set with elements in either the set or other but not both.", + "builtins.set.symmetric_difference_update" => "Update the set, keeping only elements found in either set, but not in both.", + "builtins.set.union" => "Return a new set with elements from the set and all others.", + "builtins.set.update" => "Update the set, adding elements from all others.", + "builtins.set_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.set_iterator.__eq__" => "Return self==value.", + "builtins.set_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.set_iterator.__ge__" => "Return self>=value.", + "builtins.set_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.set_iterator.__getstate__" => "Helper for pickle.", + "builtins.set_iterator.__gt__" => "Return self>value.", + "builtins.set_iterator.__hash__" => "Return hash(self).", + "builtins.set_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.set_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.set_iterator.__iter__" => "Implement iter(self).", + "builtins.set_iterator.__le__" => "Return self<=value.", + "builtins.set_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.set_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.set_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.set_iterator.__next__" => "Implement next(self).", + "builtins.set_iterator.__reduce__" => "Return state information for pickling.", + "builtins.set_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.set_iterator.__repr__" => "Return repr(self).", + "builtins.set_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.set_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.set_iterator.__str__" => "Return str(self).", + "builtins.set_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.setattr" => "Sets the named attribute on the given object to the specified value.\n\nsetattr(x, 'y', v) is equivalent to ``x.y = v``", + "builtins.slice" => "slice(stop)\nslice(start, stop[, step])\n\nCreate a slice object. This is used for extended slicing (e.g. a[0:10:2]).", + "builtins.slice.__delattr__" => "Implement delattr(self, name).", + "builtins.slice.__eq__" => "Return self==value.", + "builtins.slice.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.slice.__ge__" => "Return self>=value.", + "builtins.slice.__getattribute__" => "Return getattr(self, name).", + "builtins.slice.__getstate__" => "Helper for pickle.", + "builtins.slice.__gt__" => "Return self>value.", + "builtins.slice.__hash__" => "Return hash(self).", + "builtins.slice.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.slice.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.slice.__le__" => "Return self<=value.", + "builtins.slice.__lt__" => "Return self "Return self!=value.", + "builtins.slice.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.slice.__reduce__" => "Return state information for pickling.", + "builtins.slice.__reduce_ex__" => "Helper for pickle.", + "builtins.slice.__repr__" => "Return repr(self).", + "builtins.slice.__setattr__" => "Implement setattr(self, name, value).", + "builtins.slice.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.slice.__str__" => "Return str(self).", + "builtins.slice.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.slice.indices" => "S.indices(len) -> (start, stop, stride)\n\nAssuming a sequence of length len, calculate the start and stop\nindices, and the stride length of the extended slice described by\nS. Out of bounds indices are clipped in a manner consistent with the\nhandling of normal slices.", + "builtins.sorted" => "Return a new list containing all items from the iterable in ascending order.\n\nA custom key function can be supplied to customize the sort order, and the\nreverse flag can be set to request the result in descending order.", + "builtins.staticmethod" => "Convert a function to be a static method.\n\nA static method does not receive an implicit first argument.\nTo declare a static method, use this idiom:\n\n class C:\n @staticmethod\n def f(arg1, arg2, argN):\n ...\n\nIt can be called either on the class (e.g. C.f()) or on an instance\n(e.g. C().f()). Both the class and the instance are ignored, and\nneither is passed implicitly as the first argument to the method.\n\nStatic methods in Python are similar to those found in Java or C++.\nFor a more advanced concept, see the classmethod builtin.", + "builtins.staticmethod.__call__" => "Call self as a function.", + "builtins.staticmethod.__delattr__" => "Implement delattr(self, name).", + "builtins.staticmethod.__eq__" => "Return self==value.", + "builtins.staticmethod.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.staticmethod.__ge__" => "Return self>=value.", + "builtins.staticmethod.__get__" => "Return an attribute of instance, which is of type owner.", + "builtins.staticmethod.__getattribute__" => "Return getattr(self, name).", + "builtins.staticmethod.__getstate__" => "Helper for pickle.", + "builtins.staticmethod.__gt__" => "Return self>value.", + "builtins.staticmethod.__hash__" => "Return hash(self).", + "builtins.staticmethod.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.staticmethod.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.staticmethod.__le__" => "Return self<=value.", + "builtins.staticmethod.__lt__" => "Return self "Return self!=value.", + "builtins.staticmethod.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.staticmethod.__reduce__" => "Helper for pickle.", + "builtins.staticmethod.__reduce_ex__" => "Helper for pickle.", + "builtins.staticmethod.__repr__" => "Return repr(self).", + "builtins.staticmethod.__setattr__" => "Implement setattr(self, name, value).", + "builtins.staticmethod.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.staticmethod.__str__" => "Return str(self).", + "builtins.staticmethod.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.str" => "str(object='') -> str\nstr(bytes_or_buffer[, encoding[, errors]]) -> str\n\nCreate a new string object from the given object. If encoding or\nerrors is specified, then the object must expose a data buffer\nthat will be decoded using the given encoding and error handler.\nOtherwise, returns the result of object.__str__() (if defined)\nor repr(object).\nencoding defaults to 'utf-8'.\nerrors defaults to 'strict'.", + "builtins.str.__add__" => "Return self+value.", + "builtins.str.__contains__" => "Return bool(key in self).", + "builtins.str.__delattr__" => "Implement delattr(self, name).", + "builtins.str.__eq__" => "Return self==value.", + "builtins.str.__format__" => "Return a formatted version of the string as described by format_spec.", + "builtins.str.__ge__" => "Return self>=value.", + "builtins.str.__getattribute__" => "Return getattr(self, name).", + "builtins.str.__getitem__" => "Return self[key].", + "builtins.str.__getstate__" => "Helper for pickle.", + "builtins.str.__gt__" => "Return self>value.", + "builtins.str.__hash__" => "Return hash(self).", + "builtins.str.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.str.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.str.__iter__" => "Implement iter(self).", + "builtins.str.__le__" => "Return self<=value.", + "builtins.str.__len__" => "Return len(self).", + "builtins.str.__lt__" => "Return self "Return self%value.", + "builtins.str.__mul__" => "Return self*value.", + "builtins.str.__ne__" => "Return self!=value.", + "builtins.str.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.str.__reduce__" => "Helper for pickle.", + "builtins.str.__reduce_ex__" => "Helper for pickle.", + "builtins.str.__repr__" => "Return repr(self).", + "builtins.str.__rmod__" => "Return value%self.", + "builtins.str.__rmul__" => "Return value*self.", + "builtins.str.__setattr__" => "Implement setattr(self, name, value).", + "builtins.str.__sizeof__" => "Return the size of the string in memory, in bytes.", + "builtins.str.__str__" => "Return str(self).", + "builtins.str.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.str.capitalize" => "Return a capitalized version of the string.\n\nMore specifically, make the first character have upper case and the rest lower\ncase.", + "builtins.str.casefold" => "Return a version of the string suitable for caseless comparisons.", + "builtins.str.center" => "Return a centered string of length width.\n\nPadding is done using the specified fill character (default is a space).", + "builtins.str.count" => "Return the number of non-overlapping occurrences of substring sub in string S[start:end].\n\nOptional arguments start and end are interpreted as in slice notation.", + "builtins.str.encode" => "Encode the string using the codec registered for encoding.\n\nencoding\n The encoding in which to encode the string.\nerrors\n The error handling scheme to use for encoding errors.\n The default is 'strict' meaning that encoding errors raise a\n UnicodeEncodeError. Other possible values are 'ignore', 'replace' and\n 'xmlcharrefreplace' as well as any other name registered with\n codecs.register_error that can handle UnicodeEncodeErrors.", + "builtins.str.endswith" => "Return True if the string ends with the specified suffix, False otherwise.\n\nsuffix\n A string or a tuple of strings to try.\nstart\n Optional start position. Default: start of the string.\nend\n Optional stop position. Default: end of the string.", + "builtins.str.expandtabs" => "Return a copy where all tab characters are expanded using spaces.\n\nIf tabsize is not given, a tab size of 8 characters is assumed.", + "builtins.str.find" => "Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n\nOptional arguments start and end are interpreted as in slice notation.\nReturn -1 on failure.", + "builtins.str.format" => "Return a formatted version of the string, using substitutions from args and kwargs.\nThe substitutions are identified by braces ('{' and '}').", + "builtins.str.format_map" => "Return a formatted version of the string, using substitutions from mapping.\nThe substitutions are identified by braces ('{' and '}').", + "builtins.str.index" => "Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n\nOptional arguments start and end are interpreted as in slice notation.\nRaises ValueError when the substring is not found.", + "builtins.str.isalnum" => "Return True if the string is an alpha-numeric string, False otherwise.\n\nA string is alpha-numeric if all characters in the string are alpha-numeric and\nthere is at least one character in the string.", + "builtins.str.isalpha" => "Return True if the string is an alphabetic string, False otherwise.\n\nA string is alphabetic if all characters in the string are alphabetic and there\nis at least one character in the string.", + "builtins.str.isascii" => "Return True if all characters in the string are ASCII, False otherwise.\n\nASCII characters have code points in the range U+0000-U+007F.\nEmpty string is ASCII too.", + "builtins.str.isdecimal" => "Return True if the string is a decimal string, False otherwise.\n\nA string is a decimal string if all characters in the string are decimal and\nthere is at least one character in the string.", + "builtins.str.isdigit" => "Return True if the string is a digit string, False otherwise.\n\nA string is a digit string if all characters in the string are digits and there\nis at least one character in the string.", + "builtins.str.isidentifier" => "Return True if the string is a valid Python identifier, False otherwise.\n\nCall keyword.iskeyword(s) to test whether string s is a reserved identifier,\nsuch as \"def\" or \"class\".", + "builtins.str.islower" => "Return True if the string is a lowercase string, False otherwise.\n\nA string is lowercase if all cased characters in the string are lowercase and\nthere is at least one cased character in the string.", + "builtins.str.isnumeric" => "Return True if the string is a numeric string, False otherwise.\n\nA string is numeric if all characters in the string are numeric and there is at\nleast one character in the string.", + "builtins.str.isprintable" => "Return True if all characters in the string are printable, False otherwise.\n\nA character is printable if repr() may use it in its output.", + "builtins.str.isspace" => "Return True if the string is a whitespace string, False otherwise.\n\nA string is whitespace if all characters in the string are whitespace and there\nis at least one character in the string.", + "builtins.str.istitle" => "Return True if the string is a title-cased string, False otherwise.\n\nIn a title-cased string, upper- and title-case characters may only\nfollow uncased characters and lowercase characters only cased ones.", + "builtins.str.isupper" => "Return True if the string is an uppercase string, False otherwise.\n\nA string is uppercase if all cased characters in the string are uppercase and\nthere is at least one cased character in the string.", + "builtins.str.join" => "Concatenate any number of strings.\n\nThe string whose method is called is inserted in between each given string.\nThe result is returned as a new string.\n\nExample: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'", + "builtins.str.ljust" => "Return a left-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).", + "builtins.str.lower" => "Return a copy of the string converted to lowercase.", + "builtins.str.lstrip" => "Return a copy of the string with leading whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.", + "builtins.str.maketrans" => "Return a translation table usable for str.translate().\n\nIf there is only one argument, it must be a dictionary mapping Unicode\nordinals (integers) or characters to Unicode ordinals, strings or None.\nCharacter keys will be then converted to ordinals.\nIf there are two arguments, they must be strings of equal length, and\nin the resulting dictionary, each character in x will be mapped to the\ncharacter at the same position in y. If there is a third argument, it\nmust be a string, whose characters will be mapped to None in the result.", + "builtins.str.partition" => "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string. If the separator is found,\nreturns a 3-tuple containing the part before the separator, the separator\nitself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing the original string\nand two empty strings.", + "builtins.str.removeprefix" => "Return a str with the given prefix string removed if present.\n\nIf the string starts with the prefix string, return string[len(prefix):].\nOtherwise, return a copy of the original string.", + "builtins.str.removesuffix" => "Return a str with the given suffix string removed if present.\n\nIf the string ends with the suffix string and that suffix is not empty,\nreturn string[:-len(suffix)]. Otherwise, return a copy of the original\nstring.", + "builtins.str.replace" => "Return a copy with all occurrences of substring old replaced by new.\n\n count\n Maximum number of occurrences to replace.\n -1 (the default value) means replace all occurrences.\n\nIf the optional argument count is given, only the first count occurrences are\nreplaced.", + "builtins.str.rfind" => "Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n\nOptional arguments start and end are interpreted as in slice notation.\nReturn -1 on failure.", + "builtins.str.rindex" => "Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n\nOptional arguments start and end are interpreted as in slice notation.\nRaises ValueError when the substring is not found.", + "builtins.str.rjust" => "Return a right-justified string of length width.\n\nPadding is done using the specified fill character (default is a space).", + "builtins.str.rpartition" => "Partition the string into three parts using the given separator.\n\nThis will search for the separator in the string, starting at the end. If\nthe separator is found, returns a 3-tuple containing the part before the\nseparator, the separator itself, and the part after it.\n\nIf the separator is not found, returns a 3-tuple containing two empty strings\nand the original string.", + "builtins.str.rsplit" => "Return a list of the substrings in the string, using sep as the separator string.\n\n sep\n The separator used to split the string.\n\n When set to None (the default value), will split on any whitespace\n character (including \\n \\r \\t \\f and spaces) and will discard\n empty strings from the result.\n maxsplit\n Maximum number of splits.\n -1 (the default value) means no limit.\n\nSplitting starts at the end of the string and works to the front.", + "builtins.str.rstrip" => "Return a copy of the string with trailing whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.", + "builtins.str.split" => "Return a list of the substrings in the string, using sep as the separator string.\n\n sep\n The separator used to split the string.\n\n When set to None (the default value), will split on any whitespace\n character (including \\n \\r \\t \\f and spaces) and will discard\n empty strings from the result.\n maxsplit\n Maximum number of splits.\n -1 (the default value) means no limit.\n\nSplitting starts at the front of the string and works to the end.\n\nNote, str.split() is mainly useful for data that has been intentionally\ndelimited. With natural text that includes punctuation, consider using\nthe regular expression module.", + "builtins.str.splitlines" => "Return a list of the lines in the string, breaking at line boundaries.\n\nLine breaks are not included in the resulting list unless keepends is given and\ntrue.", + "builtins.str.startswith" => "Return True if the string starts with the specified prefix, False otherwise.\n\nprefix\n A string or a tuple of strings to try.\nstart\n Optional start position. Default: start of the string.\nend\n Optional stop position. Default: end of the string.", + "builtins.str.strip" => "Return a copy of the string with leading and trailing whitespace removed.\n\nIf chars is given and not None, remove characters in chars instead.", + "builtins.str.swapcase" => "Convert uppercase characters to lowercase and lowercase characters to uppercase.", + "builtins.str.title" => "Return a version of the string where each word is titlecased.\n\nMore specifically, words start with uppercased characters and all remaining\ncased characters have lower case.", + "builtins.str.translate" => "Replace each character in the string using the given translation table.\n\n table\n Translation table, which must be a mapping of Unicode ordinals to\n Unicode ordinals, strings, or None.\n\nThe table must implement lookup/indexing via __getitem__, for instance a\ndictionary or list. If this operation raises LookupError, the character is\nleft untouched. Characters mapped to None are deleted.", + "builtins.str.upper" => "Return a copy of the string converted to uppercase.", + "builtins.str.zfill" => "Pad a numeric string with zeros on the left, to fill a field of the given width.\n\nThe string is never truncated.", + "builtins.str_ascii_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.str_ascii_iterator.__eq__" => "Return self==value.", + "builtins.str_ascii_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.str_ascii_iterator.__ge__" => "Return self>=value.", + "builtins.str_ascii_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.str_ascii_iterator.__getstate__" => "Helper for pickle.", + "builtins.str_ascii_iterator.__gt__" => "Return self>value.", + "builtins.str_ascii_iterator.__hash__" => "Return hash(self).", + "builtins.str_ascii_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.str_ascii_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.str_ascii_iterator.__iter__" => "Implement iter(self).", + "builtins.str_ascii_iterator.__le__" => "Return self<=value.", + "builtins.str_ascii_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.str_ascii_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.str_ascii_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.str_ascii_iterator.__next__" => "Implement next(self).", + "builtins.str_ascii_iterator.__reduce__" => "Return state information for pickling.", + "builtins.str_ascii_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.str_ascii_iterator.__repr__" => "Return repr(self).", + "builtins.str_ascii_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.str_ascii_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.str_ascii_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.str_ascii_iterator.__str__" => "Return str(self).", + "builtins.str_ascii_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.sum" => "Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n\nWhen the iterable is empty, return the start value.\nThis function is intended specifically for use with numeric values and may\nreject non-numeric types.", + "builtins.super" => "super() -> same as super(__class__, )\nsuper(type) -> unbound super object\nsuper(type, obj) -> bound super object; requires isinstance(obj, type)\nsuper(type, type2) -> bound super object; requires issubclass(type2, type)\nTypical use to call a cooperative superclass method:\nclass C(B):\n def meth(self, arg):\n super().meth(arg)\nThis works for class methods too:\nclass C(B):\n @classmethod\n def cmeth(cls, arg):\n super().cmeth(arg)", + "builtins.super.__delattr__" => "Implement delattr(self, name).", + "builtins.super.__eq__" => "Return self==value.", + "builtins.super.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.super.__ge__" => "Return self>=value.", + "builtins.super.__get__" => "Return an attribute of instance, which is of type owner.", + "builtins.super.__getattribute__" => "Return getattr(self, name).", + "builtins.super.__getstate__" => "Helper for pickle.", + "builtins.super.__gt__" => "Return self>value.", + "builtins.super.__hash__" => "Return hash(self).", + "builtins.super.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.super.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.super.__le__" => "Return self<=value.", + "builtins.super.__lt__" => "Return self "Return self!=value.", + "builtins.super.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.super.__reduce__" => "Helper for pickle.", + "builtins.super.__reduce_ex__" => "Helper for pickle.", + "builtins.super.__repr__" => "Return repr(self).", + "builtins.super.__self__" => "the instance invoking super(); may be None", + "builtins.super.__self_class__" => "the type of the instance invoking super(); may be None", + "builtins.super.__setattr__" => "Implement setattr(self, name, value).", + "builtins.super.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.super.__str__" => "Return str(self).", + "builtins.super.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.super.__thisclass__" => "the class invoking super()", + "builtins.tuple" => "Built-in immutable sequence.\n\nIf no argument is given, the constructor returns an empty tuple.\nIf iterable is specified the tuple is initialized from iterable's items.\n\nIf the argument is a tuple, the return value is the same object.", + "builtins.tuple.__add__" => "Return self+value.", + "builtins.tuple.__class_getitem__" => "See PEP 585", + "builtins.tuple.__contains__" => "Return bool(key in self).", + "builtins.tuple.__delattr__" => "Implement delattr(self, name).", + "builtins.tuple.__eq__" => "Return self==value.", + "builtins.tuple.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.tuple.__ge__" => "Return self>=value.", + "builtins.tuple.__getattribute__" => "Return getattr(self, name).", + "builtins.tuple.__getitem__" => "Return self[key].", + "builtins.tuple.__getstate__" => "Helper for pickle.", + "builtins.tuple.__gt__" => "Return self>value.", + "builtins.tuple.__hash__" => "Return hash(self).", + "builtins.tuple.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.tuple.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.tuple.__iter__" => "Implement iter(self).", + "builtins.tuple.__le__" => "Return self<=value.", + "builtins.tuple.__len__" => "Return len(self).", + "builtins.tuple.__lt__" => "Return self "Return self*value.", + "builtins.tuple.__ne__" => "Return self!=value.", + "builtins.tuple.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.tuple.__reduce__" => "Helper for pickle.", + "builtins.tuple.__reduce_ex__" => "Helper for pickle.", + "builtins.tuple.__repr__" => "Return repr(self).", + "builtins.tuple.__rmul__" => "Return value*self.", + "builtins.tuple.__setattr__" => "Implement setattr(self, name, value).", + "builtins.tuple.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.tuple.__str__" => "Return str(self).", + "builtins.tuple.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.tuple.count" => "Return number of occurrences of value.", + "builtins.tuple.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "builtins.tuple_iterator.__delattr__" => "Implement delattr(self, name).", + "builtins.tuple_iterator.__eq__" => "Return self==value.", + "builtins.tuple_iterator.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.tuple_iterator.__ge__" => "Return self>=value.", + "builtins.tuple_iterator.__getattribute__" => "Return getattr(self, name).", + "builtins.tuple_iterator.__getstate__" => "Helper for pickle.", + "builtins.tuple_iterator.__gt__" => "Return self>value.", + "builtins.tuple_iterator.__hash__" => "Return hash(self).", + "builtins.tuple_iterator.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.tuple_iterator.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.tuple_iterator.__iter__" => "Implement iter(self).", + "builtins.tuple_iterator.__le__" => "Return self<=value.", + "builtins.tuple_iterator.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "builtins.tuple_iterator.__lt__" => "Return self "Return self!=value.", + "builtins.tuple_iterator.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.tuple_iterator.__next__" => "Implement next(self).", + "builtins.tuple_iterator.__reduce__" => "Return state information for pickling.", + "builtins.tuple_iterator.__reduce_ex__" => "Helper for pickle.", + "builtins.tuple_iterator.__repr__" => "Return repr(self).", + "builtins.tuple_iterator.__setattr__" => "Implement setattr(self, name, value).", + "builtins.tuple_iterator.__setstate__" => "Set state information for unpickling.", + "builtins.tuple_iterator.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.tuple_iterator.__str__" => "Return str(self).", + "builtins.tuple_iterator.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.type" => "type(object) -> the object's type\ntype(name, bases, dict, **kwds) -> a new type", + "builtins.type.__base__" => "The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.", + "builtins.type.__base__.__delattr__" => "Implement delattr(self, name).", + "builtins.type.__base__.__eq__" => "Return self==value.", + "builtins.type.__base__.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.type.__base__.__ge__" => "Return self>=value.", + "builtins.type.__base__.__getattribute__" => "Return getattr(self, name).", + "builtins.type.__base__.__getstate__" => "Helper for pickle.", + "builtins.type.__base__.__gt__" => "Return self>value.", + "builtins.type.__base__.__hash__" => "Return hash(self).", + "builtins.type.__base__.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.type.__base__.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.type.__base__.__le__" => "Return self<=value.", + "builtins.type.__base__.__lt__" => "Return self "Return self!=value.", + "builtins.type.__base__.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.type.__base__.__reduce__" => "Helper for pickle.", + "builtins.type.__base__.__reduce_ex__" => "Helper for pickle.", + "builtins.type.__base__.__repr__" => "Return repr(self).", + "builtins.type.__base__.__setattr__" => "Implement setattr(self, name, value).", + "builtins.type.__base__.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.type.__base__.__str__" => "Return str(self).", + "builtins.type.__base__.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.type.__call__" => "Call self as a function.", + "builtins.type.__delattr__" => "Implement delattr(self, name).", + "builtins.type.__eq__" => "Return self==value.", + "builtins.type.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.type.__ge__" => "Return self>=value.", + "builtins.type.__getattribute__" => "Return getattr(self, name).", + "builtins.type.__getstate__" => "Helper for pickle.", + "builtins.type.__gt__" => "Return self>value.", + "builtins.type.__hash__" => "Return hash(self).", + "builtins.type.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.type.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.type.__instancecheck__" => "Check if an object is an instance.", + "builtins.type.__le__" => "Return self<=value.", + "builtins.type.__lt__" => "Return self "Return self!=value.", + "builtins.type.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.type.__or__" => "Return self|value.", + "builtins.type.__prepare__" => "Create the namespace for the class statement", + "builtins.type.__reduce__" => "Helper for pickle.", + "builtins.type.__reduce_ex__" => "Helper for pickle.", + "builtins.type.__repr__" => "Return repr(self).", + "builtins.type.__ror__" => "Return value|self.", + "builtins.type.__setattr__" => "Implement setattr(self, name, value).", + "builtins.type.__sizeof__" => "Return memory consumption of the type object.", + "builtins.type.__str__" => "Return str(self).", + "builtins.type.__subclasscheck__" => "Check if a class is a subclass.", + "builtins.type.__subclasses__" => "Return a list of immediate subclasses.", + "builtins.type.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "builtins.type.mro" => "Return a type's method resolution order.", + "builtins.vars" => "vars([object]) -> dictionary\n\nWithout arguments, equivalent to locals().\nWith an argument, equivalent to object.__dict__.", + "builtins.zip" => "The zip object yields n-length tuples, where n is the number of iterables\npassed as positional arguments to zip(). The i-th element in every tuple\ncomes from the i-th iterable argument to zip(). This continues until the\nshortest argument is exhausted.\n\nIf strict is true and one of the arguments is exhausted before the others,\nraise a ValueError.\n\n >>> list(zip('abcdefg', range(3), range(4)))\n [('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]", + "builtins.zip.__delattr__" => "Implement delattr(self, name).", + "builtins.zip.__eq__" => "Return self==value.", + "builtins.zip.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "builtins.zip.__ge__" => "Return self>=value.", + "builtins.zip.__getattribute__" => "Return getattr(self, name).", + "builtins.zip.__getstate__" => "Helper for pickle.", + "builtins.zip.__gt__" => "Return self>value.", + "builtins.zip.__hash__" => "Return hash(self).", + "builtins.zip.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "builtins.zip.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "builtins.zip.__iter__" => "Implement iter(self).", + "builtins.zip.__le__" => "Return self<=value.", + "builtins.zip.__lt__" => "Return self "Return self!=value.", + "builtins.zip.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "builtins.zip.__next__" => "Implement next(self).", + "builtins.zip.__reduce__" => "Return state information for pickling.", + "builtins.zip.__reduce_ex__" => "Helper for pickle.", + "builtins.zip.__repr__" => "Return repr(self).", + "builtins.zip.__setattr__" => "Implement setattr(self, name, value).", + "builtins.zip.__setstate__" => "Set state information for unpickling.", + "builtins.zip.__sizeof__" => "Size of object in memory, in bytes.", + "builtins.zip.__str__" => "Return str(self).", + "builtins.zip.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "cmath" => "This module provides access to mathematical functions for complex\nnumbers.", + "cmath.acos" => "Return the arc cosine of z.", + "cmath.acosh" => "Return the inverse hyperbolic cosine of z.", + "cmath.asin" => "Return the arc sine of z.", + "cmath.asinh" => "Return the inverse hyperbolic sine of z.", + "cmath.atan" => "Return the arc tangent of z.", + "cmath.atanh" => "Return the inverse hyperbolic tangent of z.", + "cmath.cos" => "Return the cosine of z.", + "cmath.cosh" => "Return the hyperbolic cosine of z.", + "cmath.exp" => "Return the exponential value e**z.", + "cmath.isclose" => "Determine whether two complex numbers are close in value.\n\n rel_tol\n maximum difference for being considered \"close\", relative to the\n magnitude of the input values\n abs_tol\n maximum difference for being considered \"close\", regardless of the\n magnitude of the input values\n\nReturn True if a is close in value to b, and False otherwise.\n\nFor the values to be considered close, the difference between them must be\nsmaller than at least one of the tolerances.\n\n-inf, inf and NaN behave similarly to the IEEE 754 Standard. That is, NaN is\nnot close to anything, even itself. inf and -inf are only close to themselves.", + "cmath.isfinite" => "Return True if both the real and imaginary parts of z are finite, else False.", + "cmath.isinf" => "Checks if the real or imaginary part of z is infinite.", + "cmath.isnan" => "Checks if the real or imaginary part of z not a number (NaN).", + "cmath.log" => "log(z[, base]) -> the logarithm of z to the given base.\n\nIf the base is not specified, returns the natural logarithm (base e) of z.", + "cmath.log10" => "Return the base-10 logarithm of z.", + "cmath.phase" => "Return argument, also known as the phase angle, of a complex.", + "cmath.polar" => "Convert a complex from rectangular coordinates to polar coordinates.\n\nr is the distance from 0 and phi the phase angle.", + "cmath.rect" => "Convert from polar coordinates to rectangular coordinates.", + "cmath.sin" => "Return the sine of z.", + "cmath.sinh" => "Return the hyperbolic sine of z.", + "cmath.sqrt" => "Return the square root of z.", + "cmath.tan" => "Return the tangent of z.", + "cmath.tanh" => "Return the hyperbolic tangent of z.", + "errno" => "This module makes available standard errno system symbols.\n\nThe value of each symbol is the corresponding integer value,\ne.g., on most systems, errno.ENOENT equals the integer 2.\n\nThe dictionary errno.errorcode maps numeric codes to symbol names,\ne.g., errno.errorcode[2] could be the string 'ENOENT'.\n\nSymbols that are not relevant to the underlying system are not defined.\n\nTo map error codes to error messages, use the function os.strerror(),\ne.g. os.strerror(2) could return 'No such file or directory'.", + "faulthandler" => "faulthandler module.", + "faulthandler._fatal_error_c_thread" => "Call Py_FatalError() in a new C thread.", + "faulthandler._raise_exception" => "Call RaiseException(code, flags).", + "faulthandler._read_null" => "Read from NULL, raise a SIGSEGV or SIGBUS signal depending on the platform.", + "faulthandler._sigabrt" => "Raise a SIGABRT signal.", + "faulthandler._sigfpe" => "Raise a SIGFPE signal.", + "faulthandler._sigsegv" => "Raise a SIGSEGV signal.", + "faulthandler._stack_overflow" => "Recursive call to raise a stack overflow.", + "faulthandler.cancel_dump_traceback_later" => "Cancel the previous call to dump_traceback_later().", + "faulthandler.disable" => "Disable the fault handler.", + "faulthandler.dump_traceback" => "Dump the traceback of the current thread, or of all threads if all_threads is True, into file.", + "faulthandler.dump_traceback_later" => "Dump the traceback of all threads in timeout seconds,\nor each timeout seconds if repeat is True. If exit is True, call _exit(1) which is not safe.", + "faulthandler.enable" => "Enable the fault handler.", + "faulthandler.is_enabled" => "Check if the handler is enabled.", + "faulthandler.register" => "Register a handler for the signal 'signum': dump the traceback of the current thread, or of all threads if all_threads is True, into file.", + "faulthandler.unregister" => "Unregister the handler of the signal 'signum' registered by register().", + "fcntl" => "This module performs file control and I/O control on file\ndescriptors. It is an interface to the fcntl() and ioctl() Unix\nroutines. File descriptors can be obtained with the fileno() method of\na file or socket object.", + "fcntl.fcntl" => "Perform the operation `cmd` on file descriptor fd.\n\nThe values used for `cmd` are operating system dependent, and are available\nas constants in the fcntl module, using the same names as used in\nthe relevant C header files. The argument arg is optional, and\ndefaults to 0; it may be an int or a string. If arg is given as a string,\nthe return value of fcntl is a string of that length, containing the\nresulting value put in the arg buffer by the operating system. The length\nof the arg string is not allowed to exceed 1024 bytes. If the arg given\nis an integer or if none is specified, the result value is an integer\ncorresponding to the return value of the fcntl call in the C code.", + "fcntl.flock" => "Perform the lock operation `operation` on file descriptor `fd`.\n\nSee the Unix manual page for flock(2) for details (On some systems, this\nfunction is emulated using fcntl()).", + "fcntl.ioctl" => "Perform the operation `request` on file descriptor `fd`.\n\nThe values used for `request` are operating system dependent, and are available\nas constants in the fcntl or termios library modules, using the same names as\nused in the relevant C header files.\n\nThe argument `arg` is optional, and defaults to 0; it may be an int or a\nbuffer containing character data (most likely a string or an array).\n\nIf the argument is a mutable buffer (such as an array) and if the\nmutate_flag argument (which is only allowed in this case) is true then the\nbuffer is (in effect) passed to the operating system and changes made by\nthe OS will be reflected in the contents of the buffer after the call has\nreturned. The return value is the integer returned by the ioctl system\ncall.\n\nIf the argument is a mutable buffer and the mutable_flag argument is false,\nthe behavior is as if a string had been passed.\n\nIf the argument is an immutable buffer (most likely a string) then a copy\nof the buffer is passed to the operating system and the return value is a\nstring of the same length containing whatever the operating system put in\nthe buffer. The length of the arg buffer in this case is not allowed to\nexceed 1024 bytes.\n\nIf the arg given is an integer or if none is specified, the result value is\nan integer corresponding to the return value of the ioctl call in the C\ncode.", + "fcntl.lockf" => "A wrapper around the fcntl() locking calls.\n\n`fd` is the file descriptor of the file to lock or unlock, and operation is one\nof the following values:\n\n LOCK_UN - unlock\n LOCK_SH - acquire a shared lock\n LOCK_EX - acquire an exclusive lock\n\nWhen operation is LOCK_SH or LOCK_EX, it can also be bitwise ORed with\nLOCK_NB to avoid blocking on lock acquisition. If LOCK_NB is used and the\nlock cannot be acquired, an OSError will be raised and the exception will\nhave an errno attribute set to EACCES or EAGAIN (depending on the operating\nsystem -- for portability, check for either value).\n\n`len` is the number of bytes to lock, with the default meaning to lock to\nEOF. `start` is the byte offset, relative to `whence`, to that the lock\nstarts. `whence` is as with fileobj.seek(), specifically:\n\n 0 - relative to the start of the file (SEEK_SET)\n 1 - relative to the current buffer position (SEEK_CUR)\n 2 - relative to the end of the file (SEEK_END)", + "gc" => "This module provides access to the garbage collector for reference cycles.\n\nenable() -- Enable automatic garbage collection.\ndisable() -- Disable automatic garbage collection.\nisenabled() -- Returns true if automatic collection is enabled.\ncollect() -- Do a full collection right now.\nget_count() -- Return the current collection counts.\nget_stats() -- Return list of dictionaries containing per-generation stats.\nset_debug() -- Set debugging flags.\nget_debug() -- Get debugging flags.\nset_threshold() -- Set the collection thresholds.\nget_threshold() -- Return the current collection thresholds.\nget_objects() -- Return a list of all objects tracked by the collector.\nis_tracked() -- Returns true if a given object is tracked.\nis_finalized() -- Returns true if a given object has been already finalized.\nget_referrers() -- Return the list of objects that refer to an object.\nget_referents() -- Return the list of objects that an object refers to.\nfreeze() -- Freeze all tracked objects and ignore them for future collections.\nunfreeze() -- Unfreeze all objects in the permanent generation.\nget_freeze_count() -- Return the number of objects in the permanent generation.", + "gc.collect" => "Run the garbage collector.\n\nWith no arguments, run a full collection. The optional argument\nmay be an integer specifying which generation to collect. A ValueError\nis raised if the generation number is invalid.\n\nThe number of unreachable objects is returned.", + "gc.disable" => "Disable automatic garbage collection.", + "gc.enable" => "Enable automatic garbage collection.", + "gc.freeze" => "Freeze all current tracked objects and ignore them for future collections.\n\nThis can be used before a POSIX fork() call to make the gc copy-on-write friendly.\nNote: collection before a POSIX fork() call may free pages for future allocation\nwhich can cause copy-on-write.", + "gc.get_count" => "Return a three-tuple of the current collection counts.", + "gc.get_debug" => "Get the garbage collection debugging flags.", + "gc.get_freeze_count" => "Return the number of objects in the permanent generation.", + "gc.get_objects" => "Return a list of objects tracked by the collector (excluding the list returned).\n\n generation\n Generation to extract the objects from.\n\nIf generation is not None, return only the objects tracked by the collector\nthat are in that generation.", + "gc.get_referents" => "Return the list of objects that are directly referred to by 'objs'.", + "gc.get_referrers" => "Return the list of objects that directly refer to any of 'objs'.", + "gc.get_stats" => "Return a list of dictionaries containing per-generation statistics.", + "gc.get_threshold" => "Return the current collection thresholds.", + "gc.is_finalized" => "Returns true if the object has been already finalized by the GC.", + "gc.is_tracked" => "Returns true if the object is tracked by the garbage collector.\n\nSimple atomic objects will return false.", + "gc.isenabled" => "Returns true if automatic garbage collection is enabled.", + "gc.set_debug" => "Set the garbage collection debugging flags.\n\n flags\n An integer that can have the following bits turned on:\n DEBUG_STATS - Print statistics during collection.\n DEBUG_COLLECTABLE - Print collectable objects found.\n DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects\n found.\n DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n DEBUG_LEAK - Debug leaking programs (everything but STATS).\n\nDebugging information is written to sys.stderr.", + "gc.set_threshold" => "set_threshold(threshold0, [threshold1, [threshold2]])\nSet the collection thresholds (the collection frequency).\n\nSetting 'threshold0' to zero disables collection.", + "gc.unfreeze" => "Unfreeze all objects in the permanent generation.\n\nPut all objects in the permanent generation back into oldest generation.", + "grp" => "Access to the Unix group database.\n\nGroup entries are reported as 4-tuples containing the following fields\nfrom the group database, in order:\n\n gr_name - name of the group\n gr_passwd - group password (encrypted); often empty\n gr_gid - numeric ID of the group\n gr_mem - list of members\n\nThe gid is an integer, name and password are strings. (Note that most\nusers are not explicitly listed as members of the groups they are in\naccording to the password database. Check both databases to get\ncomplete membership information.)", + "grp.getgrall" => "Return a list of all available group entries, in arbitrary order.\n\nAn entry whose name starts with '+' or '-' represents an instruction\nto use YP/NIS and may not be accessible via getgrnam or getgrgid.", + "grp.getgrgid" => "Return the group database entry for the given numeric group ID.\n\nIf id is not valid, raise KeyError.", + "grp.getgrnam" => "Return the group database entry for the given group name.\n\nIf name is not valid, raise KeyError.", + "grp.struct_group" => "grp.struct_group: Results from getgr*() routines.\n\nThis object may be accessed either as a tuple of\n (gr_name,gr_passwd,gr_gid,gr_mem)\nor via the object attributes as named in the above tuple.", + "grp.struct_group.__add__" => "Return self+value.", + "grp.struct_group.__class_getitem__" => "See PEP 585", + "grp.struct_group.__contains__" => "Return bool(key in self).", + "grp.struct_group.__delattr__" => "Implement delattr(self, name).", + "grp.struct_group.__eq__" => "Return self==value.", + "grp.struct_group.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "grp.struct_group.__ge__" => "Return self>=value.", + "grp.struct_group.__getattribute__" => "Return getattr(self, name).", + "grp.struct_group.__getitem__" => "Return self[key].", + "grp.struct_group.__getstate__" => "Helper for pickle.", + "grp.struct_group.__gt__" => "Return self>value.", + "grp.struct_group.__hash__" => "Return hash(self).", + "grp.struct_group.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "grp.struct_group.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "grp.struct_group.__iter__" => "Implement iter(self).", + "grp.struct_group.__le__" => "Return self<=value.", + "grp.struct_group.__len__" => "Return len(self).", + "grp.struct_group.__lt__" => "Return self "Return self*value.", + "grp.struct_group.__ne__" => "Return self!=value.", + "grp.struct_group.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "grp.struct_group.__reduce_ex__" => "Helper for pickle.", + "grp.struct_group.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "grp.struct_group.__repr__" => "Return repr(self).", + "grp.struct_group.__rmul__" => "Return value*self.", + "grp.struct_group.__setattr__" => "Implement setattr(self, name, value).", + "grp.struct_group.__sizeof__" => "Size of object in memory, in bytes.", + "grp.struct_group.__str__" => "Return str(self).", + "grp.struct_group.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "grp.struct_group.count" => "Return number of occurrences of value.", + "grp.struct_group.gr_gid" => "group id", + "grp.struct_group.gr_mem" => "group members", + "grp.struct_group.gr_name" => "group name", + "grp.struct_group.gr_passwd" => "password", + "grp.struct_group.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "itertools" => "Functional tools for creating and using iterators.\n\nInfinite iterators:\ncount(start=0, step=1) --> start, start+step, start+2*step, ...\ncycle(p) --> p0, p1, ... plast, p0, p1, ...\nrepeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times\n\nIterators terminating on the shortest input sequence:\naccumulate(p[, func]) --> p0, p0+p1, p0+p1+p2\nbatched(p, n) --> [p0, p1, ..., p_n-1], [p_n, p_n+1, ..., p_2n-1], ...\nchain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...\nchain.from_iterable([p, q, ...]) --> p0, p1, ... plast, q0, q1, ...\ncompress(data, selectors) --> (d[0] if s[0]), (d[1] if s[1]), ...\ndropwhile(predicate, seq) --> seq[n], seq[n+1], starting when predicate fails\ngroupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)\nfilterfalse(predicate, seq) --> elements of seq where predicate(elem) is False\nislice(seq, [start,] stop [, step]) --> elements from\n seq[start:stop:step]\npairwise(s) --> (s[0],s[1]), (s[1],s[2]), (s[2], s[3]), ...\nstarmap(fun, seq) --> fun(*seq[0]), fun(*seq[1]), ...\ntee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n\ntakewhile(predicate, seq) --> seq[0], seq[1], until predicate fails\nzip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...\n\nCombinatoric generators:\nproduct(p, q, ... [repeat=1]) --> cartesian product\npermutations(p[, r])\ncombinations(p, r)\ncombinations_with_replacement(p, r)", + "itertools._grouper.__delattr__" => "Implement delattr(self, name).", + "itertools._grouper.__eq__" => "Return self==value.", + "itertools._grouper.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools._grouper.__ge__" => "Return self>=value.", + "itertools._grouper.__getattribute__" => "Return getattr(self, name).", + "itertools._grouper.__getstate__" => "Helper for pickle.", + "itertools._grouper.__gt__" => "Return self>value.", + "itertools._grouper.__hash__" => "Return hash(self).", + "itertools._grouper.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools._grouper.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools._grouper.__iter__" => "Implement iter(self).", + "itertools._grouper.__le__" => "Return self<=value.", + "itertools._grouper.__lt__" => "Return self "Return self!=value.", + "itertools._grouper.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools._grouper.__next__" => "Implement next(self).", + "itertools._grouper.__reduce__" => "Return state information for pickling.", + "itertools._grouper.__reduce_ex__" => "Helper for pickle.", + "itertools._grouper.__repr__" => "Return repr(self).", + "itertools._grouper.__setattr__" => "Implement setattr(self, name, value).", + "itertools._grouper.__sizeof__" => "Size of object in memory, in bytes.", + "itertools._grouper.__str__" => "Return str(self).", + "itertools._grouper.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools._tee" => "Iterator wrapped to make it copyable.", + "itertools._tee.__copy__" => "Returns an independent iterator.", + "itertools._tee.__delattr__" => "Implement delattr(self, name).", + "itertools._tee.__eq__" => "Return self==value.", + "itertools._tee.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools._tee.__ge__" => "Return self>=value.", + "itertools._tee.__getattribute__" => "Return getattr(self, name).", + "itertools._tee.__getstate__" => "Helper for pickle.", + "itertools._tee.__gt__" => "Return self>value.", + "itertools._tee.__hash__" => "Return hash(self).", + "itertools._tee.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools._tee.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools._tee.__iter__" => "Implement iter(self).", + "itertools._tee.__le__" => "Return self<=value.", + "itertools._tee.__lt__" => "Return self "Return self!=value.", + "itertools._tee.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools._tee.__next__" => "Implement next(self).", + "itertools._tee.__reduce__" => "Return state information for pickling.", + "itertools._tee.__reduce_ex__" => "Helper for pickle.", + "itertools._tee.__repr__" => "Return repr(self).", + "itertools._tee.__setattr__" => "Implement setattr(self, name, value).", + "itertools._tee.__setstate__" => "Set state information for unpickling.", + "itertools._tee.__sizeof__" => "Size of object in memory, in bytes.", + "itertools._tee.__str__" => "Return str(self).", + "itertools._tee.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools._tee_dataobject" => "teedataobject(iterable, values, next, /)\n--\n\nData container common to multiple tee objects.", + "itertools._tee_dataobject.__delattr__" => "Implement delattr(self, name).", + "itertools._tee_dataobject.__eq__" => "Return self==value.", + "itertools._tee_dataobject.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools._tee_dataobject.__ge__" => "Return self>=value.", + "itertools._tee_dataobject.__getattribute__" => "Return getattr(self, name).", + "itertools._tee_dataobject.__getstate__" => "Helper for pickle.", + "itertools._tee_dataobject.__gt__" => "Return self>value.", + "itertools._tee_dataobject.__hash__" => "Return hash(self).", + "itertools._tee_dataobject.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools._tee_dataobject.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools._tee_dataobject.__le__" => "Return self<=value.", + "itertools._tee_dataobject.__lt__" => "Return self "Return self!=value.", + "itertools._tee_dataobject.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools._tee_dataobject.__reduce__" => "Return state information for pickling.", + "itertools._tee_dataobject.__reduce_ex__" => "Helper for pickle.", + "itertools._tee_dataobject.__repr__" => "Return repr(self).", + "itertools._tee_dataobject.__setattr__" => "Implement setattr(self, name, value).", + "itertools._tee_dataobject.__sizeof__" => "Size of object in memory, in bytes.", + "itertools._tee_dataobject.__str__" => "Return str(self).", + "itertools._tee_dataobject.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.accumulate" => "Return series of accumulated sums (or other binary function results).", + "itertools.accumulate.__delattr__" => "Implement delattr(self, name).", + "itertools.accumulate.__eq__" => "Return self==value.", + "itertools.accumulate.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.accumulate.__ge__" => "Return self>=value.", + "itertools.accumulate.__getattribute__" => "Return getattr(self, name).", + "itertools.accumulate.__getstate__" => "Helper for pickle.", + "itertools.accumulate.__gt__" => "Return self>value.", + "itertools.accumulate.__hash__" => "Return hash(self).", + "itertools.accumulate.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.accumulate.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.accumulate.__iter__" => "Implement iter(self).", + "itertools.accumulate.__le__" => "Return self<=value.", + "itertools.accumulate.__lt__" => "Return self "Return self!=value.", + "itertools.accumulate.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.accumulate.__next__" => "Implement next(self).", + "itertools.accumulate.__reduce__" => "Return state information for pickling.", + "itertools.accumulate.__reduce_ex__" => "Helper for pickle.", + "itertools.accumulate.__repr__" => "Return repr(self).", + "itertools.accumulate.__setattr__" => "Implement setattr(self, name, value).", + "itertools.accumulate.__setstate__" => "Set state information for unpickling.", + "itertools.accumulate.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.accumulate.__str__" => "Return str(self).", + "itertools.accumulate.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.batched" => "Batch data into tuples of length n. The last batch may be shorter than n.\n\nLoops over the input iterable and accumulates data into tuples\nup to size n. The input is consumed lazily, just enough to\nfill a batch. The result is yielded as soon as a batch is full\nor when the input iterable is exhausted.\n\n >>> for batch in batched('ABCDEFG', 3):\n ... print(batch)\n ...\n ('A', 'B', 'C')\n ('D', 'E', 'F')\n ('G',)\n\nIf \"strict\" is True, raises a ValueError if the final batch is shorter\nthan n.", + "itertools.batched.__delattr__" => "Implement delattr(self, name).", + "itertools.batched.__eq__" => "Return self==value.", + "itertools.batched.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.batched.__ge__" => "Return self>=value.", + "itertools.batched.__getattribute__" => "Return getattr(self, name).", + "itertools.batched.__getstate__" => "Helper for pickle.", + "itertools.batched.__gt__" => "Return self>value.", + "itertools.batched.__hash__" => "Return hash(self).", + "itertools.batched.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.batched.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.batched.__iter__" => "Implement iter(self).", + "itertools.batched.__le__" => "Return self<=value.", + "itertools.batched.__lt__" => "Return self "Return self!=value.", + "itertools.batched.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.batched.__next__" => "Implement next(self).", + "itertools.batched.__reduce__" => "Helper for pickle.", + "itertools.batched.__reduce_ex__" => "Helper for pickle.", + "itertools.batched.__repr__" => "Return repr(self).", + "itertools.batched.__setattr__" => "Implement setattr(self, name, value).", + "itertools.batched.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.batched.__str__" => "Return str(self).", + "itertools.batched.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.chain" => "Return a chain object whose .__next__() method returns elements from the\nfirst iterable until it is exhausted, then elements from the next\niterable, until all of the iterables are exhausted.", + "itertools.chain.__class_getitem__" => "See PEP 585", + "itertools.chain.__delattr__" => "Implement delattr(self, name).", + "itertools.chain.__eq__" => "Return self==value.", + "itertools.chain.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.chain.__ge__" => "Return self>=value.", + "itertools.chain.__getattribute__" => "Return getattr(self, name).", + "itertools.chain.__getstate__" => "Helper for pickle.", + "itertools.chain.__gt__" => "Return self>value.", + "itertools.chain.__hash__" => "Return hash(self).", + "itertools.chain.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.chain.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.chain.__iter__" => "Implement iter(self).", + "itertools.chain.__le__" => "Return self<=value.", + "itertools.chain.__lt__" => "Return self "Return self!=value.", + "itertools.chain.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.chain.__next__" => "Implement next(self).", + "itertools.chain.__reduce__" => "Return state information for pickling.", + "itertools.chain.__reduce_ex__" => "Helper for pickle.", + "itertools.chain.__repr__" => "Return repr(self).", + "itertools.chain.__setattr__" => "Implement setattr(self, name, value).", + "itertools.chain.__setstate__" => "Set state information for unpickling.", + "itertools.chain.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.chain.__str__" => "Return str(self).", + "itertools.chain.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.chain.from_iterable" => "Alternative chain() constructor taking a single iterable argument that evaluates lazily.", + "itertools.combinations" => "Return successive r-length combinations of elements in the iterable.\n\ncombinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3)", + "itertools.combinations.__delattr__" => "Implement delattr(self, name).", + "itertools.combinations.__eq__" => "Return self==value.", + "itertools.combinations.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.combinations.__ge__" => "Return self>=value.", + "itertools.combinations.__getattribute__" => "Return getattr(self, name).", + "itertools.combinations.__getstate__" => "Helper for pickle.", + "itertools.combinations.__gt__" => "Return self>value.", + "itertools.combinations.__hash__" => "Return hash(self).", + "itertools.combinations.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.combinations.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.combinations.__iter__" => "Implement iter(self).", + "itertools.combinations.__le__" => "Return self<=value.", + "itertools.combinations.__lt__" => "Return self "Return self!=value.", + "itertools.combinations.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.combinations.__next__" => "Implement next(self).", + "itertools.combinations.__reduce__" => "Return state information for pickling.", + "itertools.combinations.__reduce_ex__" => "Helper for pickle.", + "itertools.combinations.__repr__" => "Return repr(self).", + "itertools.combinations.__setattr__" => "Implement setattr(self, name, value).", + "itertools.combinations.__setstate__" => "Set state information for unpickling.", + "itertools.combinations.__sizeof__" => "Returns size in memory, in bytes.", + "itertools.combinations.__str__" => "Return str(self).", + "itertools.combinations.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.combinations_with_replacement" => "Return successive r-length combinations of elements in the iterable allowing individual elements to have successive repeats.\n\ncombinations_with_replacement('ABC', 2) --> ('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')", + "itertools.combinations_with_replacement.__delattr__" => "Implement delattr(self, name).", + "itertools.combinations_with_replacement.__eq__" => "Return self==value.", + "itertools.combinations_with_replacement.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.combinations_with_replacement.__ge__" => "Return self>=value.", + "itertools.combinations_with_replacement.__getattribute__" => "Return getattr(self, name).", + "itertools.combinations_with_replacement.__getstate__" => "Helper for pickle.", + "itertools.combinations_with_replacement.__gt__" => "Return self>value.", + "itertools.combinations_with_replacement.__hash__" => "Return hash(self).", + "itertools.combinations_with_replacement.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.combinations_with_replacement.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.combinations_with_replacement.__iter__" => "Implement iter(self).", + "itertools.combinations_with_replacement.__le__" => "Return self<=value.", + "itertools.combinations_with_replacement.__lt__" => "Return self "Return self!=value.", + "itertools.combinations_with_replacement.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.combinations_with_replacement.__next__" => "Implement next(self).", + "itertools.combinations_with_replacement.__reduce__" => "Return state information for pickling.", + "itertools.combinations_with_replacement.__reduce_ex__" => "Helper for pickle.", + "itertools.combinations_with_replacement.__repr__" => "Return repr(self).", + "itertools.combinations_with_replacement.__setattr__" => "Implement setattr(self, name, value).", + "itertools.combinations_with_replacement.__setstate__" => "Set state information for unpickling.", + "itertools.combinations_with_replacement.__sizeof__" => "Returns size in memory, in bytes.", + "itertools.combinations_with_replacement.__str__" => "Return str(self).", + "itertools.combinations_with_replacement.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.compress" => "Return data elements corresponding to true selector elements.\n\nForms a shorter iterator from selected data elements using the selectors to\nchoose the data elements.", + "itertools.compress.__delattr__" => "Implement delattr(self, name).", + "itertools.compress.__eq__" => "Return self==value.", + "itertools.compress.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.compress.__ge__" => "Return self>=value.", + "itertools.compress.__getattribute__" => "Return getattr(self, name).", + "itertools.compress.__getstate__" => "Helper for pickle.", + "itertools.compress.__gt__" => "Return self>value.", + "itertools.compress.__hash__" => "Return hash(self).", + "itertools.compress.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.compress.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.compress.__iter__" => "Implement iter(self).", + "itertools.compress.__le__" => "Return self<=value.", + "itertools.compress.__lt__" => "Return self "Return self!=value.", + "itertools.compress.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.compress.__next__" => "Implement next(self).", + "itertools.compress.__reduce__" => "Return state information for pickling.", + "itertools.compress.__reduce_ex__" => "Helper for pickle.", + "itertools.compress.__repr__" => "Return repr(self).", + "itertools.compress.__setattr__" => "Implement setattr(self, name, value).", + "itertools.compress.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.compress.__str__" => "Return str(self).", + "itertools.compress.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.count" => "Return a count object whose .__next__() method returns consecutive values.\n\nEquivalent to:\n def count(firstval=0, step=1):\n x = firstval\n while 1:\n yield x\n x += step", + "itertools.count.__delattr__" => "Implement delattr(self, name).", + "itertools.count.__eq__" => "Return self==value.", + "itertools.count.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.count.__ge__" => "Return self>=value.", + "itertools.count.__getattribute__" => "Return getattr(self, name).", + "itertools.count.__getstate__" => "Helper for pickle.", + "itertools.count.__gt__" => "Return self>value.", + "itertools.count.__hash__" => "Return hash(self).", + "itertools.count.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.count.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.count.__iter__" => "Implement iter(self).", + "itertools.count.__le__" => "Return self<=value.", + "itertools.count.__lt__" => "Return self "Return self!=value.", + "itertools.count.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.count.__next__" => "Implement next(self).", + "itertools.count.__reduce__" => "Return state information for pickling.", + "itertools.count.__reduce_ex__" => "Helper for pickle.", + "itertools.count.__repr__" => "Return repr(self).", + "itertools.count.__setattr__" => "Implement setattr(self, name, value).", + "itertools.count.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.count.__str__" => "Return str(self).", + "itertools.count.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.cycle" => "Return elements from the iterable until it is exhausted. Then repeat the sequence indefinitely.", + "itertools.cycle.__delattr__" => "Implement delattr(self, name).", + "itertools.cycle.__eq__" => "Return self==value.", + "itertools.cycle.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.cycle.__ge__" => "Return self>=value.", + "itertools.cycle.__getattribute__" => "Return getattr(self, name).", + "itertools.cycle.__getstate__" => "Helper for pickle.", + "itertools.cycle.__gt__" => "Return self>value.", + "itertools.cycle.__hash__" => "Return hash(self).", + "itertools.cycle.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.cycle.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.cycle.__iter__" => "Implement iter(self).", + "itertools.cycle.__le__" => "Return self<=value.", + "itertools.cycle.__lt__" => "Return self "Return self!=value.", + "itertools.cycle.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.cycle.__next__" => "Implement next(self).", + "itertools.cycle.__reduce__" => "Return state information for pickling.", + "itertools.cycle.__reduce_ex__" => "Helper for pickle.", + "itertools.cycle.__repr__" => "Return repr(self).", + "itertools.cycle.__setattr__" => "Implement setattr(self, name, value).", + "itertools.cycle.__setstate__" => "Set state information for unpickling.", + "itertools.cycle.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.cycle.__str__" => "Return str(self).", + "itertools.cycle.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.dropwhile" => "Drop items from the iterable while predicate(item) is true.\n\nAfterwards, return every element until the iterable is exhausted.", + "itertools.dropwhile.__delattr__" => "Implement delattr(self, name).", + "itertools.dropwhile.__eq__" => "Return self==value.", + "itertools.dropwhile.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.dropwhile.__ge__" => "Return self>=value.", + "itertools.dropwhile.__getattribute__" => "Return getattr(self, name).", + "itertools.dropwhile.__getstate__" => "Helper for pickle.", + "itertools.dropwhile.__gt__" => "Return self>value.", + "itertools.dropwhile.__hash__" => "Return hash(self).", + "itertools.dropwhile.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.dropwhile.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.dropwhile.__iter__" => "Implement iter(self).", + "itertools.dropwhile.__le__" => "Return self<=value.", + "itertools.dropwhile.__lt__" => "Return self "Return self!=value.", + "itertools.dropwhile.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.dropwhile.__next__" => "Implement next(self).", + "itertools.dropwhile.__reduce__" => "Return state information for pickling.", + "itertools.dropwhile.__reduce_ex__" => "Helper for pickle.", + "itertools.dropwhile.__repr__" => "Return repr(self).", + "itertools.dropwhile.__setattr__" => "Implement setattr(self, name, value).", + "itertools.dropwhile.__setstate__" => "Set state information for unpickling.", + "itertools.dropwhile.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.dropwhile.__str__" => "Return str(self).", + "itertools.dropwhile.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.filterfalse" => "Return those items of iterable for which function(item) is false.\n\nIf function is None, return the items that are false.", + "itertools.filterfalse.__delattr__" => "Implement delattr(self, name).", + "itertools.filterfalse.__eq__" => "Return self==value.", + "itertools.filterfalse.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.filterfalse.__ge__" => "Return self>=value.", + "itertools.filterfalse.__getattribute__" => "Return getattr(self, name).", + "itertools.filterfalse.__getstate__" => "Helper for pickle.", + "itertools.filterfalse.__gt__" => "Return self>value.", + "itertools.filterfalse.__hash__" => "Return hash(self).", + "itertools.filterfalse.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.filterfalse.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.filterfalse.__iter__" => "Implement iter(self).", + "itertools.filterfalse.__le__" => "Return self<=value.", + "itertools.filterfalse.__lt__" => "Return self "Return self!=value.", + "itertools.filterfalse.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.filterfalse.__next__" => "Implement next(self).", + "itertools.filterfalse.__reduce__" => "Return state information for pickling.", + "itertools.filterfalse.__reduce_ex__" => "Helper for pickle.", + "itertools.filterfalse.__repr__" => "Return repr(self).", + "itertools.filterfalse.__setattr__" => "Implement setattr(self, name, value).", + "itertools.filterfalse.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.filterfalse.__str__" => "Return str(self).", + "itertools.filterfalse.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.groupby" => "make an iterator that returns consecutive keys and groups from the iterable\n\niterable\n Elements to divide into groups according to the key function.\nkey\n A function for computing the group category for each element.\n If the key function is not specified or is None, the element itself\n is used for grouping.", + "itertools.groupby.__delattr__" => "Implement delattr(self, name).", + "itertools.groupby.__eq__" => "Return self==value.", + "itertools.groupby.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.groupby.__ge__" => "Return self>=value.", + "itertools.groupby.__getattribute__" => "Return getattr(self, name).", + "itertools.groupby.__getstate__" => "Helper for pickle.", + "itertools.groupby.__gt__" => "Return self>value.", + "itertools.groupby.__hash__" => "Return hash(self).", + "itertools.groupby.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.groupby.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.groupby.__iter__" => "Implement iter(self).", + "itertools.groupby.__le__" => "Return self<=value.", + "itertools.groupby.__lt__" => "Return self "Return self!=value.", + "itertools.groupby.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.groupby.__next__" => "Implement next(self).", + "itertools.groupby.__reduce__" => "Return state information for pickling.", + "itertools.groupby.__reduce_ex__" => "Helper for pickle.", + "itertools.groupby.__repr__" => "Return repr(self).", + "itertools.groupby.__setattr__" => "Implement setattr(self, name, value).", + "itertools.groupby.__setstate__" => "Set state information for unpickling.", + "itertools.groupby.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.groupby.__str__" => "Return str(self).", + "itertools.groupby.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.islice" => "islice(iterable, stop) --> islice object\nislice(iterable, start, stop[, step]) --> islice object\n\nReturn an iterator whose next() method returns selected values from an\niterable. If start is specified, will skip all preceding elements;\notherwise, start defaults to zero. Step defaults to one. If\nspecified as another value, step determines how many values are\nskipped between successive calls. Works like a slice() on a list\nbut returns an iterator.", + "itertools.islice.__delattr__" => "Implement delattr(self, name).", + "itertools.islice.__eq__" => "Return self==value.", + "itertools.islice.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.islice.__ge__" => "Return self>=value.", + "itertools.islice.__getattribute__" => "Return getattr(self, name).", + "itertools.islice.__getstate__" => "Helper for pickle.", + "itertools.islice.__gt__" => "Return self>value.", + "itertools.islice.__hash__" => "Return hash(self).", + "itertools.islice.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.islice.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.islice.__iter__" => "Implement iter(self).", + "itertools.islice.__le__" => "Return self<=value.", + "itertools.islice.__lt__" => "Return self "Return self!=value.", + "itertools.islice.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.islice.__next__" => "Implement next(self).", + "itertools.islice.__reduce__" => "Return state information for pickling.", + "itertools.islice.__reduce_ex__" => "Helper for pickle.", + "itertools.islice.__repr__" => "Return repr(self).", + "itertools.islice.__setattr__" => "Implement setattr(self, name, value).", + "itertools.islice.__setstate__" => "Set state information for unpickling.", + "itertools.islice.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.islice.__str__" => "Return str(self).", + "itertools.islice.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.pairwise" => "Return an iterator of overlapping pairs taken from the input iterator.\n\ns -> (s0,s1), (s1,s2), (s2, s3), ...", + "itertools.pairwise.__delattr__" => "Implement delattr(self, name).", + "itertools.pairwise.__eq__" => "Return self==value.", + "itertools.pairwise.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.pairwise.__ge__" => "Return self>=value.", + "itertools.pairwise.__getattribute__" => "Return getattr(self, name).", + "itertools.pairwise.__getstate__" => "Helper for pickle.", + "itertools.pairwise.__gt__" => "Return self>value.", + "itertools.pairwise.__hash__" => "Return hash(self).", + "itertools.pairwise.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.pairwise.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.pairwise.__iter__" => "Implement iter(self).", + "itertools.pairwise.__le__" => "Return self<=value.", + "itertools.pairwise.__lt__" => "Return self "Return self!=value.", + "itertools.pairwise.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.pairwise.__next__" => "Implement next(self).", + "itertools.pairwise.__reduce__" => "Helper for pickle.", + "itertools.pairwise.__reduce_ex__" => "Helper for pickle.", + "itertools.pairwise.__repr__" => "Return repr(self).", + "itertools.pairwise.__setattr__" => "Implement setattr(self, name, value).", + "itertools.pairwise.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.pairwise.__str__" => "Return str(self).", + "itertools.pairwise.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.permutations" => "Return successive r-length permutations of elements in the iterable.\n\npermutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)", + "itertools.permutations.__delattr__" => "Implement delattr(self, name).", + "itertools.permutations.__eq__" => "Return self==value.", + "itertools.permutations.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.permutations.__ge__" => "Return self>=value.", + "itertools.permutations.__getattribute__" => "Return getattr(self, name).", + "itertools.permutations.__getstate__" => "Helper for pickle.", + "itertools.permutations.__gt__" => "Return self>value.", + "itertools.permutations.__hash__" => "Return hash(self).", + "itertools.permutations.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.permutations.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.permutations.__iter__" => "Implement iter(self).", + "itertools.permutations.__le__" => "Return self<=value.", + "itertools.permutations.__lt__" => "Return self "Return self!=value.", + "itertools.permutations.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.permutations.__next__" => "Implement next(self).", + "itertools.permutations.__reduce__" => "Return state information for pickling.", + "itertools.permutations.__reduce_ex__" => "Helper for pickle.", + "itertools.permutations.__repr__" => "Return repr(self).", + "itertools.permutations.__setattr__" => "Implement setattr(self, name, value).", + "itertools.permutations.__setstate__" => "Set state information for unpickling.", + "itertools.permutations.__sizeof__" => "Returns size in memory, in bytes.", + "itertools.permutations.__str__" => "Return str(self).", + "itertools.permutations.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.product" => "Cartesian product of input iterables. Equivalent to nested for-loops.\n\nFor example, product(A, B) returns the same as: ((x,y) for x in A for y in B).\nThe leftmost iterators are in the outermost for-loop, so the output tuples\ncycle in a manner similar to an odometer (with the rightmost element changing\non every iteration).\n\nTo compute the product of an iterable with itself, specify the number\nof repetitions with the optional repeat keyword argument. For example,\nproduct(A, repeat=4) means the same as product(A, A, A, A).\n\nproduct('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\nproduct((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ...", + "itertools.product.__delattr__" => "Implement delattr(self, name).", + "itertools.product.__eq__" => "Return self==value.", + "itertools.product.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.product.__ge__" => "Return self>=value.", + "itertools.product.__getattribute__" => "Return getattr(self, name).", + "itertools.product.__getstate__" => "Helper for pickle.", + "itertools.product.__gt__" => "Return self>value.", + "itertools.product.__hash__" => "Return hash(self).", + "itertools.product.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.product.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.product.__iter__" => "Implement iter(self).", + "itertools.product.__le__" => "Return self<=value.", + "itertools.product.__lt__" => "Return self "Return self!=value.", + "itertools.product.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.product.__next__" => "Implement next(self).", + "itertools.product.__reduce__" => "Return state information for pickling.", + "itertools.product.__reduce_ex__" => "Helper for pickle.", + "itertools.product.__repr__" => "Return repr(self).", + "itertools.product.__setattr__" => "Implement setattr(self, name, value).", + "itertools.product.__setstate__" => "Set state information for unpickling.", + "itertools.product.__sizeof__" => "Returns size in memory, in bytes.", + "itertools.product.__str__" => "Return str(self).", + "itertools.product.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.repeat" => "repeat(object [,times]) -> create an iterator which returns the object\nfor the specified number of times. If not specified, returns the object\nendlessly.", + "itertools.repeat.__delattr__" => "Implement delattr(self, name).", + "itertools.repeat.__eq__" => "Return self==value.", + "itertools.repeat.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.repeat.__ge__" => "Return self>=value.", + "itertools.repeat.__getattribute__" => "Return getattr(self, name).", + "itertools.repeat.__getstate__" => "Helper for pickle.", + "itertools.repeat.__gt__" => "Return self>value.", + "itertools.repeat.__hash__" => "Return hash(self).", + "itertools.repeat.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.repeat.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.repeat.__iter__" => "Implement iter(self).", + "itertools.repeat.__le__" => "Return self<=value.", + "itertools.repeat.__length_hint__" => "Private method returning an estimate of len(list(it)).", + "itertools.repeat.__lt__" => "Return self "Return self!=value.", + "itertools.repeat.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.repeat.__next__" => "Implement next(self).", + "itertools.repeat.__reduce__" => "Return state information for pickling.", + "itertools.repeat.__reduce_ex__" => "Helper for pickle.", + "itertools.repeat.__repr__" => "Return repr(self).", + "itertools.repeat.__setattr__" => "Implement setattr(self, name, value).", + "itertools.repeat.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.repeat.__str__" => "Return str(self).", + "itertools.repeat.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.starmap" => "Return an iterator whose values are returned from the function evaluated with an argument tuple taken from the given sequence.", + "itertools.starmap.__delattr__" => "Implement delattr(self, name).", + "itertools.starmap.__eq__" => "Return self==value.", + "itertools.starmap.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.starmap.__ge__" => "Return self>=value.", + "itertools.starmap.__getattribute__" => "Return getattr(self, name).", + "itertools.starmap.__getstate__" => "Helper for pickle.", + "itertools.starmap.__gt__" => "Return self>value.", + "itertools.starmap.__hash__" => "Return hash(self).", + "itertools.starmap.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.starmap.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.starmap.__iter__" => "Implement iter(self).", + "itertools.starmap.__le__" => "Return self<=value.", + "itertools.starmap.__lt__" => "Return self "Return self!=value.", + "itertools.starmap.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.starmap.__next__" => "Implement next(self).", + "itertools.starmap.__reduce__" => "Return state information for pickling.", + "itertools.starmap.__reduce_ex__" => "Helper for pickle.", + "itertools.starmap.__repr__" => "Return repr(self).", + "itertools.starmap.__setattr__" => "Implement setattr(self, name, value).", + "itertools.starmap.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.starmap.__str__" => "Return str(self).", + "itertools.starmap.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.takewhile" => "Return successive entries from an iterable as long as the predicate evaluates to true for each entry.", + "itertools.takewhile.__delattr__" => "Implement delattr(self, name).", + "itertools.takewhile.__eq__" => "Return self==value.", + "itertools.takewhile.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.takewhile.__ge__" => "Return self>=value.", + "itertools.takewhile.__getattribute__" => "Return getattr(self, name).", + "itertools.takewhile.__getstate__" => "Helper for pickle.", + "itertools.takewhile.__gt__" => "Return self>value.", + "itertools.takewhile.__hash__" => "Return hash(self).", + "itertools.takewhile.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.takewhile.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.takewhile.__iter__" => "Implement iter(self).", + "itertools.takewhile.__le__" => "Return self<=value.", + "itertools.takewhile.__lt__" => "Return self "Return self!=value.", + "itertools.takewhile.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.takewhile.__next__" => "Implement next(self).", + "itertools.takewhile.__reduce__" => "Return state information for pickling.", + "itertools.takewhile.__reduce_ex__" => "Helper for pickle.", + "itertools.takewhile.__repr__" => "Return repr(self).", + "itertools.takewhile.__setattr__" => "Implement setattr(self, name, value).", + "itertools.takewhile.__setstate__" => "Set state information for unpickling.", + "itertools.takewhile.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.takewhile.__str__" => "Return str(self).", + "itertools.takewhile.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "itertools.tee" => "Returns a tuple of n independent iterators.", + "itertools.zip_longest" => "Return a zip_longest object whose .__next__() method returns a tuple where\nthe i-th element comes from the i-th iterable argument. The .__next__()\nmethod continues until the longest iterable in the argument sequence\nis exhausted and then it raises StopIteration. When the shorter iterables\nare exhausted, the fillvalue is substituted in their place. The fillvalue\ndefaults to None or can be specified by a keyword argument.", + "itertools.zip_longest.__delattr__" => "Implement delattr(self, name).", + "itertools.zip_longest.__eq__" => "Return self==value.", + "itertools.zip_longest.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "itertools.zip_longest.__ge__" => "Return self>=value.", + "itertools.zip_longest.__getattribute__" => "Return getattr(self, name).", + "itertools.zip_longest.__getstate__" => "Helper for pickle.", + "itertools.zip_longest.__gt__" => "Return self>value.", + "itertools.zip_longest.__hash__" => "Return hash(self).", + "itertools.zip_longest.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "itertools.zip_longest.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "itertools.zip_longest.__iter__" => "Implement iter(self).", + "itertools.zip_longest.__le__" => "Return self<=value.", + "itertools.zip_longest.__lt__" => "Return self "Return self!=value.", + "itertools.zip_longest.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "itertools.zip_longest.__next__" => "Implement next(self).", + "itertools.zip_longest.__reduce__" => "Return state information for pickling.", + "itertools.zip_longest.__reduce_ex__" => "Helper for pickle.", + "itertools.zip_longest.__repr__" => "Return repr(self).", + "itertools.zip_longest.__setattr__" => "Implement setattr(self, name, value).", + "itertools.zip_longest.__setstate__" => "Set state information for unpickling.", + "itertools.zip_longest.__sizeof__" => "Size of object in memory, in bytes.", + "itertools.zip_longest.__str__" => "Return str(self).", + "itertools.zip_longest.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "marshal" => "This module contains functions that can read and write Python values in\na binary format. The format is specific to Python, but independent of\nmachine architecture issues.\n\nNot all Python object types are supported; in general, only objects\nwhose value is independent from a particular invocation of Python can be\nwritten and read by this module. The following types are supported:\nNone, integers, floating-point numbers, strings, bytes, bytearrays,\ntuples, lists, sets, dictionaries, and code objects, where it\nshould be understood that tuples, lists and dictionaries are only\nsupported as long as the values contained therein are themselves\nsupported; and recursive lists and dictionaries should not be written\n(they will cause infinite loops).\n\nVariables:\n\nversion -- indicates the format that the module uses. Version 0 is the\n historical format, version 1 shares interned strings and version 2\n uses a binary format for floating-point numbers.\n Version 3 shares common object references (New in version 3.4).\n\nFunctions:\n\ndump() -- write value to a file\nload() -- read value from a file\ndumps() -- marshal value as a bytes object\nloads() -- read value from a bytes-like object", + "marshal.dump" => "Write the value on the open file.\n\n value\n Must be a supported type.\n file\n Must be a writeable binary file.\n version\n Indicates the data format that dump should use.\n allow_code\n Allow to write code objects.\n\nIf the value has (or contains an object that has) an unsupported type, a\nValueError exception is raised - but garbage data will also be written\nto the file. The object will not be properly read back by load().", + "marshal.dumps" => "Return the bytes object that would be written to a file by dump(value, file).\n\n value\n Must be a supported type.\n version\n Indicates the data format that dumps should use.\n allow_code\n Allow to write code objects.\n\nRaise a ValueError exception if value has (or contains an object that has) an\nunsupported type.", + "marshal.load" => "Read one value from the open file and return it.\n\n file\n Must be readable binary file.\n allow_code\n Allow to load code objects.\n\nIf no valid value is read (e.g. because the data has a different Python\nversion's incompatible marshal format), raise EOFError, ValueError or\nTypeError.\n\nNote: If an object containing an unsupported type was marshalled with\ndump(), load() will substitute None for the unmarshallable type.", + "marshal.loads" => "Convert the bytes-like object to a value.\n\n allow_code\n Allow to load code objects.\n\nIf no valid value is found, raise EOFError, ValueError or TypeError. Extra\nbytes in the input are ignored.", + "math" => "This module provides access to the mathematical functions\ndefined by the C standard.", + "math.acos" => "Return the arc cosine (measured in radians) of x.\n\nThe result is between 0 and pi.", + "math.acosh" => "Return the inverse hyperbolic cosine of x.", + "math.asin" => "Return the arc sine (measured in radians) of x.\n\nThe result is between -pi/2 and pi/2.", + "math.asinh" => "Return the inverse hyperbolic sine of x.", + "math.atan" => "Return the arc tangent (measured in radians) of x.\n\nThe result is between -pi/2 and pi/2.", + "math.atan2" => "Return the arc tangent (measured in radians) of y/x.\n\nUnlike atan(y/x), the signs of both x and y are considered.", + "math.atanh" => "Return the inverse hyperbolic tangent of x.", + "math.cbrt" => "Return the cube root of x.", + "math.ceil" => "Return the ceiling of x as an Integral.\n\nThis is the smallest integer >= x.", + "math.comb" => "Number of ways to choose k items from n items without repetition and without order.\n\nEvaluates to n! / (k! * (n - k)!) when k <= n and evaluates\nto zero when k > n.\n\nAlso called the binomial coefficient because it is equivalent\nto the coefficient of k-th term in polynomial expansion of the\nexpression (1 + x)**n.\n\nRaises TypeError if either of the arguments are not integers.\nRaises ValueError if either of the arguments are negative.", + "math.copysign" => "Return a float with the magnitude (absolute value) of x but the sign of y.\n\nOn platforms that support signed zeros, copysign(1.0, -0.0)\nreturns -1.0.", + "math.cos" => "Return the cosine of x (measured in radians).", + "math.cosh" => "Return the hyperbolic cosine of x.", + "math.degrees" => "Convert angle x from radians to degrees.", + "math.dist" => "Return the Euclidean distance between two points p and q.\n\nThe points should be specified as sequences (or iterables) of\ncoordinates. Both inputs must have the same dimension.\n\nRoughly equivalent to:\n sqrt(sum((px - qx) ** 2.0 for px, qx in zip(p, q)))", + "math.erf" => "Error function at x.", + "math.erfc" => "Complementary error function at x.", + "math.exp" => "Return e raised to the power of x.", + "math.exp2" => "Return 2 raised to the power of x.", + "math.expm1" => "Return exp(x)-1.\n\nThis function avoids the loss of precision involved in the direct evaluation of exp(x)-1 for small x.", + "math.fabs" => "Return the absolute value of the float x.", + "math.factorial" => "Find n!.", + "math.floor" => "Return the floor of x as an Integral.\n\nThis is the largest integer <= x.", + "math.fma" => "Fused multiply-add operation.\n\nCompute (x * y) + z with a single round.", + "math.fmod" => "Return fmod(x, y), according to platform C.\n\nx % y may differ.", + "math.frexp" => "Return the mantissa and exponent of x, as pair (m, e).\n\nm is a float and e is an int, such that x = m * 2.**e.\nIf x is 0, m and e are both 0. Else 0.5 <= abs(m) < 1.0.", + "math.fsum" => "Return an accurate floating-point sum of values in the iterable seq.\n\nAssumes IEEE-754 floating-point arithmetic.", + "math.gamma" => "Gamma function at x.", + "math.gcd" => "Greatest Common Divisor.", + "math.hypot" => "hypot(*coordinates) -> value\n\nMultidimensional Euclidean distance from the origin to a point.\n\nRoughly equivalent to:\n sqrt(sum(x**2 for x in coordinates))\n\nFor a two dimensional point (x, y), gives the hypotenuse\nusing the Pythagorean theorem: sqrt(x*x + y*y).\n\nFor example, the hypotenuse of a 3/4/5 right triangle is:\n\n >>> hypot(3.0, 4.0)\n 5.0", + "math.isclose" => "Determine whether two floating-point numbers are close in value.\n\n rel_tol\n maximum difference for being considered \"close\", relative to the\n magnitude of the input values\n abs_tol\n maximum difference for being considered \"close\", regardless of the\n magnitude of the input values\n\nReturn True if a is close in value to b, and False otherwise.\n\nFor the values to be considered close, the difference between them\nmust be smaller than at least one of the tolerances.\n\n-inf, inf and NaN behave similarly to the IEEE 754 Standard. That\nis, NaN is not close to anything, even itself. inf and -inf are\nonly close to themselves.", + "math.isfinite" => "Return True if x is neither an infinity nor a NaN, and False otherwise.", + "math.isinf" => "Return True if x is a positive or negative infinity, and False otherwise.", + "math.isnan" => "Return True if x is a NaN (not a number), and False otherwise.", + "math.isqrt" => "Return the integer part of the square root of the input.", + "math.lcm" => "Least Common Multiple.", + "math.ldexp" => "Return x * (2**i).\n\nThis is essentially the inverse of frexp().", + "math.lgamma" => "Natural logarithm of absolute value of Gamma function at x.", + "math.log" => "log(x, [base=math.e])\nReturn the logarithm of x to the given base.\n\nIf the base is not specified, returns the natural logarithm (base e) of x.", + "math.log10" => "Return the base 10 logarithm of x.", + "math.log1p" => "Return the natural logarithm of 1+x (base e).\n\nThe result is computed in a way which is accurate for x near zero.", + "math.log2" => "Return the base 2 logarithm of x.", + "math.modf" => "Return the fractional and integer parts of x.\n\nBoth results carry the sign of x and are floats.", + "math.nextafter" => "Return the floating-point value the given number of steps after x towards y.\n\nIf steps is not specified or is None, it defaults to 1.\n\nRaises a TypeError, if x or y is not a double, or if steps is not an integer.\nRaises ValueError if steps is negative.", + "math.perm" => "Number of ways to choose k items from n items without repetition and with order.\n\nEvaluates to n! / (n - k)! when k <= n and evaluates\nto zero when k > n.\n\nIf k is not specified or is None, then k defaults to n\nand the function returns n!.\n\nRaises TypeError if either of the arguments are not integers.\nRaises ValueError if either of the arguments are negative.", + "math.pow" => "Return x**y (x to the power of y).", + "math.prod" => "Calculate the product of all the elements in the input iterable.\n\nThe default start value for the product is 1.\n\nWhen the iterable is empty, return the start value. This function is\nintended specifically for use with numeric values and may reject\nnon-numeric types.", + "math.radians" => "Convert angle x from degrees to radians.", + "math.remainder" => "Difference between x and the closest integer multiple of y.\n\nReturn x - n*y where n*y is the closest integer multiple of y.\nIn the case where x is exactly halfway between two multiples of\ny, the nearest even value of n is used. The result is always exact.", + "math.sin" => "Return the sine of x (measured in radians).", + "math.sinh" => "Return the hyperbolic sine of x.", + "math.sqrt" => "Return the square root of x.", + "math.sumprod" => "Return the sum of products of values from two iterables p and q.\n\nRoughly equivalent to:\n\n sum(itertools.starmap(operator.mul, zip(p, q, strict=True)))\n\nFor float and mixed int/float inputs, the intermediate products\nand sums are computed with extended precision.", + "math.tan" => "Return the tangent of x (measured in radians).", + "math.tanh" => "Return the hyperbolic tangent of x.", + "math.trunc" => "Truncates the Real x to the nearest Integral toward 0.\n\nUses the __trunc__ magic method.", + "math.ulp" => "Return the value of the least significant bit of the float x.", + "mmap.mmap" => "Windows: mmap(fileno, length[, tagname[, access[, offset]]])\n\nMaps length bytes from the file specified by the file handle fileno,\nand returns a mmap object. If length is larger than the current size\nof the file, the file is extended to contain length bytes. If length\nis 0, the maximum length of the map is the current size of the file,\nexcept that if the file is empty Windows raises an exception (you cannot\ncreate an empty mapping on Windows).\n\nUnix: mmap(fileno, length[, flags[, prot[, access[, offset[, trackfd]]]]])\n\nMaps length bytes from the file specified by the file descriptor fileno,\nand returns a mmap object. If length is 0, the maximum length of the map\nwill be the current size of the file when mmap is called.\nflags specifies the nature of the mapping. MAP_PRIVATE creates a\nprivate copy-on-write mapping, so changes to the contents of the mmap\nobject will be private to this process, and MAP_SHARED creates a mapping\nthat's shared with all other processes mapping the same areas of the file.\nThe default value is MAP_SHARED.\n\nTo map anonymous memory, pass -1 as the fileno (both versions).", + "mmap.mmap.__buffer__" => "Return a buffer object that exposes the underlying memory of the object.", + "mmap.mmap.__delattr__" => "Implement delattr(self, name).", + "mmap.mmap.__delitem__" => "Delete self[key].", + "mmap.mmap.__eq__" => "Return self==value.", + "mmap.mmap.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "mmap.mmap.__ge__" => "Return self>=value.", + "mmap.mmap.__getattribute__" => "Return getattr(self, name).", + "mmap.mmap.__getitem__" => "Return self[key].", + "mmap.mmap.__getstate__" => "Helper for pickle.", + "mmap.mmap.__gt__" => "Return self>value.", + "mmap.mmap.__hash__" => "Return hash(self).", + "mmap.mmap.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "mmap.mmap.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "mmap.mmap.__le__" => "Return self<=value.", + "mmap.mmap.__len__" => "Return len(self).", + "mmap.mmap.__lt__" => "Return self "Return self!=value.", + "mmap.mmap.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "mmap.mmap.__reduce__" => "Helper for pickle.", + "mmap.mmap.__reduce_ex__" => "Helper for pickle.", + "mmap.mmap.__release_buffer__" => "Release the buffer object that exposes the underlying memory of the object.", + "mmap.mmap.__repr__" => "Return repr(self).", + "mmap.mmap.__setattr__" => "Implement setattr(self, name, value).", + "mmap.mmap.__setitem__" => "Set self[key] to value.", + "mmap.mmap.__sizeof__" => "Size of object in memory, in bytes.", + "mmap.mmap.__str__" => "Return str(self).", + "mmap.mmap.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "msvcrt.GetErrorMode" => "Wrapper around GetErrorMode.", + "msvcrt.SetErrorMode" => "Wrapper around SetErrorMode.", + "msvcrt.get_osfhandle" => "Return the file handle for the file descriptor fd.\n\nRaises OSError if fd is not recognized.", + "msvcrt.getch" => "Read a keypress and return the resulting character as a byte string.\n\nNothing is echoed to the console. This call will block if a keypress is\nnot already available, but will not wait for Enter to be pressed. If the\npressed key was a special function key, this will return '\\000' or\n'\\xe0'; the next call will return the keycode. The Control-C keypress\ncannot be read with this function.", + "msvcrt.getche" => "Similar to getch(), but the keypress will be echoed if possible.", + "msvcrt.getwch" => "Wide char variant of getch(), returning a Unicode value.", + "msvcrt.getwche" => "Wide char variant of getche(), returning a Unicode value.", + "msvcrt.heapmin" => "Minimize the malloc() heap.\n\nForce the malloc() heap to clean itself up and return unused blocks\nto the operating system. On failure, this raises OSError.", + "msvcrt.kbhit" => "Returns a nonzero value if a keypress is waiting to be read. Otherwise, return 0.", + "msvcrt.locking" => "Lock part of a file based on file descriptor fd from the C runtime.\n\nRaises OSError on failure. The locked region of the file extends from\nthe current file position for nbytes bytes, and may continue beyond\nthe end of the file. mode must be one of the LK_* constants listed\nbelow. Multiple regions in a file may be locked at the same time, but\nmay not overlap. Adjacent regions are not merged; they must be unlocked\nindividually.", + "msvcrt.open_osfhandle" => "Create a C runtime file descriptor from the file handle handle.\n\nThe flags parameter should be a bitwise OR of os.O_APPEND, os.O_RDONLY,\nand os.O_TEXT. The returned file descriptor may be used as a parameter\nto os.fdopen() to create a file object.", + "msvcrt.putch" => "Print the byte string char to the console without buffering.", + "msvcrt.putwch" => "Wide char variant of putch(), accepting a Unicode value.", + "msvcrt.setmode" => "Set the line-end translation mode for the file descriptor fd.\n\nTo set it to text mode, flags should be os.O_TEXT; for binary, it\nshould be os.O_BINARY.\n\nReturn value is the previous mode.", + "msvcrt.ungetch" => "Opposite of getch.\n\nCause the byte string char to be \"pushed back\" into the\nconsole buffer; it will be the next character read by\ngetch() or getche().", + "msvcrt.ungetwch" => "Wide char variant of ungetch(), accepting a Unicode value.", + "nt" => "This module provides access to operating system functionality that is\nstandardized by the C Standard and the POSIX standard (a thinly\ndisguised Unix interface). Refer to the library manual and\ncorresponding Unix manual entries for more information on calls.", + "nt.DirEntry.__class_getitem__" => "See PEP 585", + "nt.DirEntry.__delattr__" => "Implement delattr(self, name).", + "nt.DirEntry.__eq__" => "Return self==value.", + "nt.DirEntry.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "nt.DirEntry.__fspath__" => "Returns the path for the entry.", + "nt.DirEntry.__ge__" => "Return self>=value.", + "nt.DirEntry.__getattribute__" => "Return getattr(self, name).", + "nt.DirEntry.__getstate__" => "Helper for pickle.", + "nt.DirEntry.__gt__" => "Return self>value.", + "nt.DirEntry.__hash__" => "Return hash(self).", + "nt.DirEntry.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "nt.DirEntry.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "nt.DirEntry.__le__" => "Return self<=value.", + "nt.DirEntry.__lt__" => "Return self "Return self!=value.", + "nt.DirEntry.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "nt.DirEntry.__reduce__" => "Helper for pickle.", + "nt.DirEntry.__reduce_ex__" => "Helper for pickle.", + "nt.DirEntry.__repr__" => "Return repr(self).", + "nt.DirEntry.__setattr__" => "Implement setattr(self, name, value).", + "nt.DirEntry.__sizeof__" => "Size of object in memory, in bytes.", + "nt.DirEntry.__str__" => "Return str(self).", + "nt.DirEntry.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "nt.DirEntry.inode" => "Return inode of the entry; cached per entry.", + "nt.DirEntry.is_dir" => "Return True if the entry is a directory; cached per entry.", + "nt.DirEntry.is_file" => "Return True if the entry is a file; cached per entry.", + "nt.DirEntry.is_junction" => "Return True if the entry is a junction; cached per entry.", + "nt.DirEntry.is_symlink" => "Return True if the entry is a symbolic link; cached per entry.", + "nt.DirEntry.name" => "the entry's base filename, relative to scandir() \"path\" argument", + "nt.DirEntry.path" => "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)", + "nt.DirEntry.stat" => "Return stat_result object for the entry; cached per entry.", + "nt._add_dll_directory" => "Add a path to the DLL search path.\n\nThis search path is used when resolving dependencies for imported\nextension modules (the module itself is resolved through sys.path),\nand also by ctypes.\n\nReturns an opaque value that may be passed to os.remove_dll_directory\nto remove this directory from the search path.", + "nt._exit" => "Exit to the system with specified status, without normal exit processing.", + "nt._findfirstfile" => "A function to get the real file name without accessing the file in Windows.", + "nt._getdiskusage" => "Return disk usage statistics about the given path as a (total, free) tuple.", + "nt._getfinalpathname" => "A helper function for samepath on windows.", + "nt._getvolumepathname" => "A helper function for ismount on Win32.", + "nt._inputhook" => "Calls PyOS_CallInputHook droppong the GIL first", + "nt._is_inputhook_installed" => "Checks if PyOS_CallInputHook is set", + "nt._path_exists" => "Test whether a path exists. Returns False for broken symbolic links.", + "nt._path_isdevdrive" => "Determines whether the specified path is on a Windows Dev Drive.", + "nt._path_isdir" => "Return true if the pathname refers to an existing directory.", + "nt._path_isfile" => "Test whether a path is a regular file", + "nt._path_isjunction" => "Test whether a path is a junction", + "nt._path_islink" => "Test whether a path is a symbolic link", + "nt._path_lexists" => "Test whether a path exists. Returns True for broken symbolic links.", + "nt._path_normpath" => "Normalize path, eliminating double slashes, etc.", + "nt._path_splitroot" => "Removes everything after the root on Win32.", + "nt._path_splitroot_ex" => "Split a pathname into drive, root and tail.\n\nThe tail contains anything after the root.", + "nt._remove_dll_directory" => "Removes a path from the DLL search path.\n\nThe parameter is an opaque value that was returned from\nos.add_dll_directory. You can only remove directories that you added\nyourself.", + "nt._supports_virtual_terminal" => "Checks if virtual terminal is supported in windows", + "nt.abort" => "Abort the interpreter immediately.\n\nThis function 'dumps core' or otherwise fails in the hardest way possible\non the hosting operating system. This function never returns.", + "nt.access" => "Use the real uid/gid to test for access to a path.\n\n path\n Path to be tested; can be string, bytes, or a path-like object.\n mode\n Operating-system mode bitfield. Can be F_OK to test existence,\n or the inclusive-OR of R_OK, W_OK, and X_OK.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that\n directory.\n effective_ids\n If True, access will use the effective uid/gid instead of\n the real uid/gid.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n access will examine the symbolic link itself instead of the file\n the link points to.\n\ndir_fd, effective_ids, and follow_symlinks may not be implemented\n on your platform. If they are unavailable, using them will raise a\n NotImplementedError.\n\nNote that most operations will use the effective uid/gid, therefore this\n routine can be used in a suid/sgid environment to test if the invoking user\n has the specified access to the path.", + "nt.chdir" => "Change the current working directory to the specified path.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\nIf this functionality is unavailable, using it raises an exception.", + "nt.chmod" => "Change the access permissions of a file.\n\n path\n Path to be modified. May always be specified as a str, bytes, or a path-like object.\n On some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.\n mode\n Operating-system mode bitfield.\n Be careful when using number literals for *mode*. The conventional UNIX notation for\n numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n Python.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that\n directory.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n chmod will modify the symbolic link itself instead of the file\n the link points to.\n\nIt is an error to use dir_fd or follow_symlinks when specifying path as\n an open file descriptor.\ndir_fd and follow_symlinks may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "nt.close" => "Close a file descriptor.", + "nt.closerange" => "Closes all file descriptors in [fd_low, fd_high), ignoring errors.", + "nt.cpu_count" => "Return the number of logical CPUs in the system.\n\nReturn None if indeterminable.", + "nt.device_encoding" => "Return a string describing the encoding of a terminal's file descriptor.\n\nThe file descriptor must be attached to a terminal.\nIf the device is not a terminal, return None.", + "nt.dup" => "Return a duplicate of a file descriptor.", + "nt.dup2" => "Duplicate file descriptor.", + "nt.execv" => "Execute an executable path with arguments, replacing current process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.", + "nt.execve" => "Execute an executable path with arguments, replacing current process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.\nenv\n Dictionary of strings mapping to strings.", + "nt.fchmod" => "Change the access permissions of the file given by file descriptor fd.\n\n fd\n The file descriptor of the file to be modified.\n mode\n Operating-system mode bitfield.\n Be careful when using number literals for *mode*. The conventional UNIX notation for\n numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n Python.\n\nEquivalent to os.chmod(fd, mode).", + "nt.fspath" => "Return the file system path representation of the object.\n\nIf the object is str or bytes, then allow it to pass through as-is. If the\nobject defines __fspath__(), then return the result of that method. All other\ntypes raise a TypeError.", + "nt.fstat" => "Perform a stat system call on the given file descriptor.\n\nLike stat(), but for an open file descriptor.\nEquivalent to os.stat(fd).", + "nt.fsync" => "Force write of fd to disk.", + "nt.ftruncate" => "Truncate a file, specified by file descriptor, to a specific length.", + "nt.get_blocking" => "Get the blocking mode of the file descriptor.\n\nReturn False if the O_NONBLOCK flag is set, True if the flag is cleared.", + "nt.get_handle_inheritable" => "Get the close-on-exe flag of the specified file descriptor.", + "nt.get_inheritable" => "Get the close-on-exe flag of the specified file descriptor.", + "nt.get_terminal_size" => "Return the size of the terminal window as (columns, lines).\n\nThe optional argument fd (default standard output) specifies\nwhich file descriptor should be queried.\n\nIf the file descriptor is not connected to a terminal, an OSError\nis thrown.\n\nThis function will only be defined if an implementation is\navailable for this system.\n\nshutil.get_terminal_size is the high-level function which should\nnormally be used, os.get_terminal_size is the low-level implementation.", + "nt.getcwd" => "Return a unicode string representing the current working directory.", + "nt.getcwdb" => "Return a bytes string representing the current working directory.", + "nt.getlogin" => "Return the actual login name.", + "nt.getpid" => "Return the current process id.", + "nt.getppid" => "Return the parent's process id.\n\nIf the parent process has already exited, Windows machines will still\nreturn its id; others systems will return the id of the 'init' process (1).", + "nt.isatty" => "Return True if the fd is connected to a terminal.\n\nReturn True if the file descriptor is an open file descriptor\nconnected to the slave end of a terminal.", + "nt.kill" => "Kill a process with a signal.", + "nt.lchmod" => "Change the access permissions of a file, without following symbolic links.\n\nIf path is a symlink, this affects the link itself rather than the target.\nEquivalent to chmod(path, mode, follow_symlinks=False).\"", + "nt.link" => "Create a hard link to a file.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nIf follow_symlinks is False, and the last element of src is a symbolic\n link, link will create a link to the symbolic link itself instead of the\n file the link points to.\nsrc_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your\n platform. If they are unavailable, using them will raise a\n NotImplementedError.", + "nt.listdir" => "Return a list containing the names of the files in the directory.\n\npath can be specified as either str, bytes, or a path-like object. If path is bytes,\n the filenames returned will also be bytes; in all other circumstances\n the filenames returned will be str.\nIf path is None, uses the path='.'.\nOn some platforms, path may also be specified as an open file descriptor;\\\n the file descriptor must refer to a directory.\n If this functionality is unavailable, using it raises NotImplementedError.\n\nThe list is in arbitrary order. It does not include the special\nentries '.' and '..' even if they are present in the directory.", + "nt.listdrives" => "Return a list containing the names of drives in the system.\n\nA drive name typically looks like 'C:\\\\'.", + "nt.listmounts" => "Return a list containing mount points for a particular volume.\n\n'volume' should be a GUID path as returned from os.listvolumes.", + "nt.listvolumes" => "Return a list containing the volumes in the system.\n\nVolumes are typically represented as a GUID path.", + "nt.lseek" => "Set the position of a file descriptor. Return the new position.\n\n fd\n An open file descriptor, as returned by os.open().\n position\n Position, interpreted relative to 'whence'.\n whence\n The relative position to seek from. Valid values are:\n - SEEK_SET: seek from the start of the file.\n - SEEK_CUR: seek from the current file position.\n - SEEK_END: seek from the end of the file.\n\nThe return value is the number of bytes relative to the beginning of the file.", + "nt.lstat" => "Perform a stat system call on the given path, without following symbolic links.\n\nLike stat(), but do not follow symbolic links.\nEquivalent to stat(path, follow_symlinks=False).", + "nt.mkdir" => "Create a directory.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.\n\nThe mode argument is ignored on Windows. Where it is used, the current umask\nvalue is first masked out.", + "nt.open" => "Open a file for low level IO. Returns a file descriptor (integer).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "nt.pipe" => "Create a pipe.\n\nReturns a tuple of two file descriptors:\n (read_fd, write_fd)", + "nt.putenv" => "Change or add an environment variable.", + "nt.read" => "Read from a file descriptor. Returns a bytes object.", + "nt.readlink" => "Return a string representing the path to which the symbolic link points.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\nand path should be relative; path will then be relative to that directory.\n\ndir_fd may not be implemented on your platform. If it is unavailable,\nusing it will raise a NotImplementedError.", + "nt.remove" => "Remove a file (same as unlink()).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "nt.rename" => "Rename a file or directory.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nsrc_dir_fd and dst_dir_fd, may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "nt.replace" => "Rename a file or directory, overwriting the destination.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nsrc_dir_fd and dst_dir_fd, may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "nt.rmdir" => "Remove a directory.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "nt.scandir" => "Return an iterator of DirEntry objects for given path.\n\npath can be specified as either str, bytes, or a path-like object. If path\nis bytes, the names of yielded DirEntry objects will also be bytes; in\nall other circumstances they will be str.\n\nIf path is None, uses the path='.'.", + "nt.set_blocking" => "Set the blocking mode of the specified file descriptor.\n\nSet the O_NONBLOCK flag if blocking is False,\nclear the O_NONBLOCK flag otherwise.", + "nt.set_handle_inheritable" => "Set the inheritable flag of the specified handle.", + "nt.set_inheritable" => "Set the inheritable flag of the specified file descriptor.", + "nt.spawnv" => "Execute the program specified by path in a new process.\n\nmode\n Mode of process creation.\npath\n Path of executable file.\nargv\n Tuple or list of strings.", + "nt.spawnve" => "Execute the program specified by path in a new process.\n\nmode\n Mode of process creation.\npath\n Path of executable file.\nargv\n Tuple or list of strings.\nenv\n Dictionary of strings mapping to strings.", + "nt.startfile" => "Start a file with its associated application.\n\nWhen \"operation\" is not specified or \"open\", this acts like\ndouble-clicking the file in Explorer, or giving the file name as an\nargument to the DOS \"start\" command: the file is opened with whatever\napplication (if any) its extension is associated.\nWhen another \"operation\" is given, it specifies what should be done with\nthe file. A typical operation is \"print\".\n\n\"arguments\" is passed to the application, but should be omitted if the\nfile is a document.\n\n\"cwd\" is the working directory for the operation. If \"filepath\" is\nrelative, it will be resolved against this directory. This argument\nshould usually be an absolute path.\n\n\"show_cmd\" can be used to override the recommended visibility option.\nSee the Windows ShellExecute documentation for values.\n\nstartfile returns as soon as the associated application is launched.\nThere is no option to wait for the application to close, and no way\nto retrieve the application's exit status.\n\nThe filepath is relative to the current directory. If you want to use\nan absolute path, make sure the first character is not a slash (\"/\");\nthe underlying Win32 ShellExecute function doesn't work if it is.", + "nt.stat" => "Perform a stat system call on the given path.\n\n path\n Path to be examined; can be string, bytes, a path-like object or\n open-file-descriptor int.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be a relative string; path will then be relative to\n that directory.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n stat will examine the symbolic link itself instead of the file\n the link points to.\n\ndir_fd and follow_symlinks may not be implemented\n on your platform. If they are unavailable, using them will raise a\n NotImplementedError.\n\nIt's an error to use dir_fd or follow_symlinks when specifying path as\n an open file descriptor.", + "nt.strerror" => "Translate an error code to a message string.", + "nt.symlink" => "Create a symbolic link pointing to src named dst.\n\ntarget_is_directory is required on Windows if the target is to be\n interpreted as a directory. (On Windows, symlink requires\n Windows 6.0 or greater, and raises a NotImplementedError otherwise.)\n target_is_directory is ignored on non-Windows platforms.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "nt.system" => "Execute the command in a subshell.", + "nt.times" => "Return a collection containing process timing information.\n\nThe object returned behaves like a named tuple with these fields:\n (utime, stime, cutime, cstime, elapsed_time)\nAll fields are floating-point numbers.", + "nt.times_result" => "times_result: Result from os.times().\n\nThis object may be accessed either as a tuple of\n (user, system, children_user, children_system, elapsed),\nor via the attributes user, system, children_user, children_system,\nand elapsed.\n\nSee os.times for more information.", + "nt.times_result.__add__" => "Return self+value.", + "nt.times_result.__class_getitem__" => "See PEP 585", + "nt.times_result.__contains__" => "Return bool(key in self).", + "nt.times_result.__delattr__" => "Implement delattr(self, name).", + "nt.times_result.__eq__" => "Return self==value.", + "nt.times_result.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "nt.times_result.__ge__" => "Return self>=value.", + "nt.times_result.__getattribute__" => "Return getattr(self, name).", + "nt.times_result.__getitem__" => "Return self[key].", + "nt.times_result.__getstate__" => "Helper for pickle.", + "nt.times_result.__gt__" => "Return self>value.", + "nt.times_result.__hash__" => "Return hash(self).", + "nt.times_result.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "nt.times_result.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "nt.times_result.__iter__" => "Implement iter(self).", + "nt.times_result.__le__" => "Return self<=value.", + "nt.times_result.__len__" => "Return len(self).", + "nt.times_result.__lt__" => "Return self "Return self*value.", + "nt.times_result.__ne__" => "Return self!=value.", + "nt.times_result.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "nt.times_result.__reduce_ex__" => "Helper for pickle.", + "nt.times_result.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "nt.times_result.__repr__" => "Return repr(self).", + "nt.times_result.__rmul__" => "Return value*self.", + "nt.times_result.__setattr__" => "Implement setattr(self, name, value).", + "nt.times_result.__sizeof__" => "Size of object in memory, in bytes.", + "nt.times_result.__str__" => "Return str(self).", + "nt.times_result.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "nt.times_result.children_system" => "system time of children", + "nt.times_result.children_user" => "user time of children", + "nt.times_result.count" => "Return number of occurrences of value.", + "nt.times_result.elapsed" => "elapsed time since an arbitrary point in the past", + "nt.times_result.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "nt.times_result.system" => "system time", + "nt.times_result.user" => "user time", + "nt.truncate" => "Truncate a file, specified by path, to a specific length.\n\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.", + "nt.umask" => "Set the current numeric umask and return the previous umask.", + "nt.uname_result" => "uname_result: Result from os.uname().\n\nThis object may be accessed either as a tuple of\n (sysname, nodename, release, version, machine),\nor via the attributes sysname, nodename, release, version, and machine.\n\nSee os.uname for more information.", + "nt.uname_result.__add__" => "Return self+value.", + "nt.uname_result.__class_getitem__" => "See PEP 585", + "nt.uname_result.__contains__" => "Return bool(key in self).", + "nt.uname_result.__delattr__" => "Implement delattr(self, name).", + "nt.uname_result.__eq__" => "Return self==value.", + "nt.uname_result.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "nt.uname_result.__ge__" => "Return self>=value.", + "nt.uname_result.__getattribute__" => "Return getattr(self, name).", + "nt.uname_result.__getitem__" => "Return self[key].", + "nt.uname_result.__getstate__" => "Helper for pickle.", + "nt.uname_result.__gt__" => "Return self>value.", + "nt.uname_result.__hash__" => "Return hash(self).", + "nt.uname_result.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "nt.uname_result.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "nt.uname_result.__iter__" => "Implement iter(self).", + "nt.uname_result.__le__" => "Return self<=value.", + "nt.uname_result.__len__" => "Return len(self).", + "nt.uname_result.__lt__" => "Return self "Return self*value.", + "nt.uname_result.__ne__" => "Return self!=value.", + "nt.uname_result.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "nt.uname_result.__reduce_ex__" => "Helper for pickle.", + "nt.uname_result.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "nt.uname_result.__repr__" => "Return repr(self).", + "nt.uname_result.__rmul__" => "Return value*self.", + "nt.uname_result.__setattr__" => "Implement setattr(self, name, value).", + "nt.uname_result.__sizeof__" => "Size of object in memory, in bytes.", + "nt.uname_result.__str__" => "Return str(self).", + "nt.uname_result.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "nt.uname_result.count" => "Return number of occurrences of value.", + "nt.uname_result.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "nt.uname_result.machine" => "hardware identifier", + "nt.uname_result.nodename" => "name of machine on network (implementation-defined)", + "nt.uname_result.release" => "operating system release", + "nt.uname_result.sysname" => "operating system name", + "nt.uname_result.version" => "operating system version", + "nt.unlink" => "Remove a file (same as remove()).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "nt.unsetenv" => "Delete an environment variable.", + "nt.urandom" => "Return a bytes object containing random bytes suitable for cryptographic use.", + "nt.utime" => "Set the access and modified time of path.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.\n\nIf times is not None, it must be a tuple (atime, mtime);\n atime and mtime should be expressed as float seconds since the epoch.\nIf ns is specified, it must be a tuple (atime_ns, mtime_ns);\n atime_ns and mtime_ns should be expressed as integer nanoseconds\n since the epoch.\nIf times is None and ns is unspecified, utime uses the current time.\nSpecifying tuples for both times and ns is an error.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, utime will modify the symbolic link itself instead of the file the\n link points to.\nIt is an error to use dir_fd or follow_symlinks when specifying path\n as an open file descriptor.\ndir_fd and follow_symlinks may not be available on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "nt.waitpid" => "Wait for completion of a given process.\n\nReturns a tuple of information regarding the process:\n (pid, status << 8)\n\nThe options argument is ignored on Windows.", + "nt.waitstatus_to_exitcode" => "Convert a wait status to an exit code.\n\nOn Unix:\n\n* If WIFEXITED(status) is true, return WEXITSTATUS(status).\n* If WIFSIGNALED(status) is true, return -WTERMSIG(status).\n* Otherwise, raise a ValueError.\n\nOn Windows, return status shifted right by 8 bits.\n\nOn Unix, if the process is being traced or if waitpid() was called with\nWUNTRACED option, the caller must first check if WIFSTOPPED(status) is true.\nThis function must not be called if WIFSTOPPED(status) is true.", + "nt.write" => "Write a bytes object to a file descriptor.", + "posix" => "This module provides access to operating system functionality that is\nstandardized by the C Standard and the POSIX standard (a thinly\ndisguised Unix interface). Refer to the library manual and\ncorresponding Unix manual entries for more information on calls.", + "posix.DirEntry.__class_getitem__" => "See PEP 585", + "posix.DirEntry.__delattr__" => "Implement delattr(self, name).", + "posix.DirEntry.__eq__" => "Return self==value.", + "posix.DirEntry.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "posix.DirEntry.__fspath__" => "Returns the path for the entry.", + "posix.DirEntry.__ge__" => "Return self>=value.", + "posix.DirEntry.__getattribute__" => "Return getattr(self, name).", + "posix.DirEntry.__getstate__" => "Helper for pickle.", + "posix.DirEntry.__gt__" => "Return self>value.", + "posix.DirEntry.__hash__" => "Return hash(self).", + "posix.DirEntry.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "posix.DirEntry.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "posix.DirEntry.__le__" => "Return self<=value.", + "posix.DirEntry.__lt__" => "Return self "Return self!=value.", + "posix.DirEntry.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "posix.DirEntry.__reduce__" => "Helper for pickle.", + "posix.DirEntry.__reduce_ex__" => "Helper for pickle.", + "posix.DirEntry.__repr__" => "Return repr(self).", + "posix.DirEntry.__setattr__" => "Implement setattr(self, name, value).", + "posix.DirEntry.__sizeof__" => "Size of object in memory, in bytes.", + "posix.DirEntry.__str__" => "Return str(self).", + "posix.DirEntry.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "posix.DirEntry.inode" => "Return inode of the entry; cached per entry.", + "posix.DirEntry.is_dir" => "Return True if the entry is a directory; cached per entry.", + "posix.DirEntry.is_file" => "Return True if the entry is a file; cached per entry.", + "posix.DirEntry.is_junction" => "Return True if the entry is a junction; cached per entry.", + "posix.DirEntry.is_symlink" => "Return True if the entry is a symbolic link; cached per entry.", + "posix.DirEntry.name" => "the entry's base filename, relative to scandir() \"path\" argument", + "posix.DirEntry.path" => "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)", + "posix.DirEntry.stat" => "Return stat_result object for the entry; cached per entry.", + "posix.WCOREDUMP" => "Return True if the process returning status was dumped to a core file.", + "posix.WEXITSTATUS" => "Return the process return code from status.", + "posix.WIFCONTINUED" => "Return True if a particular process was continued from a job control stop.\n\nReturn True if the process returning status was continued from a\njob control stop.", + "posix.WIFEXITED" => "Return True if the process returning status exited via the exit() system call.", + "posix.WIFSIGNALED" => "Return True if the process returning status was terminated by a signal.", + "posix.WIFSTOPPED" => "Return True if the process returning status was stopped.", + "posix.WSTOPSIG" => "Return the signal that stopped the process that provided the status value.", + "posix.WTERMSIG" => "Return the signal that terminated the process that provided the status value.", + "posix._exit" => "Exit to the system with specified status, without normal exit processing.", + "posix._fcopyfile" => "Efficiently copy content or metadata of 2 regular file descriptors (macOS).", + "posix._inputhook" => "Calls PyOS_CallInputHook droppong the GIL first", + "posix._is_inputhook_installed" => "Checks if PyOS_CallInputHook is set", + "posix._path_normpath" => "Normalize path, eliminating double slashes, etc.", + "posix._path_splitroot_ex" => "Split a pathname into drive, root and tail.\n\nThe tail contains anything after the root.", + "posix.abort" => "Abort the interpreter immediately.\n\nThis function 'dumps core' or otherwise fails in the hardest way possible\non the hosting operating system. This function never returns.", + "posix.access" => "Use the real uid/gid to test for access to a path.\n\n path\n Path to be tested; can be string, bytes, or a path-like object.\n mode\n Operating-system mode bitfield. Can be F_OK to test existence,\n or the inclusive-OR of R_OK, W_OK, and X_OK.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that\n directory.\n effective_ids\n If True, access will use the effective uid/gid instead of\n the real uid/gid.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n access will examine the symbolic link itself instead of the file\n the link points to.\n\ndir_fd, effective_ids, and follow_symlinks may not be implemented\n on your platform. If they are unavailable, using them will raise a\n NotImplementedError.\n\nNote that most operations will use the effective uid/gid, therefore this\n routine can be used in a suid/sgid environment to test if the invoking user\n has the specified access to the path.", + "posix.chdir" => "Change the current working directory to the specified path.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\nIf this functionality is unavailable, using it raises an exception.", + "posix.chflags" => "Set file flags.\n\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, chflags will change flags on the symbolic link itself instead of the\n file the link points to.\nfollow_symlinks may not be implemented on your platform. If it is\nunavailable, using it will raise a NotImplementedError.", + "posix.chmod" => "Change the access permissions of a file.\n\n path\n Path to be modified. May always be specified as a str, bytes, or a path-like object.\n On some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.\n mode\n Operating-system mode bitfield.\n Be careful when using number literals for *mode*. The conventional UNIX notation for\n numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n Python.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that\n directory.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n chmod will modify the symbolic link itself instead of the file\n the link points to.\n\nIt is an error to use dir_fd or follow_symlinks when specifying path as\n an open file descriptor.\ndir_fd and follow_symlinks may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "posix.chown" => "Change the owner and group id of path to the numeric uid and gid.\\\n\n path\n Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that\n directory.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n stat will examine the symbolic link itself instead of the file\n the link points to.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, chown will modify the symbolic link itself instead of the file the\n link points to.\nIt is an error to use dir_fd or follow_symlinks when specifying path as\n an open file descriptor.\ndir_fd and follow_symlinks may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "posix.chroot" => "Change root directory to path.", + "posix.close" => "Close a file descriptor.", + "posix.closerange" => "Closes all file descriptors in [fd_low, fd_high), ignoring errors.", + "posix.confstr" => "Return a string-valued system configuration variable.", + "posix.copy_file_range" => "Copy count bytes from one file descriptor to another.\n\n src\n Source file descriptor.\n dst\n Destination file descriptor.\n count\n Number of bytes to copy.\n offset_src\n Starting offset in src.\n offset_dst\n Starting offset in dst.\n\nIf offset_src is None, then src is read from the current position;\nrespectively for offset_dst.", + "posix.cpu_count" => "Return the number of logical CPUs in the system.\n\nReturn None if indeterminable.", + "posix.ctermid" => "Return the name of the controlling terminal for this process.", + "posix.device_encoding" => "Return a string describing the encoding of a terminal's file descriptor.\n\nThe file descriptor must be attached to a terminal.\nIf the device is not a terminal, return None.", + "posix.dup" => "Return a duplicate of a file descriptor.", + "posix.dup2" => "Duplicate file descriptor.", + "posix.eventfd" => "Creates and returns an event notification file descriptor.", + "posix.eventfd_read" => "Read eventfd value", + "posix.eventfd_write" => "Write eventfd value.", + "posix.execv" => "Execute an executable path with arguments, replacing current process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.", + "posix.execve" => "Execute an executable path with arguments, replacing current process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.\nenv\n Dictionary of strings mapping to strings.", + "posix.fchdir" => "Change to the directory of the given file descriptor.\n\nfd must be opened on a directory, not a file.\nEquivalent to os.chdir(fd).", + "posix.fchmod" => "Change the access permissions of the file given by file descriptor fd.\n\n fd\n The file descriptor of the file to be modified.\n mode\n Operating-system mode bitfield.\n Be careful when using number literals for *mode*. The conventional UNIX notation for\n numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in\n Python.\n\nEquivalent to os.chmod(fd, mode).", + "posix.fchown" => "Change the owner and group id of the file specified by file descriptor.\n\nEquivalent to os.chown(fd, uid, gid).", + "posix.fdatasync" => "Force write of fd to disk without forcing update of metadata.", + "posix.fork" => "Fork a child process.\n\nReturn 0 to child process and PID of child to parent process.", + "posix.forkpty" => "Fork a new process with a new pseudo-terminal as controlling tty.\n\nReturns a tuple of (pid, master_fd).\nLike fork(), return pid of 0 to the child process,\nand pid of child to the parent process.\nTo both, return fd of newly opened pseudo-terminal.", + "posix.fpathconf" => "Return the configuration limit name for the file descriptor fd.\n\nIf there is no limit, return -1.", + "posix.fspath" => "Return the file system path representation of the object.\n\nIf the object is str or bytes, then allow it to pass through as-is. If the\nobject defines __fspath__(), then return the result of that method. All other\ntypes raise a TypeError.", + "posix.fstat" => "Perform a stat system call on the given file descriptor.\n\nLike stat(), but for an open file descriptor.\nEquivalent to os.stat(fd).", + "posix.fstatvfs" => "Perform an fstatvfs system call on the given fd.\n\nEquivalent to statvfs(fd).", + "posix.fsync" => "Force write of fd to disk.", + "posix.ftruncate" => "Truncate a file, specified by file descriptor, to a specific length.", + "posix.get_blocking" => "Get the blocking mode of the file descriptor.\n\nReturn False if the O_NONBLOCK flag is set, True if the flag is cleared.", + "posix.get_inheritable" => "Get the close-on-exe flag of the specified file descriptor.", + "posix.get_terminal_size" => "Return the size of the terminal window as (columns, lines).\n\nThe optional argument fd (default standard output) specifies\nwhich file descriptor should be queried.\n\nIf the file descriptor is not connected to a terminal, an OSError\nis thrown.\n\nThis function will only be defined if an implementation is\navailable for this system.\n\nshutil.get_terminal_size is the high-level function which should\nnormally be used, os.get_terminal_size is the low-level implementation.", + "posix.getcwd" => "Return a unicode string representing the current working directory.", + "posix.getcwdb" => "Return a bytes string representing the current working directory.", + "posix.getegid" => "Return the current process's effective group id.", + "posix.geteuid" => "Return the current process's effective user id.", + "posix.getgid" => "Return the current process's group id.", + "posix.getgrouplist" => "Returns a list of groups to which a user belongs.\n\nuser\n username to lookup\ngroup\n base group id of the user", + "posix.getgroups" => "Return list of supplemental group IDs for the process.", + "posix.getloadavg" => "Return average recent system load information.\n\nReturn the number of processes in the system run queue averaged over\nthe last 1, 5, and 15 minutes as a tuple of three floats.\nRaises OSError if the load average was unobtainable.", + "posix.getlogin" => "Return the actual login name.", + "posix.getpgid" => "Call the system call getpgid(), and return the result.", + "posix.getpgrp" => "Return the current process group id.", + "posix.getpid" => "Return the current process id.", + "posix.getppid" => "Return the parent's process id.\n\nIf the parent process has already exited, Windows machines will still\nreturn its id; others systems will return the id of the 'init' process (1).", + "posix.getpriority" => "Return program scheduling priority.", + "posix.getrandom" => "Obtain a series of random bytes.", + "posix.getresgid" => "Return a tuple of the current process's real, effective, and saved group ids.", + "posix.getresuid" => "Return a tuple of the current process's real, effective, and saved user ids.", + "posix.getsid" => "Call the system call getsid(pid) and return the result.", + "posix.getuid" => "Return the current process's user id.", + "posix.getxattr" => "Return the value of extended attribute attribute on path.\n\npath may be either a string, a path-like object, or an open file descriptor.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, getxattr will examine the symbolic link itself instead of the file\n the link points to.", + "posix.grantpt" => "Grant access to the slave pseudo-terminal device.\n\n fd\n File descriptor of a master pseudo-terminal device.\n\nPerforms a grantpt() C function call.", + "posix.initgroups" => "Initialize the group access list.\n\nCall the system initgroups() to initialize the group access list with all of\nthe groups of which the specified username is a member, plus the specified\ngroup id.", + "posix.isatty" => "Return True if the fd is connected to a terminal.\n\nReturn True if the file descriptor is an open file descriptor\nconnected to the slave end of a terminal.", + "posix.kill" => "Kill a process with a signal.", + "posix.killpg" => "Kill a process group with a signal.", + "posix.lchflags" => "Set file flags.\n\nThis function will not follow symbolic links.\nEquivalent to chflags(path, flags, follow_symlinks=False).", + "posix.lchmod" => "Change the access permissions of a file, without following symbolic links.\n\nIf path is a symlink, this affects the link itself rather than the target.\nEquivalent to chmod(path, mode, follow_symlinks=False).\"", + "posix.lchown" => "Change the owner and group id of path to the numeric uid and gid.\n\nThis function will not follow symbolic links.\nEquivalent to os.chown(path, uid, gid, follow_symlinks=False).", + "posix.link" => "Create a hard link to a file.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nIf follow_symlinks is False, and the last element of src is a symbolic\n link, link will create a link to the symbolic link itself instead of the\n file the link points to.\nsrc_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your\n platform. If they are unavailable, using them will raise a\n NotImplementedError.", + "posix.listdir" => "Return a list containing the names of the files in the directory.\n\npath can be specified as either str, bytes, or a path-like object. If path is bytes,\n the filenames returned will also be bytes; in all other circumstances\n the filenames returned will be str.\nIf path is None, uses the path='.'.\nOn some platforms, path may also be specified as an open file descriptor;\\\n the file descriptor must refer to a directory.\n If this functionality is unavailable, using it raises NotImplementedError.\n\nThe list is in arbitrary order. It does not include the special\nentries '.' and '..' even if they are present in the directory.", + "posix.listxattr" => "Return a list of extended attributes on path.\n\npath may be either None, a string, a path-like object, or an open file descriptor.\nif path is None, listxattr will examine the current directory.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, listxattr will examine the symbolic link itself instead of the file\n the link points to.", + "posix.lockf" => "Apply, test or remove a POSIX lock on an open file descriptor.\n\nfd\n An open file descriptor.\ncommand\n One of F_LOCK, F_TLOCK, F_ULOCK or F_TEST.\nlength\n The number of bytes to lock, starting at the current position.", + "posix.login_tty" => "Prepare the tty of which fd is a file descriptor for a new login session.\n\nMake the calling process a session leader; make the tty the\ncontrolling tty, the stdin, the stdout, and the stderr of the\ncalling process; close fd.", + "posix.lseek" => "Set the position of a file descriptor. Return the new position.\n\n fd\n An open file descriptor, as returned by os.open().\n position\n Position, interpreted relative to 'whence'.\n whence\n The relative position to seek from. Valid values are:\n - SEEK_SET: seek from the start of the file.\n - SEEK_CUR: seek from the current file position.\n - SEEK_END: seek from the end of the file.\n\nThe return value is the number of bytes relative to the beginning of the file.", + "posix.lstat" => "Perform a stat system call on the given path, without following symbolic links.\n\nLike stat(), but do not follow symbolic links.\nEquivalent to stat(path, follow_symlinks=False).", + "posix.major" => "Extracts a device major number from a raw device number.", + "posix.makedev" => "Composes a raw device number from the major and minor device numbers.", + "posix.minor" => "Extracts a device minor number from a raw device number.", + "posix.mkdir" => "Create a directory.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.\n\nThe mode argument is ignored on Windows. Where it is used, the current umask\nvalue is first masked out.", + "posix.mkfifo" => "Create a \"fifo\" (a POSIX named pipe).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.mknod" => "Create a node in the file system.\n\nCreate a node in the file system (file, device special file or named pipe)\nat path. mode specifies both the permissions to use and the\ntype of node to be created, being combined (bitwise OR) with one of\nS_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set on mode,\ndevice defines the newly created device special file (probably using\nos.makedev()). Otherwise device is ignored.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.nice" => "Add increment to the priority of process and return the new priority.", + "posix.open" => "Open a file for low level IO. Returns a file descriptor (integer).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.openpty" => "Open a pseudo-terminal.\n\nReturn a tuple of (master_fd, slave_fd) containing open file descriptors\nfor both the master and slave ends.", + "posix.pathconf" => "Return the configuration limit name for the file or directory path.\n\nIf there is no limit, return -1.\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.", + "posix.pidfd_open" => "Return a file descriptor referring to the process *pid*.\n\nThe descriptor can be used to perform process management without races and\nsignals.", + "posix.pipe" => "Create a pipe.\n\nReturns a tuple of two file descriptors:\n (read_fd, write_fd)", + "posix.pipe2" => "Create a pipe with flags set atomically.\n\nReturns a tuple of two file descriptors:\n (read_fd, write_fd)\n\nflags can be constructed by ORing together one or more of these values:\nO_NONBLOCK, O_CLOEXEC.", + "posix.posix_fadvise" => "Announce an intention to access data in a specific pattern.\n\nAnnounce an intention to access data in a specific pattern, thus allowing\nthe kernel to make optimizations.\nThe advice applies to the region of the file specified by fd starting at\noffset and continuing for length bytes.\nadvice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL,\nPOSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEED, or\nPOSIX_FADV_DONTNEED.", + "posix.posix_fallocate" => "Ensure a file has allocated at least a particular number of bytes on disk.\n\nEnsure that the file specified by fd encompasses a range of bytes\nstarting at offset bytes from the beginning and continuing for length bytes.", + "posix.posix_openpt" => "Open and return a file descriptor for a master pseudo-terminal device.\n\nPerforms a posix_openpt() C function call. The oflag argument is used to\nset file status flags and file access modes as specified in the manual page\nof posix_openpt() of your system.", + "posix.posix_spawn" => "Execute the program specified by path in a new process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.\nenv\n Dictionary of strings mapping to strings.\nfile_actions\n A sequence of file action tuples.\nsetpgroup\n The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\nresetids\n If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.\nsetsid\n If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\nsetsigmask\n The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\nsetsigdef\n The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.\nscheduler\n A tuple with the scheduler policy (optional) and parameters.", + "posix.posix_spawnp" => "Execute the program specified by path in a new process.\n\npath\n Path of executable file.\nargv\n Tuple or list of strings.\nenv\n Dictionary of strings mapping to strings.\nfile_actions\n A sequence of file action tuples.\nsetpgroup\n The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\nresetids\n If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.\nsetsid\n If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\nsetsigmask\n The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\nsetsigdef\n The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.\nscheduler\n A tuple with the scheduler policy (optional) and parameters.", + "posix.pread" => "Read a number of bytes from a file descriptor starting at a particular offset.\n\nRead length bytes from file descriptor fd, starting at offset bytes from\nthe beginning of the file. The file offset remains unchanged.", + "posix.preadv" => "Reads from a file descriptor into a number of mutable bytes-like objects.\n\nCombines the functionality of readv() and pread(). As readv(), it will\ntransfer data into each buffer until it is full and then move on to the next\nbuffer in the sequence to hold the rest of the data. Its fourth argument,\nspecifies the file offset at which the input operation is to be performed. It\nwill return the total number of bytes read (which can be less than the total\ncapacity of all the objects).\n\nThe flags argument contains a bitwise OR of zero or more of the following flags:\n\n- RWF_HIPRI\n- RWF_NOWAIT\n\nUsing non-zero flags requires Linux 4.6 or newer.", + "posix.ptsname" => "Return the name of the slave pseudo-terminal device.\n\n fd\n File descriptor of a master pseudo-terminal device.\n\nIf the ptsname_r() C function is available, it is called;\notherwise, performs a ptsname() C function call.", + "posix.putenv" => "Change or add an environment variable.", + "posix.pwrite" => "Write bytes to a file descriptor starting at a particular offset.\n\nWrite buffer to fd, starting at offset bytes from the beginning of\nthe file. Returns the number of bytes written. Does not change the\ncurrent file offset.", + "posix.pwritev" => "Writes the contents of bytes-like objects to a file descriptor at a given offset.\n\nCombines the functionality of writev() and pwrite(). All buffers must be a sequence\nof bytes-like objects. Buffers are processed in array order. Entire contents of first\nbuffer is written before proceeding to second, and so on. The operating system may\nset a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used.\nThis function writes the contents of each object to the file descriptor and returns\nthe total number of bytes written.\n\nThe flags argument contains a bitwise OR of zero or more of the following flags:\n\n- RWF_DSYNC\n- RWF_SYNC\n- RWF_APPEND\n\nUsing non-zero flags requires Linux 4.7 or newer.", + "posix.read" => "Read from a file descriptor. Returns a bytes object.", + "posix.readlink" => "Return a string representing the path to which the symbolic link points.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\nand path should be relative; path will then be relative to that directory.\n\ndir_fd may not be implemented on your platform. If it is unavailable,\nusing it will raise a NotImplementedError.", + "posix.readv" => "Read from a file descriptor fd into an iterable of buffers.\n\nThe buffers should be mutable buffers accepting bytes.\nreadv will transfer data into each buffer until it is full\nand then move on to the next buffer in the sequence to hold\nthe rest of the data.\n\nreadv returns the total number of bytes read,\nwhich may be less than the total capacity of all the buffers.", + "posix.register_at_fork" => "Register callables to be called when forking a new process.\n\n before\n A callable to be called in the parent before the fork() syscall.\n after_in_child\n A callable to be called in the child after fork().\n after_in_parent\n A callable to be called in the parent after fork().\n\n'before' callbacks are called in reverse order.\n'after_in_child' and 'after_in_parent' callbacks are called in order.", + "posix.remove" => "Remove a file (same as unlink()).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.removexattr" => "Remove extended attribute attribute on path.\n\npath may be either a string, a path-like object, or an open file descriptor.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, removexattr will modify the symbolic link itself instead of the file\n the link points to.", + "posix.rename" => "Rename a file or directory.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nsrc_dir_fd and dst_dir_fd, may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "posix.replace" => "Rename a file or directory, overwriting the destination.\n\nIf either src_dir_fd or dst_dir_fd is not None, it should be a file\n descriptor open to a directory, and the respective path string (src or dst)\n should be relative; the path will then be relative to that directory.\nsrc_dir_fd and dst_dir_fd, may not be implemented on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "posix.rmdir" => "Remove a directory.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.scandir" => "Return an iterator of DirEntry objects for given path.\n\npath can be specified as either str, bytes, or a path-like object. If path\nis bytes, the names of yielded DirEntry objects will also be bytes; in\nall other circumstances they will be str.\n\nIf path is None, uses the path='.'.", + "posix.sched_get_priority_max" => "Get the maximum scheduling priority for policy.", + "posix.sched_get_priority_min" => "Get the minimum scheduling priority for policy.", + "posix.sched_getaffinity" => "Return the affinity of the process identified by pid (or the current process if zero).\n\nThe affinity is returned as a set of CPU identifiers.", + "posix.sched_getparam" => "Returns scheduling parameters for the process identified by pid.\n\nIf pid is 0, returns parameters for the calling process.\nReturn value is an instance of sched_param.", + "posix.sched_getscheduler" => "Get the scheduling policy for the process identified by pid.\n\nPassing 0 for pid returns the scheduling policy for the calling process.", + "posix.sched_param" => "Currently has only one field: sched_priority\n\nsched_priority\n A scheduling parameter.", + "posix.sched_param.__add__" => "Return self+value.", + "posix.sched_param.__class_getitem__" => "See PEP 585", + "posix.sched_param.__contains__" => "Return bool(key in self).", + "posix.sched_param.__delattr__" => "Implement delattr(self, name).", + "posix.sched_param.__eq__" => "Return self==value.", + "posix.sched_param.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "posix.sched_param.__ge__" => "Return self>=value.", + "posix.sched_param.__getattribute__" => "Return getattr(self, name).", + "posix.sched_param.__getitem__" => "Return self[key].", + "posix.sched_param.__getstate__" => "Helper for pickle.", + "posix.sched_param.__gt__" => "Return self>value.", + "posix.sched_param.__hash__" => "Return hash(self).", + "posix.sched_param.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "posix.sched_param.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "posix.sched_param.__iter__" => "Implement iter(self).", + "posix.sched_param.__le__" => "Return self<=value.", + "posix.sched_param.__len__" => "Return len(self).", + "posix.sched_param.__lt__" => "Return self "Return self*value.", + "posix.sched_param.__ne__" => "Return self!=value.", + "posix.sched_param.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "posix.sched_param.__reduce_ex__" => "Helper for pickle.", + "posix.sched_param.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "posix.sched_param.__repr__" => "Return repr(self).", + "posix.sched_param.__rmul__" => "Return value*self.", + "posix.sched_param.__setattr__" => "Implement setattr(self, name, value).", + "posix.sched_param.__sizeof__" => "Size of object in memory, in bytes.", + "posix.sched_param.__str__" => "Return str(self).", + "posix.sched_param.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "posix.sched_param.count" => "Return number of occurrences of value.", + "posix.sched_param.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "posix.sched_param.sched_priority" => "the scheduling priority", + "posix.sched_rr_get_interval" => "Return the round-robin quantum for the process identified by pid, in seconds.\n\nValue returned is a float.", + "posix.sched_setaffinity" => "Set the CPU affinity of the process identified by pid to mask.\n\nmask should be an iterable of integers identifying CPUs.", + "posix.sched_setparam" => "Set scheduling parameters for the process identified by pid.\n\nIf pid is 0, sets parameters for the calling process.\nparam should be an instance of sched_param.", + "posix.sched_setscheduler" => "Set the scheduling policy for the process identified by pid.\n\nIf pid is 0, the calling process is changed.\nparam is an instance of sched_param.", + "posix.sched_yield" => "Voluntarily relinquish the CPU.", + "posix.sendfile" => "Copy count bytes from file descriptor in_fd to file descriptor out_fd.", + "posix.set_blocking" => "Set the blocking mode of the specified file descriptor.\n\nSet the O_NONBLOCK flag if blocking is False,\nclear the O_NONBLOCK flag otherwise.", + "posix.set_inheritable" => "Set the inheritable flag of the specified file descriptor.", + "posix.setegid" => "Set the current process's effective group id.", + "posix.seteuid" => "Set the current process's effective user id.", + "posix.setgid" => "Set the current process's group id.", + "posix.setgroups" => "Set the groups of the current process to list.", + "posix.setns" => "Move the calling thread into different namespaces.\n\nfd\n A file descriptor to a namespace.\nnstype\n Type of namespace.", + "posix.setpgid" => "Call the system call setpgid(pid, pgrp).", + "posix.setpgrp" => "Make the current process the leader of its process group.", + "posix.setpriority" => "Set program scheduling priority.", + "posix.setregid" => "Set the current process's real and effective group ids.", + "posix.setresgid" => "Set the current process's real, effective, and saved group ids.", + "posix.setresuid" => "Set the current process's real, effective, and saved user ids.", + "posix.setreuid" => "Set the current process's real and effective user ids.", + "posix.setsid" => "Call the system call setsid().", + "posix.setuid" => "Set the current process's user id.", + "posix.setxattr" => "Set extended attribute attribute on path to value.\n\npath may be either a string, a path-like object, or an open file descriptor.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, setxattr will modify the symbolic link itself instead of the file\n the link points to.", + "posix.splice" => "Transfer count bytes from one pipe to a descriptor or vice versa.\n\n src\n Source file descriptor.\n dst\n Destination file descriptor.\n count\n Number of bytes to copy.\n offset_src\n Starting offset in src.\n offset_dst\n Starting offset in dst.\n flags\n Flags to modify the semantics of the call.\n\nIf offset_src is None, then src is read from the current position;\nrespectively for offset_dst. The offset associated to the file\ndescriptor that refers to a pipe must be None.", + "posix.stat" => "Perform a stat system call on the given path.\n\n path\n Path to be examined; can be string, bytes, a path-like object or\n open-file-descriptor int.\n dir_fd\n If not None, it should be a file descriptor open to a directory,\n and path should be a relative string; path will then be relative to\n that directory.\n follow_symlinks\n If False, and the last element of the path is a symbolic link,\n stat will examine the symbolic link itself instead of the file\n the link points to.\n\ndir_fd and follow_symlinks may not be implemented\n on your platform. If they are unavailable, using them will raise a\n NotImplementedError.\n\nIt's an error to use dir_fd or follow_symlinks when specifying path as\n an open file descriptor.", + "posix.statvfs" => "Perform a statvfs system call on the given path.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.", + "posix.strerror" => "Translate an error code to a message string.", + "posix.symlink" => "Create a symbolic link pointing to src named dst.\n\ntarget_is_directory is required on Windows if the target is to be\n interpreted as a directory. (On Windows, symlink requires\n Windows 6.0 or greater, and raises a NotImplementedError otherwise.)\n target_is_directory is ignored on non-Windows platforms.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.sync" => "Force write of everything to disk.", + "posix.sysconf" => "Return an integer-valued system configuration variable.", + "posix.system" => "Execute the command in a subshell.", + "posix.tcgetpgrp" => "Return the process group associated with the terminal specified by fd.", + "posix.tcsetpgrp" => "Set the process group associated with the terminal specified by fd.", + "posix.timerfd_create" => "Create and return a timer file descriptor.\n\nclockid\n A valid clock ID constant as timer file descriptor.\n\n time.CLOCK_REALTIME\n time.CLOCK_MONOTONIC\n time.CLOCK_BOOTTIME\nflags\n 0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC.\n\n os.TFD_NONBLOCK\n If *TFD_NONBLOCK* is set as a flag, read doesn't blocks.\n If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires.\n\n os.TFD_CLOEXEC\n If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag", + "posix.timerfd_gettime" => "Return a tuple of a timer file descriptor's (interval, next expiration) in float seconds.\n\nfd\n A timer file descriptor.", + "posix.timerfd_gettime_ns" => "Return a tuple of a timer file descriptor's (interval, next expiration) in nanoseconds.\n\nfd\n A timer file descriptor.", + "posix.timerfd_settime" => "Alter a timer file descriptor's internal timer in seconds.\n\nfd\n A timer file descriptor.\nflags\n 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.\ninitial\n The initial expiration time, in seconds.\ninterval\n The timer's interval, in seconds.", + "posix.timerfd_settime_ns" => "Alter a timer file descriptor's internal timer in nanoseconds.\n\nfd\n A timer file descriptor.\nflags\n 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.\ninitial\n initial expiration timing in seconds.\ninterval\n interval for the timer in seconds.", + "posix.times" => "Return a collection containing process timing information.\n\nThe object returned behaves like a named tuple with these fields:\n (utime, stime, cutime, cstime, elapsed_time)\nAll fields are floating-point numbers.", + "posix.times_result" => "times_result: Result from os.times().\n\nThis object may be accessed either as a tuple of\n (user, system, children_user, children_system, elapsed),\nor via the attributes user, system, children_user, children_system,\nand elapsed.\n\nSee os.times for more information.", + "posix.times_result.__add__" => "Return self+value.", + "posix.times_result.__class_getitem__" => "See PEP 585", + "posix.times_result.__contains__" => "Return bool(key in self).", + "posix.times_result.__delattr__" => "Implement delattr(self, name).", + "posix.times_result.__eq__" => "Return self==value.", + "posix.times_result.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "posix.times_result.__ge__" => "Return self>=value.", + "posix.times_result.__getattribute__" => "Return getattr(self, name).", + "posix.times_result.__getitem__" => "Return self[key].", + "posix.times_result.__getstate__" => "Helper for pickle.", + "posix.times_result.__gt__" => "Return self>value.", + "posix.times_result.__hash__" => "Return hash(self).", + "posix.times_result.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "posix.times_result.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "posix.times_result.__iter__" => "Implement iter(self).", + "posix.times_result.__le__" => "Return self<=value.", + "posix.times_result.__len__" => "Return len(self).", + "posix.times_result.__lt__" => "Return self "Return self*value.", + "posix.times_result.__ne__" => "Return self!=value.", + "posix.times_result.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "posix.times_result.__reduce_ex__" => "Helper for pickle.", + "posix.times_result.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "posix.times_result.__repr__" => "Return repr(self).", + "posix.times_result.__rmul__" => "Return value*self.", + "posix.times_result.__setattr__" => "Implement setattr(self, name, value).", + "posix.times_result.__sizeof__" => "Size of object in memory, in bytes.", + "posix.times_result.__str__" => "Return str(self).", + "posix.times_result.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "posix.times_result.children_system" => "system time of children", + "posix.times_result.children_user" => "user time of children", + "posix.times_result.count" => "Return number of occurrences of value.", + "posix.times_result.elapsed" => "elapsed time since an arbitrary point in the past", + "posix.times_result.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "posix.times_result.system" => "system time", + "posix.times_result.user" => "user time", + "posix.truncate" => "Truncate a file, specified by path, to a specific length.\n\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.", + "posix.ttyname" => "Return the name of the terminal device connected to 'fd'.\n\nfd\n Integer file descriptor handle.", + "posix.umask" => "Set the current numeric umask and return the previous umask.", + "posix.uname" => "Return an object identifying the current operating system.\n\nThe object behaves like a named tuple with the following fields:\n (sysname, nodename, release, version, machine)", + "posix.uname_result" => "uname_result: Result from os.uname().\n\nThis object may be accessed either as a tuple of\n (sysname, nodename, release, version, machine),\nor via the attributes sysname, nodename, release, version, and machine.\n\nSee os.uname for more information.", + "posix.uname_result.__add__" => "Return self+value.", + "posix.uname_result.__class_getitem__" => "See PEP 585", + "posix.uname_result.__contains__" => "Return bool(key in self).", + "posix.uname_result.__delattr__" => "Implement delattr(self, name).", + "posix.uname_result.__eq__" => "Return self==value.", + "posix.uname_result.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "posix.uname_result.__ge__" => "Return self>=value.", + "posix.uname_result.__getattribute__" => "Return getattr(self, name).", + "posix.uname_result.__getitem__" => "Return self[key].", + "posix.uname_result.__getstate__" => "Helper for pickle.", + "posix.uname_result.__gt__" => "Return self>value.", + "posix.uname_result.__hash__" => "Return hash(self).", + "posix.uname_result.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "posix.uname_result.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "posix.uname_result.__iter__" => "Implement iter(self).", + "posix.uname_result.__le__" => "Return self<=value.", + "posix.uname_result.__len__" => "Return len(self).", + "posix.uname_result.__lt__" => "Return self "Return self*value.", + "posix.uname_result.__ne__" => "Return self!=value.", + "posix.uname_result.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "posix.uname_result.__reduce_ex__" => "Helper for pickle.", + "posix.uname_result.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "posix.uname_result.__repr__" => "Return repr(self).", + "posix.uname_result.__rmul__" => "Return value*self.", + "posix.uname_result.__setattr__" => "Implement setattr(self, name, value).", + "posix.uname_result.__sizeof__" => "Size of object in memory, in bytes.", + "posix.uname_result.__str__" => "Return str(self).", + "posix.uname_result.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "posix.uname_result.count" => "Return number of occurrences of value.", + "posix.uname_result.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "posix.uname_result.machine" => "hardware identifier", + "posix.uname_result.nodename" => "name of machine on network (implementation-defined)", + "posix.uname_result.release" => "operating system release", + "posix.uname_result.sysname" => "operating system name", + "posix.uname_result.version" => "operating system version", + "posix.unlink" => "Remove a file (same as remove()).\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\ndir_fd may not be implemented on your platform.\n If it is unavailable, using it will raise a NotImplementedError.", + "posix.unlockpt" => "Unlock a pseudo-terminal master/slave pair.\n\n fd\n File descriptor of a master pseudo-terminal device.\n\nPerforms an unlockpt() C function call.", + "posix.unsetenv" => "Delete an environment variable.", + "posix.unshare" => "Disassociate parts of a process (or thread) execution context.\n\nflags\n Namespaces to be unshared.", + "posix.urandom" => "Return a bytes object containing random bytes suitable for cryptographic use.", + "posix.utime" => "Set the access and modified time of path.\n\npath may always be specified as a string.\nOn some platforms, path may also be specified as an open file descriptor.\n If this functionality is unavailable, using it raises an exception.\n\nIf times is not None, it must be a tuple (atime, mtime);\n atime and mtime should be expressed as float seconds since the epoch.\nIf ns is specified, it must be a tuple (atime_ns, mtime_ns);\n atime_ns and mtime_ns should be expressed as integer nanoseconds\n since the epoch.\nIf times is None and ns is unspecified, utime uses the current time.\nSpecifying tuples for both times and ns is an error.\n\nIf dir_fd is not None, it should be a file descriptor open to a directory,\n and path should be relative; path will then be relative to that directory.\nIf follow_symlinks is False, and the last element of the path is a symbolic\n link, utime will modify the symbolic link itself instead of the file the\n link points to.\nIt is an error to use dir_fd or follow_symlinks when specifying path\n as an open file descriptor.\ndir_fd and follow_symlinks may not be available on your platform.\n If they are unavailable, using them will raise a NotImplementedError.", + "posix.wait" => "Wait for completion of a child process.\n\nReturns a tuple of information about the child process:\n (pid, status)", + "posix.wait3" => "Wait for completion of a child process.\n\nReturns a tuple of information about the child process:\n (pid, status, rusage)", + "posix.wait4" => "Wait for completion of a specific child process.\n\nReturns a tuple of information about the child process:\n (pid, status, rusage)", + "posix.waitid" => "Returns the result of waiting for a process or processes.\n\n idtype\n Must be one of be P_PID, P_PGID or P_ALL.\n id\n The id to wait on.\n options\n Constructed from the ORing of one or more of WEXITED, WSTOPPED\n or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT.\n\nReturns either waitid_result or None if WNOHANG is specified and there are\nno children in a waitable state.", + "posix.waitid_result" => "waitid_result: Result from waitid.\n\nThis object may be accessed either as a tuple of\n (si_pid, si_uid, si_signo, si_status, si_code),\nor via the attributes si_pid, si_uid, and so on.\n\nSee os.waitid for more information.", + "posix.waitid_result.__add__" => "Return self+value.", + "posix.waitid_result.__class_getitem__" => "See PEP 585", + "posix.waitid_result.__contains__" => "Return bool(key in self).", + "posix.waitid_result.__delattr__" => "Implement delattr(self, name).", + "posix.waitid_result.__eq__" => "Return self==value.", + "posix.waitid_result.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "posix.waitid_result.__ge__" => "Return self>=value.", + "posix.waitid_result.__getattribute__" => "Return getattr(self, name).", + "posix.waitid_result.__getitem__" => "Return self[key].", + "posix.waitid_result.__getstate__" => "Helper for pickle.", + "posix.waitid_result.__gt__" => "Return self>value.", + "posix.waitid_result.__hash__" => "Return hash(self).", + "posix.waitid_result.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "posix.waitid_result.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "posix.waitid_result.__iter__" => "Implement iter(self).", + "posix.waitid_result.__le__" => "Return self<=value.", + "posix.waitid_result.__len__" => "Return len(self).", + "posix.waitid_result.__lt__" => "Return self "Return self*value.", + "posix.waitid_result.__ne__" => "Return self!=value.", + "posix.waitid_result.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "posix.waitid_result.__reduce_ex__" => "Helper for pickle.", + "posix.waitid_result.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "posix.waitid_result.__repr__" => "Return repr(self).", + "posix.waitid_result.__rmul__" => "Return value*self.", + "posix.waitid_result.__setattr__" => "Implement setattr(self, name, value).", + "posix.waitid_result.__sizeof__" => "Size of object in memory, in bytes.", + "posix.waitid_result.__str__" => "Return str(self).", + "posix.waitid_result.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "posix.waitid_result.count" => "Return number of occurrences of value.", + "posix.waitid_result.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "posix.waitpid" => "Wait for completion of a given child process.\n\nReturns a tuple of information regarding the child process:\n (pid, status)\n\nThe options argument is ignored on Windows.", + "posix.waitstatus_to_exitcode" => "Convert a wait status to an exit code.\n\nOn Unix:\n\n* If WIFEXITED(status) is true, return WEXITSTATUS(status).\n* If WIFSIGNALED(status) is true, return -WTERMSIG(status).\n* Otherwise, raise a ValueError.\n\nOn Windows, return status shifted right by 8 bits.\n\nOn Unix, if the process is being traced or if waitpid() was called with\nWUNTRACED option, the caller must first check if WIFSTOPPED(status) is true.\nThis function must not be called if WIFSTOPPED(status) is true.", + "posix.write" => "Write a bytes object to a file descriptor.", + "posix.writev" => "Iterate over buffers, and write the contents of each to a file descriptor.\n\nReturns the total number of bytes written.\nbuffers must be a sequence of bytes-like objects.", + "pwd" => "This module provides access to the Unix password database.\nIt is available on all Unix versions.\n\nPassword database entries are reported as 7-tuples containing the following\nitems from the password database (see `'), in order:\npw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell.\nThe uid and gid items are integers, all others are strings. An\nexception is raised if the entry asked for cannot be found.", + "pwd.getpwall" => "Return a list of all available password database entries, in arbitrary order.\n\nSee help(pwd) for more on password database entries.", + "pwd.getpwnam" => "Return the password database entry for the given user name.\n\nSee `help(pwd)` for more on password database entries.", + "pwd.getpwuid" => "Return the password database entry for the given numeric user ID.\n\nSee `help(pwd)` for more on password database entries.", + "pwd.struct_passwd" => "pwd.struct_passwd: Results from getpw*() routines.\n\nThis object may be accessed either as a tuple of\n (pw_name,pw_passwd,pw_uid,pw_gid,pw_gecos,pw_dir,pw_shell)\nor via the object attributes as named in the above tuple.", + "pwd.struct_passwd.__add__" => "Return self+value.", + "pwd.struct_passwd.__class_getitem__" => "See PEP 585", + "pwd.struct_passwd.__contains__" => "Return bool(key in self).", + "pwd.struct_passwd.__delattr__" => "Implement delattr(self, name).", + "pwd.struct_passwd.__eq__" => "Return self==value.", + "pwd.struct_passwd.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "pwd.struct_passwd.__ge__" => "Return self>=value.", + "pwd.struct_passwd.__getattribute__" => "Return getattr(self, name).", + "pwd.struct_passwd.__getitem__" => "Return self[key].", + "pwd.struct_passwd.__getstate__" => "Helper for pickle.", + "pwd.struct_passwd.__gt__" => "Return self>value.", + "pwd.struct_passwd.__hash__" => "Return hash(self).", + "pwd.struct_passwd.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "pwd.struct_passwd.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "pwd.struct_passwd.__iter__" => "Implement iter(self).", + "pwd.struct_passwd.__le__" => "Return self<=value.", + "pwd.struct_passwd.__len__" => "Return len(self).", + "pwd.struct_passwd.__lt__" => "Return self "Return self*value.", + "pwd.struct_passwd.__ne__" => "Return self!=value.", + "pwd.struct_passwd.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "pwd.struct_passwd.__reduce_ex__" => "Helper for pickle.", + "pwd.struct_passwd.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "pwd.struct_passwd.__repr__" => "Return repr(self).", + "pwd.struct_passwd.__rmul__" => "Return value*self.", + "pwd.struct_passwd.__setattr__" => "Implement setattr(self, name, value).", + "pwd.struct_passwd.__sizeof__" => "Size of object in memory, in bytes.", + "pwd.struct_passwd.__str__" => "Return str(self).", + "pwd.struct_passwd.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "pwd.struct_passwd.count" => "Return number of occurrences of value.", + "pwd.struct_passwd.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "pwd.struct_passwd.pw_dir" => "home directory", + "pwd.struct_passwd.pw_gecos" => "real name", + "pwd.struct_passwd.pw_gid" => "group id", + "pwd.struct_passwd.pw_name" => "user name", + "pwd.struct_passwd.pw_passwd" => "password", + "pwd.struct_passwd.pw_shell" => "shell program", + "pwd.struct_passwd.pw_uid" => "user id", + "pyexpat" => "Python wrapper for Expat parser.", + "pyexpat.ErrorString" => "Returns string error for given number.", + "pyexpat.ParserCreate" => "Return a new XML parser object.", + "pyexpat.XMLParserType" => "XML parser", + "pyexpat.XMLParserType.ExternalEntityParserCreate" => "Create a parser for parsing an external entity based on the information passed to the ExternalEntityRefHandler.", + "pyexpat.XMLParserType.GetBase" => "Return base URL string for the parser.", + "pyexpat.XMLParserType.GetInputContext" => "Return the untranslated text of the input that caused the current event.\n\nIf the event was generated by a large amount of text (such as a start tag\nfor an element with many attributes), not all of the text may be available.", + "pyexpat.XMLParserType.GetReparseDeferralEnabled" => "Retrieve reparse deferral enabled status; always returns false with Expat <2.6.0.", + "pyexpat.XMLParserType.Parse" => "Parse XML data.\n\n`isfinal' should be true at end of input.", + "pyexpat.XMLParserType.ParseFile" => "Parse XML data from file-like object.", + "pyexpat.XMLParserType.SetBase" => "Set the base URL for the parser.", + "pyexpat.XMLParserType.SetParamEntityParsing" => "Controls parsing of parameter entities (including the external DTD subset).\n\nPossible flag values are XML_PARAM_ENTITY_PARSING_NEVER,\nXML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE and\nXML_PARAM_ENTITY_PARSING_ALWAYS. Returns true if setting the flag\nwas successful.", + "pyexpat.XMLParserType.SetReparseDeferralEnabled" => "Enable/Disable reparse deferral; enabled by default with Expat >=2.6.0.", + "pyexpat.XMLParserType.UseForeignDTD" => "Allows the application to provide an artificial external subset if one is not specified as part of the document instance.\n\nThis readily allows the use of a 'default' document type controlled by the\napplication, while still getting the advantage of providing document type\ninformation to the parser. 'flag' defaults to True if not provided.", + "pyexpat.XMLParserType.__delattr__" => "Implement delattr(self, name).", + "pyexpat.XMLParserType.__eq__" => "Return self==value.", + "pyexpat.XMLParserType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "pyexpat.XMLParserType.__ge__" => "Return self>=value.", + "pyexpat.XMLParserType.__getattribute__" => "Return getattr(self, name).", + "pyexpat.XMLParserType.__getstate__" => "Helper for pickle.", + "pyexpat.XMLParserType.__gt__" => "Return self>value.", + "pyexpat.XMLParserType.__hash__" => "Return hash(self).", + "pyexpat.XMLParserType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "pyexpat.XMLParserType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "pyexpat.XMLParserType.__le__" => "Return self<=value.", + "pyexpat.XMLParserType.__lt__" => "Return self "Return self!=value.", + "pyexpat.XMLParserType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "pyexpat.XMLParserType.__reduce__" => "Helper for pickle.", + "pyexpat.XMLParserType.__reduce_ex__" => "Helper for pickle.", + "pyexpat.XMLParserType.__repr__" => "Return repr(self).", + "pyexpat.XMLParserType.__setattr__" => "Implement setattr(self, name, value).", + "pyexpat.XMLParserType.__sizeof__" => "Size of object in memory, in bytes.", + "pyexpat.XMLParserType.__str__" => "Return str(self).", + "pyexpat.XMLParserType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "readline" => "Importing this module enables command line editing using GNU readline.", + "readline.add_history" => "Add an item to the history buffer.", + "readline.append_history_file" => "Append the last nelements items of the history list to file.\n\nThe default filename is ~/.history.", + "readline.clear_history" => "Clear the current readline history.", + "readline.get_begidx" => "Get the beginning index of the completion scope.", + "readline.get_completer" => "Get the current completer function.", + "readline.get_completer_delims" => "Get the word delimiters for completion.", + "readline.get_completion_type" => "Get the type of completion being attempted.", + "readline.get_current_history_length" => "Return the current (not the maximum) length of history.", + "readline.get_endidx" => "Get the ending index of the completion scope.", + "readline.get_history_item" => "Return the current contents of history item at one-based index.", + "readline.get_history_length" => "Return the maximum number of lines that will be written to the history file.", + "readline.get_line_buffer" => "Return the current contents of the line buffer.", + "readline.insert_text" => "Insert text into the line buffer at the cursor position.", + "readline.parse_and_bind" => "Execute the init line provided in the string argument.", + "readline.read_history_file" => "Load a readline history file.\n\nThe default filename is ~/.history.", + "readline.read_init_file" => "Execute a readline initialization file.\n\nThe default filename is the last filename used.", + "readline.redisplay" => "Change what's displayed on the screen to reflect contents of the line buffer.", + "readline.remove_history_item" => "Remove history item given by its zero-based position.", + "readline.replace_history_item" => "Replaces history item given by its position with contents of line.\n\npos is zero-based.", + "readline.set_auto_history" => "Enables or disables automatic history.", + "readline.set_completer" => "Set or remove the completer function.\n\nThe function is called as function(text, state),\nfor state in 0, 1, 2, ..., until it returns a non-string.\nIt should return the next possible completion starting with 'text'.", + "readline.set_completer_delims" => "Set the word delimiters for completion.", + "readline.set_completion_display_matches_hook" => "Set or remove the completion display function.\n\nThe function is called as\n function(substitution, [matches], longest_match_length)\nonce each time matches need to be displayed.", + "readline.set_history_length" => "Set the maximal number of lines which will be written to the history file.\n\nA negative length is used to inhibit history truncation.", + "readline.set_pre_input_hook" => "Set or remove the function invoked by the rl_pre_input_hook callback.\n\nThe function is called with no arguments after the first prompt\nhas been printed and just before readline starts reading input\ncharacters.", + "readline.set_startup_hook" => "Set or remove the function invoked by the rl_startup_hook callback.\n\nThe function is called with no arguments just\nbefore readline prints the first prompt.", + "readline.write_history_file" => "Save a readline history file.\n\nThe default filename is ~/.history.", + "resource.struct_rusage" => "struct_rusage: Result from getrusage.\n\nThis object may be accessed either as a tuple of\n (utime,stime,maxrss,ixrss,idrss,isrss,minflt,majflt,\n nswap,inblock,oublock,msgsnd,msgrcv,nsignals,nvcsw,nivcsw)\nor via the attributes ru_utime, ru_stime, ru_maxrss, and so on.", + "resource.struct_rusage.__add__" => "Return self+value.", + "resource.struct_rusage.__class_getitem__" => "See PEP 585", + "resource.struct_rusage.__contains__" => "Return bool(key in self).", + "resource.struct_rusage.__delattr__" => "Implement delattr(self, name).", + "resource.struct_rusage.__eq__" => "Return self==value.", + "resource.struct_rusage.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "resource.struct_rusage.__ge__" => "Return self>=value.", + "resource.struct_rusage.__getattribute__" => "Return getattr(self, name).", + "resource.struct_rusage.__getitem__" => "Return self[key].", + "resource.struct_rusage.__getstate__" => "Helper for pickle.", + "resource.struct_rusage.__gt__" => "Return self>value.", + "resource.struct_rusage.__hash__" => "Return hash(self).", + "resource.struct_rusage.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "resource.struct_rusage.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "resource.struct_rusage.__iter__" => "Implement iter(self).", + "resource.struct_rusage.__le__" => "Return self<=value.", + "resource.struct_rusage.__len__" => "Return len(self).", + "resource.struct_rusage.__lt__" => "Return self "Return self*value.", + "resource.struct_rusage.__ne__" => "Return self!=value.", + "resource.struct_rusage.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "resource.struct_rusage.__reduce_ex__" => "Helper for pickle.", + "resource.struct_rusage.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "resource.struct_rusage.__repr__" => "Return repr(self).", + "resource.struct_rusage.__rmul__" => "Return value*self.", + "resource.struct_rusage.__setattr__" => "Implement setattr(self, name, value).", + "resource.struct_rusage.__sizeof__" => "Size of object in memory, in bytes.", + "resource.struct_rusage.__str__" => "Return str(self).", + "resource.struct_rusage.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "resource.struct_rusage.count" => "Return number of occurrences of value.", + "resource.struct_rusage.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "resource.struct_rusage.ru_idrss" => "unshared data size", + "resource.struct_rusage.ru_inblock" => "block input operations", + "resource.struct_rusage.ru_isrss" => "unshared stack size", + "resource.struct_rusage.ru_ixrss" => "shared memory size", + "resource.struct_rusage.ru_majflt" => "page faults requiring I/O", + "resource.struct_rusage.ru_maxrss" => "max. resident set size", + "resource.struct_rusage.ru_minflt" => "page faults not requiring I/O", + "resource.struct_rusage.ru_msgrcv" => "IPC messages received", + "resource.struct_rusage.ru_msgsnd" => "IPC messages sent", + "resource.struct_rusage.ru_nivcsw" => "involuntary context switches", + "resource.struct_rusage.ru_nsignals" => "signals received", + "resource.struct_rusage.ru_nswap" => "number of swap outs", + "resource.struct_rusage.ru_nvcsw" => "voluntary context switches", + "resource.struct_rusage.ru_oublock" => "block output operations", + "resource.struct_rusage.ru_stime" => "system time used", + "resource.struct_rusage.ru_utime" => "user time used", + "select" => "This module supports asynchronous I/O on multiple file descriptors.\n\n*** IMPORTANT NOTICE ***\nOn Windows, only sockets are supported; on Unix, all file descriptors.", + "select.epoll" => "select.epoll(sizehint=-1, flags=0)\n\nReturns an epolling object\n\nsizehint must be a positive integer or -1 for the default size. The\nsizehint is used to optimize internal data structures. It doesn't limit\nthe maximum number of monitored events.", + "select.epoll.__delattr__" => "Implement delattr(self, name).", + "select.epoll.__eq__" => "Return self==value.", + "select.epoll.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "select.epoll.__ge__" => "Return self>=value.", + "select.epoll.__getattribute__" => "Return getattr(self, name).", + "select.epoll.__getstate__" => "Helper for pickle.", + "select.epoll.__gt__" => "Return self>value.", + "select.epoll.__hash__" => "Return hash(self).", + "select.epoll.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "select.epoll.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "select.epoll.__le__" => "Return self<=value.", + "select.epoll.__lt__" => "Return self "Return self!=value.", + "select.epoll.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "select.epoll.__reduce__" => "Helper for pickle.", + "select.epoll.__reduce_ex__" => "Helper for pickle.", + "select.epoll.__repr__" => "Return repr(self).", + "select.epoll.__setattr__" => "Implement setattr(self, name, value).", + "select.epoll.__sizeof__" => "Size of object in memory, in bytes.", + "select.epoll.__str__" => "Return str(self).", + "select.epoll.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "select.epoll.close" => "Close the epoll control file descriptor.\n\nFurther operations on the epoll object will raise an exception.", + "select.epoll.closed" => "True if the epoll handler is closed", + "select.epoll.fileno" => "Return the epoll control file descriptor.", + "select.epoll.fromfd" => "Create an epoll object from a given control fd.", + "select.epoll.modify" => "Modify event mask for a registered file descriptor.\n\nfd\n the target file descriptor of the operation\neventmask\n a bit set composed of the various EPOLL constants", + "select.epoll.poll" => "Wait for events on the epoll file descriptor.\n\n timeout\n the maximum time to wait in seconds (as float);\n a timeout of None or -1 makes poll wait indefinitely\n maxevents\n the maximum number of events returned; -1 means no limit\n\nReturns a list containing any descriptors that have events to report,\nas a list of (fd, events) 2-tuples.", + "select.epoll.register" => "Registers a new fd or raises an OSError if the fd is already registered.\n\n fd\n the target file descriptor of the operation\n eventmask\n a bit set composed of the various EPOLL constants\n\nThe epoll interface supports all file descriptors that support poll.", + "select.epoll.unregister" => "Remove a registered file descriptor from the epoll object.\n\nfd\n the target file descriptor of the operation", + "select.kevent" => "kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0)\n\nThis object is the equivalent of the struct kevent for the C API.\n\nSee the kqueue manpage for more detailed information about the meaning\nof the arguments.\n\nOne minor note: while you might hope that udata could store a\nreference to a python object, it cannot, because it is impossible to\nkeep a proper reference count of the object once it's passed into the\nkernel. Therefore, I have restricted it to only storing an integer. I\nrecommend ignoring it and simply using the 'ident' field to key off\nof. You could also set up a dictionary on the python side to store a\nudata->object mapping.", + "select.kevent.__delattr__" => "Implement delattr(self, name).", + "select.kevent.__eq__" => "Return self==value.", + "select.kevent.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "select.kevent.__ge__" => "Return self>=value.", + "select.kevent.__getattribute__" => "Return getattr(self, name).", + "select.kevent.__getstate__" => "Helper for pickle.", + "select.kevent.__gt__" => "Return self>value.", + "select.kevent.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "select.kevent.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "select.kevent.__le__" => "Return self<=value.", + "select.kevent.__lt__" => "Return self "Return self!=value.", + "select.kevent.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "select.kevent.__reduce__" => "Helper for pickle.", + "select.kevent.__reduce_ex__" => "Helper for pickle.", + "select.kevent.__repr__" => "Return repr(self).", + "select.kevent.__setattr__" => "Implement setattr(self, name, value).", + "select.kevent.__sizeof__" => "Size of object in memory, in bytes.", + "select.kevent.__str__" => "Return str(self).", + "select.kevent.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "select.kqueue" => "Kqueue syscall wrapper.\n\nFor example, to start watching a socket for input:\n>>> kq = kqueue()\n>>> sock = socket()\n>>> sock.connect((host, port))\n>>> kq.control([kevent(sock, KQ_FILTER_WRITE, KQ_EV_ADD)], 0)\n\nTo wait one second for it to become writeable:\n>>> kq.control(None, 1, 1000)\n\nTo stop listening:\n>>> kq.control([kevent(sock, KQ_FILTER_WRITE, KQ_EV_DELETE)], 0)", + "select.kqueue.__del__" => "Called when the instance is about to be destroyed.", + "select.kqueue.__delattr__" => "Implement delattr(self, name).", + "select.kqueue.__eq__" => "Return self==value.", + "select.kqueue.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "select.kqueue.__ge__" => "Return self>=value.", + "select.kqueue.__getattribute__" => "Return getattr(self, name).", + "select.kqueue.__getstate__" => "Helper for pickle.", + "select.kqueue.__gt__" => "Return self>value.", + "select.kqueue.__hash__" => "Return hash(self).", + "select.kqueue.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "select.kqueue.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "select.kqueue.__le__" => "Return self<=value.", + "select.kqueue.__lt__" => "Return self "Return self!=value.", + "select.kqueue.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "select.kqueue.__reduce__" => "Helper for pickle.", + "select.kqueue.__reduce_ex__" => "Helper for pickle.", + "select.kqueue.__repr__" => "Return repr(self).", + "select.kqueue.__setattr__" => "Implement setattr(self, name, value).", + "select.kqueue.__sizeof__" => "Size of object in memory, in bytes.", + "select.kqueue.__str__" => "Return str(self).", + "select.kqueue.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "select.kqueue.close" => "Close the kqueue control file descriptor.\n\nFurther operations on the kqueue object will raise an exception.", + "select.kqueue.closed" => "True if the kqueue handler is closed", + "select.kqueue.control" => "Calls the kernel kevent function.\n\nchangelist\n Must be an iterable of kevent objects describing the changes to be made\n to the kernel's watch list or None.\nmaxevents\n The maximum number of events that the kernel will return.\ntimeout\n The maximum time to wait in seconds, or else None to wait forever.\n This accepts floats for smaller timeouts, too.", + "select.kqueue.fileno" => "Return the kqueue control file descriptor.", + "select.kqueue.fromfd" => "Create a kqueue object from a given control fd.", + "select.poll" => "Returns a polling object.\n\nThis object supports registering and unregistering file descriptors, and then\npolling them for I/O events.", + "select.select" => "Wait until one or more file descriptors are ready for some kind of I/O.\n\nThe first three arguments are iterables of file descriptors to be waited for:\nrlist -- wait until ready for reading\nwlist -- wait until ready for writing\nxlist -- wait for an \"exceptional condition\"\nIf only one kind of condition is required, pass [] for the other lists.\n\nA file descriptor is either a socket or file object, or a small integer\ngotten from a fileno() method call on one of those.\n\nThe optional 4th argument specifies a timeout in seconds; it may be\na floating-point number to specify fractions of seconds. If it is absent\nor None, the call will never time out.\n\nThe return value is a tuple of three lists corresponding to the first three\narguments; each contains the subset of the corresponding file descriptors\nthat are ready.\n\n*** IMPORTANT NOTICE ***\nOn Windows, only sockets are supported; on Unix, all file\ndescriptors can be used.", + "sys" => "This module provides access to some objects used or maintained by the\ninterpreter and to functions that interact strongly with the interpreter.\n\nDynamic objects:\n\nargv -- command line arguments; argv[0] is the script pathname if known\npath -- module search path; path[0] is the script directory, else ''\nmodules -- dictionary of loaded modules\n\ndisplayhook -- called to show results in an interactive session\nexcepthook -- called to handle any uncaught exception other than SystemExit\n To customize printing in an interactive session or to install a custom\n top-level exception handler, assign other functions to replace these.\n\nstdin -- standard input file object; used by input()\nstdout -- standard output file object; used by print()\nstderr -- standard error object; used for error messages\n By assigning other file objects (or objects that behave like files)\n to these, it is possible to redirect all of the interpreter's I/O.\n\nlast_exc - the last uncaught exception\n Only available in an interactive session after a\n traceback has been printed.\nlast_type -- type of last uncaught exception\nlast_value -- value of last uncaught exception\nlast_traceback -- traceback of last uncaught exception\n These three are the (deprecated) legacy representation of last_exc.\n\nStatic objects:\n\nbuiltin_module_names -- tuple of module names built into this interpreter\ncopyright -- copyright notice pertaining to this interpreter\nexec_prefix -- prefix used to find the machine-specific Python library\nexecutable -- absolute path of the executable binary of the Python interpreter\nfloat_info -- a named tuple with information about the float implementation.\nfloat_repr_style -- string indicating the style of repr() output for floats\nhash_info -- a named tuple with information about the hash algorithm.\nhexversion -- version information encoded as a single integer\nimplementation -- Python implementation information.\nint_info -- a named tuple with information about the int implementation.\nmaxsize -- the largest supported length of containers.\nmaxunicode -- the value of the largest Unicode code point\nplatform -- platform identifier\nprefix -- prefix used to find the Python library\nthread_info -- a named tuple with information about the thread implementation.\nversion -- the version of this interpreter as a string\nversion_info -- version information as a named tuple\ndllhandle -- [Windows only] integer handle of the Python DLL\nwinver -- [Windows only] version number of the Python DLL\n_enablelegacywindowsfsencoding -- [Windows only]\n__stdin__ -- the original stdin; don't touch!\n__stdout__ -- the original stdout; don't touch!\n__stderr__ -- the original stderr; don't touch!\n__displayhook__ -- the original displayhook; don't touch!\n__excepthook__ -- the original excepthook; don't touch!\n\nFunctions:\n\ndisplayhook() -- print an object to the screen, and save it in builtins._\nexcepthook() -- print an exception and its traceback to sys.stderr\nexception() -- return the current thread's active exception\nexc_info() -- return information about the current thread's active exception\nexit() -- exit the interpreter by raising SystemExit\ngetdlopenflags() -- returns flags to be used for dlopen() calls\ngetprofile() -- get the global profiling function\ngetrefcount() -- return the reference count for an object (plus one :-)\ngetrecursionlimit() -- return the max recursion depth for the interpreter\ngetsizeof() -- return the size of an object in bytes\ngettrace() -- get the global debug tracing function\nsetdlopenflags() -- set the flags to be used for dlopen() calls\nsetprofile() -- set the global profiling function\nsetrecursionlimit() -- set the max recursion depth for the interpreter\nsettrace() -- set the global debug tracing function", + "sys.__breakpointhook__" => "This hook function is called by built-in breakpoint().", + "sys.__displayhook__" => "Print an object to sys.stdout and also save it in builtins._", + "sys.__excepthook__" => "Handle an exception by displaying it with a traceback on sys.stderr.", + "sys.__unraisablehook__" => "Handle an unraisable exception.\n\nThe unraisable argument has the following attributes:\n\n* exc_type: Exception type.\n* exc_value: Exception value, can be None.\n* exc_traceback: Exception traceback, can be None.\n* err_msg: Error message, can be None.\n* object: Object causing the exception, can be None.", + "sys._baserepl" => "Private function for getting the base REPL", + "sys._clear_internal_caches" => "Clear all internal performance-related caches.", + "sys._clear_type_cache" => "Clear the internal type lookup cache.", + "sys._current_exceptions" => "Return a dict mapping each thread's identifier to its current raised exception.\n\nThis function should be used for specialized purposes only.", + "sys._current_frames" => "Return a dict mapping each thread's thread id to its current stack frame.\n\nThis function should be used for specialized purposes only.", + "sys._debugmallocstats" => "Print summary info to stderr about the state of pymalloc's structures.\n\nIn Py_DEBUG mode, also perform some expensive internal consistency\nchecks.", + "sys._enablelegacywindowsfsencoding" => "Changes the default filesystem encoding to mbcs:replace.\n\nThis is done for consistency with earlier versions of Python. See PEP\n529 for more information.\n\nThis is equivalent to defining the PYTHONLEGACYWINDOWSFSENCODING\nenvironment variable before launching Python.", + "sys._get_cpu_count_config" => "Private function for getting PyConfig.cpu_count", + "sys._getframe" => "Return a frame object from the call stack.\n\nIf optional integer depth is given, return the frame object that many\ncalls below the top of the stack. If that is deeper than the call\nstack, ValueError is raised. The default for depth is zero, returning\nthe frame at the top of the call stack.\n\nThis function should be used for internal and specialized purposes\nonly.", + "sys._getframemodulename" => "Return the name of the module for a calling frame.\n\nThe default depth returns the module containing the call to this API.\nA more typical use in a library will pass a depth of 1 to get the user's\nmodule rather than the library module.\n\nIf no frame, module, or name can be found, returns None.", + "sys._is_gil_enabled" => "Return True if the GIL is currently enabled and False otherwise.", + "sys._is_interned" => "Return True if the given string is \"interned\".", + "sys._setprofileallthreads" => "Set the profiling function in all running threads belonging to the current interpreter.\n\nIt will be called on each function call and return. See the profiler\nchapter in the library manual.", + "sys._settraceallthreads" => "Set the global debug tracing function in all running threads belonging to the current interpreter.\n\nIt will be called on each function call. See the debugger chapter\nin the library manual.", + "sys.activate_stack_trampoline" => "Activate stack profiler trampoline *backend*.", + "sys.addaudithook" => "Adds a new audit hook callback.", + "sys.audit" => "Passes the event to any audit hooks that are attached.", + "sys.breakpointhook" => "This hook function is called by built-in breakpoint().", + "sys.call_tracing" => "Call func(*args), while tracing is enabled.\n\nThe tracing state is saved, and restored afterwards. This is intended\nto be called from a debugger from a checkpoint, to recursively debug\nsome other code.", + "sys.deactivate_stack_trampoline" => "Deactivate the current stack profiler trampoline backend.\n\nIf no stack profiler is activated, this function has no effect.", + "sys.displayhook" => "Print an object to sys.stdout and also save it in builtins._", + "sys.exc_info" => "Return current exception information: (type, value, traceback).\n\nReturn information about the most recent exception caught by an except\nclause in the current stack frame or in an older stack frame.", + "sys.excepthook" => "Handle an exception by displaying it with a traceback on sys.stderr.", + "sys.exception" => "Return the current exception.\n\nReturn the most recent exception caught by an except clause\nin the current stack frame or in an older stack frame, or None\nif no such exception exists.", + "sys.exit" => "Exit the interpreter by raising SystemExit(status).\n\nIf the status is omitted or None, it defaults to zero (i.e., success).\nIf the status is an integer, it will be used as the system exit status.\nIf it is another kind of object, it will be printed and the system\nexit status will be one (i.e., failure).", + "sys.get_asyncgen_hooks" => "Return the installed asynchronous generators hooks.\n\nThis returns a namedtuple of the form (firstiter, finalizer).", + "sys.get_coroutine_origin_tracking_depth" => "Check status of origin tracking for coroutine objects in this thread.", + "sys.get_int_max_str_digits" => "Return the maximum string digits limit for non-binary int<->str conversions.", + "sys.getallocatedblocks" => "Return the number of memory blocks currently allocated.", + "sys.getdefaultencoding" => "Return the current default encoding used by the Unicode implementation.", + "sys.getdlopenflags" => "Return the current value of the flags that are used for dlopen calls.\n\nThe flag constants are defined in the os module.", + "sys.getfilesystemencodeerrors" => "Return the error mode used Unicode to OS filename conversion.", + "sys.getfilesystemencoding" => "Return the encoding used to convert Unicode filenames to OS filenames.", + "sys.getprofile" => "Return the profiling function set with sys.setprofile.\n\nSee the profiler chapter in the library manual.", + "sys.getrecursionlimit" => "Return the current value of the recursion limit.\n\nThe recursion limit is the maximum depth of the Python interpreter\nstack. This limit prevents infinite recursion from causing an overflow\nof the C stack and crashing Python.", + "sys.getrefcount" => "Return the reference count of object.\n\nThe count returned is generally one higher than you might expect,\nbecause it includes the (temporary) reference as an argument to\ngetrefcount().", + "sys.getsizeof" => "getsizeof(object [, default]) -> int\n\nReturn the size of object in bytes.", + "sys.getswitchinterval" => "Return the current thread switch interval; see sys.setswitchinterval().", + "sys.gettrace" => "Return the global debug tracing function set with sys.settrace.\n\nSee the debugger chapter in the library manual.", + "sys.getunicodeinternedsize" => "Return the number of elements of the unicode interned dictionary", + "sys.getwindowsversion" => "Return info about the running version of Windows as a named tuple.\n\nThe members are named: major, minor, build, platform, service_pack,\nservice_pack_major, service_pack_minor, suite_mask, product_type and\nplatform_version. For backward compatibility, only the first 5 items\nare available by indexing. All elements are numbers, except\nservice_pack and platform_type which are strings, and platform_version\nwhich is a 3-tuple. Platform is always 2. Product_type may be 1 for a\nworkstation, 2 for a domain controller, 3 for a server.\nPlatform_version is a 3-tuple containing a version number that is\nintended for identifying the OS rather than feature detection.", + "sys.intern" => "``Intern'' the given string.\n\nThis enters the string in the (global) table of interned strings whose\npurpose is to speed up dictionary lookups. Return the string itself or\nthe previously interned string object with the same value.", + "sys.is_finalizing" => "Return True if Python is exiting.", + "sys.is_stack_trampoline_active" => "Return *True* if a stack profiler trampoline is active.", + "sys.set_asyncgen_hooks" => "set_asyncgen_hooks([firstiter] [, finalizer])\n\nSet a finalizer for async generators objects.", + "sys.set_coroutine_origin_tracking_depth" => "Enable or disable origin tracking for coroutine objects in this thread.\n\nCoroutine objects will track 'depth' frames of traceback information\nabout where they came from, available in their cr_origin attribute.\n\nSet a depth of 0 to disable.", + "sys.set_int_max_str_digits" => "Set the maximum string digits limit for non-binary int<->str conversions.", + "sys.setdlopenflags" => "Set the flags used by the interpreter for dlopen calls.\n\nThis is used, for example, when the interpreter loads extension\nmodules. Among other things, this will enable a lazy resolving of\nsymbols when importing a module, if called as sys.setdlopenflags(0).\nTo share symbols across extension modules, call as\nsys.setdlopenflags(os.RTLD_GLOBAL). Symbolic names for the flag\nmodules can be found in the os module (RTLD_xxx constants, e.g.\nos.RTLD_LAZY).", + "sys.setprofile" => "Set the profiling function.\n\nIt will be called on each function call and return. See the profiler\nchapter in the library manual.", + "sys.setrecursionlimit" => "Set the maximum depth of the Python interpreter stack to n.\n\nThis limit prevents infinite recursion from causing an overflow of the C\nstack and crashing Python. The highest possible limit is platform-\ndependent.", + "sys.setswitchinterval" => "Set the ideal thread switching delay inside the Python interpreter.\n\nThe actual frequency of switching threads can be lower if the\ninterpreter executes long sequences of uninterruptible code\n(this is implementation-specific and workload-dependent).\n\nThe parameter must represent the desired switching delay in seconds\nA typical value is 0.005 (5 milliseconds).", + "sys.settrace" => "Set the global debug tracing function.\n\nIt will be called on each function call. See the debugger chapter\nin the library manual.", + "sys.unraisablehook" => "Handle an unraisable exception.\n\nThe unraisable argument has the following attributes:\n\n* exc_type: Exception type.\n* exc_value: Exception value, can be None.\n* exc_traceback: Exception traceback, can be None.\n* err_msg: Error message, can be None.\n* object: Object causing the exception, can be None.", + "syslog.LOG_MASK" => "Calculates the mask for the individual priority pri.", + "syslog.LOG_UPTO" => "Calculates the mask for all priorities up to and including pri.", + "syslog.closelog" => "Reset the syslog module values and call the system library closelog().", + "syslog.openlog" => "Set logging options of subsequent syslog() calls.", + "syslog.setlogmask" => "Set the priority mask to maskpri and return the previous mask value.", + "syslog.syslog" => "syslog([priority=LOG_INFO,] message)\nSend the string message to the system logger.", + "termios" => "This module provides an interface to the Posix calls for tty I/O control.\nFor a complete description of these calls, see the Posix or Unix manual\npages. It is only available for those Unix versions that support Posix\ntermios style tty I/O control.\n\nAll functions in this module take a file descriptor fd as their first\nargument. This can be an integer file descriptor, such as returned by\nsys.stdin.fileno(), or a file object, such as sys.stdin itself.", + "termios.error.__cause__" => "exception cause", + "termios.error.__context__" => "exception context", + "termios.error.__delattr__" => "Implement delattr(self, name).", + "termios.error.__eq__" => "Return self==value.", + "termios.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "termios.error.__ge__" => "Return self>=value.", + "termios.error.__getattribute__" => "Return getattr(self, name).", + "termios.error.__getstate__" => "Helper for pickle.", + "termios.error.__gt__" => "Return self>value.", + "termios.error.__hash__" => "Return hash(self).", + "termios.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "termios.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "termios.error.__le__" => "Return self<=value.", + "termios.error.__lt__" => "Return self "Return self!=value.", + "termios.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "termios.error.__reduce_ex__" => "Helper for pickle.", + "termios.error.__repr__" => "Return repr(self).", + "termios.error.__setattr__" => "Implement setattr(self, name, value).", + "termios.error.__sizeof__" => "Size of object in memory, in bytes.", + "termios.error.__str__" => "Return str(self).", + "termios.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "termios.error.__weakref__" => "list of weak references to the object", + "termios.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "termios.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", + "termios.tcdrain" => "Wait until all output written to file descriptor fd has been transmitted.", + "termios.tcflow" => "Suspend or resume input or output on file descriptor fd.\n\nThe action argument can be termios.TCOOFF to suspend output,\ntermios.TCOON to restart output, termios.TCIOFF to suspend input,\nor termios.TCION to restart input.", + "termios.tcflush" => "Discard queued data on file descriptor fd.\n\nThe queue selector specifies which queue: termios.TCIFLUSH for the input\nqueue, termios.TCOFLUSH for the output queue, or termios.TCIOFLUSH for\nboth queues.", + "termios.tcgetattr" => "Get the tty attributes for file descriptor fd.\n\nReturns a list [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]\nwhere cc is a list of the tty special characters (each a string of\nlength 1, except the items with indices VMIN and VTIME, which are\nintegers when these fields are defined). The interpretation of the\nflags and the speeds as well as the indexing in the cc array must be\ndone using the symbolic constants defined in this module.", + "termios.tcgetwinsize" => "Get the tty winsize for file descriptor fd.\n\nReturns a tuple (ws_row, ws_col).", + "termios.tcsendbreak" => "Send a break on file descriptor fd.\n\nA zero duration sends a break for 0.25-0.5 seconds; a nonzero duration\nhas a system dependent meaning.", + "termios.tcsetattr" => "Set the tty attributes for file descriptor fd.\n\nThe attributes to be set are taken from the attributes argument, which\nis a list like the one returned by tcgetattr(). The when argument\ndetermines when the attributes are changed: termios.TCSANOW to\nchange immediately, termios.TCSADRAIN to change after transmitting all\nqueued output, or termios.TCSAFLUSH to change after transmitting all\nqueued output and discarding all queued input.", + "termios.tcsetwinsize" => "Set the tty winsize for file descriptor fd.\n\nThe winsize to be set is taken from the winsize argument, which\nis a two-item tuple (ws_row, ws_col) like the one returned by tcgetwinsize().", + "time" => "This module provides various functions to manipulate time values.\n\nThere are two standard representations of time. One is the number\nof seconds since the Epoch, in UTC (a.k.a. GMT). It may be an integer\nor a floating-point number (to represent fractions of seconds).\nThe epoch is the point where the time starts, the return value of time.gmtime(0).\nIt is January 1, 1970, 00:00:00 (UTC) on all platforms.\n\nThe other representation is a tuple of 9 integers giving local time.\nThe tuple items are:\n year (including century, e.g. 1998)\n month (1-12)\n day (1-31)\n hours (0-23)\n minutes (0-59)\n seconds (0-59)\n weekday (0-6, Monday is 0)\n Julian day (day in the year, 1-366)\n DST (Daylight Savings Time) flag (-1, 0 or 1)\nIf the DST flag is 0, the time is given in the regular time zone;\nif it is 1, the time is given in the DST time zone;\nif it is -1, mktime() should guess based on the date and time.", + "time.asctime" => "asctime([tuple]) -> string\n\nConvert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.\nWhen the time tuple is not present, current time as returned by localtime()\nis used.", + "time.clock_getres" => "clock_getres(clk_id) -> floating-point number\n\nReturn the resolution (precision) of the specified clock clk_id.", + "time.clock_gettime" => "Return the time of the specified clock clk_id as a float.", + "time.clock_gettime_ns" => "Return the time of the specified clock clk_id as nanoseconds (int).", + "time.clock_settime" => "clock_settime(clk_id, time)\n\nSet the time of the specified clock clk_id.", + "time.clock_settime_ns" => "clock_settime_ns(clk_id, time)\n\nSet the time of the specified clock clk_id with nanoseconds.", + "time.ctime" => "ctime(seconds) -> string\n\nConvert a time in seconds since the Epoch to a string in local time.\nThis is equivalent to asctime(localtime(seconds)). When the time tuple is\nnot present, current time as returned by localtime() is used.", + "time.get_clock_info" => "get_clock_info(name: str) -> dict\n\nGet information of the specified clock.", + "time.gmtime" => "gmtime([seconds]) -> (tm_year, tm_mon, tm_mday, tm_hour, tm_min,\n tm_sec, tm_wday, tm_yday, tm_isdst)\n\nConvert seconds since the Epoch to a time tuple expressing UTC (a.k.a.\nGMT). When 'seconds' is not passed in, convert the current time instead.\n\nIf the platform supports the tm_gmtoff and tm_zone, they are available as\nattributes only.", + "time.localtime" => "localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,\n tm_sec,tm_wday,tm_yday,tm_isdst)\n\nConvert seconds since the Epoch to a time tuple expressing local time.\nWhen 'seconds' is not passed in, convert the current time instead.", + "time.mktime" => "mktime(tuple) -> floating-point number\n\nConvert a time tuple in local time to seconds since the Epoch.\nNote that mktime(gmtime(0)) will not generally return zero for most\ntime zones; instead the returned value will either be equal to that\nof the timezone or altzone attributes on the time module.", + "time.monotonic" => "monotonic() -> float\n\nMonotonic clock, cannot go backward.", + "time.monotonic_ns" => "monotonic_ns() -> int\n\nMonotonic clock, cannot go backward, as nanoseconds.", + "time.perf_counter" => "perf_counter() -> float\n\nPerformance counter for benchmarking.", + "time.perf_counter_ns" => "perf_counter_ns() -> int\n\nPerformance counter for benchmarking as nanoseconds.", + "time.process_time" => "process_time() -> float\n\nProcess time for profiling: sum of the kernel and user-space CPU time.", + "time.process_time_ns" => "process_time() -> int\n\nProcess time for profiling as nanoseconds:\nsum of the kernel and user-space CPU time.", + "time.pthread_getcpuclockid" => "pthread_getcpuclockid(thread_id) -> int\n\nReturn the clk_id of a thread's CPU time clock.", + "time.sleep" => "sleep(seconds)\n\nDelay execution for a given number of seconds. The argument may be\na floating-point number for subsecond precision.", + "time.strftime" => "strftime(format[, tuple]) -> string\n\nConvert a time tuple to a string according to a format specification.\nSee the library reference manual for formatting codes. When the time tuple\nis not present, current time as returned by localtime() is used.\n\nCommonly used format codes:\n\n%Y Year with century as a decimal number.\n%m Month as a decimal number [01,12].\n%d Day of the month as a decimal number [01,31].\n%H Hour (24-hour clock) as a decimal number [00,23].\n%M Minute as a decimal number [00,59].\n%S Second as a decimal number [00,61].\n%z Time zone offset from UTC.\n%a Locale's abbreviated weekday name.\n%A Locale's full weekday name.\n%b Locale's abbreviated month name.\n%B Locale's full month name.\n%c Locale's appropriate date and time representation.\n%I Hour (12-hour clock) as a decimal number [01,12].\n%p Locale's equivalent of either AM or PM.\n\nOther codes may be available on your platform. See documentation for\nthe C library strftime function.", + "time.strptime" => "strptime(string, format) -> struct_time\n\nParse a string to a time tuple according to a format specification.\nSee the library reference manual for formatting codes (same as\nstrftime()).\n\nCommonly used format codes:\n\n%Y Year with century as a decimal number.\n%m Month as a decimal number [01,12].\n%d Day of the month as a decimal number [01,31].\n%H Hour (24-hour clock) as a decimal number [00,23].\n%M Minute as a decimal number [00,59].\n%S Second as a decimal number [00,61].\n%z Time zone offset from UTC.\n%a Locale's abbreviated weekday name.\n%A Locale's full weekday name.\n%b Locale's abbreviated month name.\n%B Locale's full month name.\n%c Locale's appropriate date and time representation.\n%I Hour (12-hour clock) as a decimal number [01,12].\n%p Locale's equivalent of either AM or PM.\n\nOther codes may be available on your platform. See documentation for\nthe C library strftime function.", + "time.struct_time" => "The time value as returned by gmtime(), localtime(), and strptime(), and\naccepted by asctime(), mktime() and strftime(). May be considered as a\nsequence of 9 integers.\n\nNote that several fields' values are not the same as those defined by\nthe C language standard for struct tm. For example, the value of the\nfield tm_year is the actual year, not year - 1900. See individual\nfields' descriptions for details.", + "time.struct_time.__add__" => "Return self+value.", + "time.struct_time.__class_getitem__" => "See PEP 585", + "time.struct_time.__contains__" => "Return bool(key in self).", + "time.struct_time.__delattr__" => "Implement delattr(self, name).", + "time.struct_time.__eq__" => "Return self==value.", + "time.struct_time.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "time.struct_time.__ge__" => "Return self>=value.", + "time.struct_time.__getattribute__" => "Return getattr(self, name).", + "time.struct_time.__getitem__" => "Return self[key].", + "time.struct_time.__getstate__" => "Helper for pickle.", + "time.struct_time.__gt__" => "Return self>value.", + "time.struct_time.__hash__" => "Return hash(self).", + "time.struct_time.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "time.struct_time.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "time.struct_time.__iter__" => "Implement iter(self).", + "time.struct_time.__le__" => "Return self<=value.", + "time.struct_time.__len__" => "Return len(self).", + "time.struct_time.__lt__" => "Return self "Return self*value.", + "time.struct_time.__ne__" => "Return self!=value.", + "time.struct_time.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "time.struct_time.__reduce_ex__" => "Helper for pickle.", + "time.struct_time.__replace__" => "Return a copy of the structure with new values for the specified fields.", + "time.struct_time.__repr__" => "Return repr(self).", + "time.struct_time.__rmul__" => "Return value*self.", + "time.struct_time.__setattr__" => "Implement setattr(self, name, value).", + "time.struct_time.__sizeof__" => "Size of object in memory, in bytes.", + "time.struct_time.__str__" => "Return str(self).", + "time.struct_time.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "time.struct_time.count" => "Return number of occurrences of value.", + "time.struct_time.index" => "Return first index of value.\n\nRaises ValueError if the value is not present.", + "time.struct_time.tm_gmtoff" => "offset from UTC in seconds", + "time.struct_time.tm_hour" => "hours, range [0, 23]", + "time.struct_time.tm_isdst" => "1 if summer time is in effect, 0 if not, and -1 if unknown", + "time.struct_time.tm_mday" => "day of month, range [1, 31]", + "time.struct_time.tm_min" => "minutes, range [0, 59]", + "time.struct_time.tm_mon" => "month of year, range [1, 12]", + "time.struct_time.tm_sec" => "seconds, range [0, 61])", + "time.struct_time.tm_wday" => "day of week, range [0, 6], Monday is 0", + "time.struct_time.tm_yday" => "day of year, range [1, 366]", + "time.struct_time.tm_year" => "year, for example, 1993", + "time.struct_time.tm_zone" => "abbreviation of timezone name", + "time.thread_time" => "thread_time() -> float\n\nThread time for profiling: sum of the kernel and user-space CPU time.", + "time.thread_time_ns" => "thread_time() -> int\n\nThread time for profiling as nanoseconds:\nsum of the kernel and user-space CPU time.", + "time.time" => "time() -> floating-point number\n\nReturn the current time in seconds since the Epoch.\nFractions of a second may be present if the system clock provides them.", + "time.time_ns" => "time_ns() -> int\n\nReturn the current time in nanoseconds since the Epoch.", + "time.tzset" => "tzset()\n\nInitialize, or reinitialize, the local timezone to the value stored in\nos.environ['TZ']. The TZ environment variable should be specified in\nstandard Unix timezone format as documented in the tzset man page\n(eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown timezones will silently\nfall back to UTC. If the TZ environment variable is not set, the local\ntimezone is set to the systems best guess of wallclock time.\nChanging the TZ environment variable without calling tzset *may* change\nthe local timezone used by methods such as localtime, but this behaviour\nshould not be relied on.", + "unicodedata" => "This module provides access to the Unicode Character Database which\ndefines character properties for all Unicode characters. The data in\nthis database is based on the UnicodeData.txt file version\n15.1.0 which is publicly available from ftp://ftp.unicode.org/.\n\nThe module uses the same names and symbols as defined by the\nUnicodeData File Format 15.1.0.", + "unicodedata.UCD.__delattr__" => "Implement delattr(self, name).", + "unicodedata.UCD.__eq__" => "Return self==value.", + "unicodedata.UCD.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "unicodedata.UCD.__ge__" => "Return self>=value.", + "unicodedata.UCD.__getattribute__" => "Return getattr(self, name).", + "unicodedata.UCD.__getstate__" => "Helper for pickle.", + "unicodedata.UCD.__gt__" => "Return self>value.", + "unicodedata.UCD.__hash__" => "Return hash(self).", + "unicodedata.UCD.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "unicodedata.UCD.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "unicodedata.UCD.__le__" => "Return self<=value.", + "unicodedata.UCD.__lt__" => "Return self "Return self!=value.", + "unicodedata.UCD.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "unicodedata.UCD.__reduce__" => "Helper for pickle.", + "unicodedata.UCD.__reduce_ex__" => "Helper for pickle.", + "unicodedata.UCD.__repr__" => "Return repr(self).", + "unicodedata.UCD.__setattr__" => "Implement setattr(self, name, value).", + "unicodedata.UCD.__sizeof__" => "Size of object in memory, in bytes.", + "unicodedata.UCD.__str__" => "Return str(self).", + "unicodedata.UCD.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "unicodedata.UCD.bidirectional" => "Returns the bidirectional class assigned to the character chr as string.\n\nIf no such value is defined, an empty string is returned.", + "unicodedata.UCD.category" => "Returns the general category assigned to the character chr as string.", + "unicodedata.UCD.combining" => "Returns the canonical combining class assigned to the character chr as integer.\n\nReturns 0 if no combining class is defined.", + "unicodedata.UCD.decimal" => "Converts a Unicode character into its equivalent decimal value.\n\nReturns the decimal value assigned to the character chr as integer.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.UCD.decomposition" => "Returns the character decomposition mapping assigned to the character chr as string.\n\nAn empty string is returned in case no such mapping is defined.", + "unicodedata.UCD.digit" => "Converts a Unicode character into its equivalent digit value.\n\nReturns the digit value assigned to the character chr as integer.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.UCD.east_asian_width" => "Returns the east asian width assigned to the character chr as string.", + "unicodedata.UCD.is_normalized" => "Return whether the Unicode string unistr is in the normal form 'form'.\n\nValid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'.", + "unicodedata.UCD.lookup" => "Look up character by name.\n\nIf a character with the given name is found, return the\ncorresponding character. If not found, KeyError is raised.", + "unicodedata.UCD.mirrored" => "Returns the mirrored property assigned to the character chr as integer.\n\nReturns 1 if the character has been identified as a \"mirrored\"\ncharacter in bidirectional text, 0 otherwise.", + "unicodedata.UCD.name" => "Returns the name assigned to the character chr as a string.\n\nIf no name is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.UCD.normalize" => "Return the normal form 'form' for the Unicode string unistr.\n\nValid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'.", + "unicodedata.UCD.numeric" => "Converts a Unicode character into its equivalent numeric value.\n\nReturns the numeric value assigned to the character chr as float.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.bidirectional" => "Returns the bidirectional class assigned to the character chr as string.\n\nIf no such value is defined, an empty string is returned.", + "unicodedata.category" => "Returns the general category assigned to the character chr as string.", + "unicodedata.combining" => "Returns the canonical combining class assigned to the character chr as integer.\n\nReturns 0 if no combining class is defined.", + "unicodedata.decimal" => "Converts a Unicode character into its equivalent decimal value.\n\nReturns the decimal value assigned to the character chr as integer.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.decomposition" => "Returns the character decomposition mapping assigned to the character chr as string.\n\nAn empty string is returned in case no such mapping is defined.", + "unicodedata.digit" => "Converts a Unicode character into its equivalent digit value.\n\nReturns the digit value assigned to the character chr as integer.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.east_asian_width" => "Returns the east asian width assigned to the character chr as string.", + "unicodedata.is_normalized" => "Return whether the Unicode string unistr is in the normal form 'form'.\n\nValid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'.", + "unicodedata.lookup" => "Look up character by name.\n\nIf a character with the given name is found, return the\ncorresponding character. If not found, KeyError is raised.", + "unicodedata.mirrored" => "Returns the mirrored property assigned to the character chr as integer.\n\nReturns 1 if the character has been identified as a \"mirrored\"\ncharacter in bidirectional text, 0 otherwise.", + "unicodedata.name" => "Returns the name assigned to the character chr as a string.\n\nIf no name is defined, default is returned, or, if not given,\nValueError is raised.", + "unicodedata.normalize" => "Return the normal form 'form' for the Unicode string unistr.\n\nValid values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'.", + "unicodedata.numeric" => "Converts a Unicode character into its equivalent numeric value.\n\nReturns the numeric value assigned to the character chr as float.\nIf no such value is defined, default is returned, or, if not given,\nValueError is raised.", + "winreg" => "This module provides access to the Windows registry API.\n\nFunctions:\n\nCloseKey() - Closes a registry key.\nConnectRegistry() - Establishes a connection to a predefined registry handle\n on another computer.\nCreateKey() - Creates the specified key, or opens it if it already exists.\nDeleteKey() - Deletes the specified key.\nDeleteValue() - Removes a named value from the specified registry key.\nEnumKey() - Enumerates subkeys of the specified open registry key.\nEnumValue() - Enumerates values of the specified open registry key.\nExpandEnvironmentStrings() - Expand the env strings in a REG_EXPAND_SZ\n string.\nFlushKey() - Writes all the attributes of the specified key to the registry.\nLoadKey() - Creates a subkey under HKEY_USER or HKEY_LOCAL_MACHINE and\n stores registration information from a specified file into that\n subkey.\nOpenKey() - Opens the specified key.\nOpenKeyEx() - Alias of OpenKey().\nQueryValue() - Retrieves the value associated with the unnamed value for a\n specified key in the registry.\nQueryValueEx() - Retrieves the type and data for a specified value name\n associated with an open registry key.\nQueryInfoKey() - Returns information about the specified key.\nSaveKey() - Saves the specified key, and all its subkeys a file.\nSetValue() - Associates a value with a specified key.\nSetValueEx() - Stores data in the value field of an open registry key.\n\nSpecial objects:\n\nHKEYType -- type object for HKEY objects\nerror -- exception raised for Win32 errors\n\nInteger constants:\nMany constants are defined - see the documentation for each function\nto see what constants are used, and where.", + "winreg.CloseKey" => "Closes a previously opened registry key.\n\n hkey\n A previously opened key.\n\nNote that if the key is not closed using this method, it will be\nclosed when the hkey object is destroyed by Python.", + "winreg.ConnectRegistry" => "Establishes a connection to the registry on another computer.\n\n computer_name\n The name of the remote computer, of the form r\"\\\\computername\". If\n None, the local computer is used.\n key\n The predefined key to connect to.\n\nThe return value is the handle of the opened key.\nIf the function fails, an OSError exception is raised.", + "winreg.CreateKey" => "Creates or opens the specified key.\n\n key\n An already open key, or one of the predefined HKEY_* constants.\n sub_key\n The name of the key this method opens or creates.\n\nIf key is one of the predefined keys, sub_key may be None. In that case,\nthe handle returned is the same key handle passed in to the function.\n\nIf the key already exists, this function opens the existing key.\n\nThe return value is the handle of the opened key.\nIf the function fails, an OSError exception is raised.", + "winreg.CreateKeyEx" => "Creates or opens the specified key.\n\n key\n An already open key, or one of the predefined HKEY_* constants.\n sub_key\n The name of the key this method opens or creates.\n reserved\n A reserved integer, and must be zero. Default is zero.\n access\n An integer that specifies an access mask that describes the\n desired security access for the key. Default is KEY_WRITE.\n\nIf key is one of the predefined keys, sub_key may be None. In that case,\nthe handle returned is the same key handle passed in to the function.\n\nIf the key already exists, this function opens the existing key\n\nThe return value is the handle of the opened key.\nIf the function fails, an OSError exception is raised.", + "winreg.DeleteKey" => "Deletes the specified key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that must be the name of a subkey of the key identified by\n the key parameter. This value must not be None, and the key may not\n have subkeys.\n\nThis method can not delete keys with subkeys.\n\nIf the function succeeds, the entire key, including all of its values,\nis removed. If the function fails, an OSError exception is raised.", + "winreg.DeleteKeyEx" => "Deletes the specified key (intended for 64-bit OS).\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that must be the name of a subkey of the key identified by\n the key parameter. This value must not be None, and the key may not\n have subkeys.\n access\n An integer that specifies an access mask that describes the\n desired security access for the key. Default is KEY_WOW64_64KEY.\n reserved\n A reserved integer, and must be zero. Default is zero.\n\nWhile this function is intended to be used for 64-bit OS, it is also\n available on 32-bit systems.\n\nThis method can not delete keys with subkeys.\n\nIf the function succeeds, the entire key, including all of its values,\nis removed. If the function fails, an OSError exception is raised.\nOn unsupported Windows versions, NotImplementedError is raised.", + "winreg.DeleteValue" => "Removes a named value from a registry key.\n\nkey\n An already open key, or any one of the predefined HKEY_* constants.\nvalue\n A string that identifies the value to remove.", + "winreg.DisableReflectionKey" => "Disables registry reflection for 32bit processes running on a 64bit OS.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n\nWill generally raise NotImplementedError if executed on a 32bit OS.\n\nIf the key is not on the reflection list, the function succeeds but has\nno effect. Disabling reflection for a key does not affect reflection\nof any subkeys.", + "winreg.EnableReflectionKey" => "Restores registry reflection for the specified disabled key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n\nWill generally raise NotImplementedError if executed on a 32bit OS.\nRestoring reflection for a key does not affect reflection of any\nsubkeys.", + "winreg.EnumKey" => "Enumerates subkeys of an open registry key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n index\n An integer that identifies the index of the key to retrieve.\n\nThe function retrieves the name of one subkey each time it is called.\nIt is typically called repeatedly until an OSError exception is\nraised, indicating no more values are available.", + "winreg.EnumValue" => "Enumerates values of an open registry key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n index\n An integer that identifies the index of the value to retrieve.\n\nThe function retrieves the name of one subkey each time it is called.\nIt is typically called repeatedly, until an OSError exception\nis raised, indicating no more values.\n\nThe result is a tuple of 3 items:\n value_name\n A string that identifies the value.\n value_data\n An object that holds the value data, and whose type depends\n on the underlying registry type.\n data_type\n An integer that identifies the type of the value data.", + "winreg.ExpandEnvironmentStrings" => "Expand environment vars.", + "winreg.FlushKey" => "Writes all the attributes of a key to the registry.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n\nIt is not necessary to call FlushKey to change a key. Registry changes\nare flushed to disk by the registry using its lazy flusher. Registry\nchanges are also flushed to disk at system shutdown. Unlike\nCloseKey(), the FlushKey() method returns only when all the data has\nbeen written to the registry.\n\nAn application should only call FlushKey() if it requires absolute\ncertainty that registry changes are on disk. If you don't know whether\na FlushKey() call is required, it probably isn't.", + "winreg.HKEYType" => "PyHKEY Object - A Python object, representing a win32 registry key.\n\nThis object wraps a Windows HKEY object, automatically closing it when\nthe object is destroyed. To guarantee cleanup, you can call either\nthe Close() method on the PyHKEY, or the CloseKey() method.\n\nAll functions which accept a handle object also accept an integer --\nhowever, use of the handle object is encouraged.\n\nFunctions:\nClose() - Closes the underlying handle.\nDetach() - Returns the integer Win32 handle, detaching it from the object\n\nProperties:\nhandle - The integer Win32 handle.\n\nOperations:\n__bool__ - Handles with an open object return true, otherwise false.\n__int__ - Converting a handle to an integer returns the Win32 handle.\nrich comparison - Handle objects are compared using the handle value.", + "winreg.HKEYType.Close" => "Closes the underlying Windows handle.\n\nIf the handle is already closed, no error is raised.", + "winreg.HKEYType.Detach" => "Detaches the Windows handle from the handle object.\n\nThe result is the value of the handle before it is detached. If the\nhandle is already detached, this will return zero.\n\nAfter calling this function, the handle is effectively invalidated,\nbut the handle is not closed. You would call this function when you\nneed the underlying win32 handle to exist beyond the lifetime of the\nhandle object.", + "winreg.HKEYType.__abs__" => "abs(self)", + "winreg.HKEYType.__add__" => "Return self+value.", + "winreg.HKEYType.__and__" => "Return self&value.", + "winreg.HKEYType.__bool__" => "True if self else False", + "winreg.HKEYType.__delattr__" => "Implement delattr(self, name).", + "winreg.HKEYType.__divmod__" => "Return divmod(self, value).", + "winreg.HKEYType.__eq__" => "Return self==value.", + "winreg.HKEYType.__float__" => "float(self)", + "winreg.HKEYType.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "winreg.HKEYType.__ge__" => "Return self>=value.", + "winreg.HKEYType.__getattribute__" => "Return getattr(self, name).", + "winreg.HKEYType.__getstate__" => "Helper for pickle.", + "winreg.HKEYType.__gt__" => "Return self>value.", + "winreg.HKEYType.__hash__" => "Return hash(self).", + "winreg.HKEYType.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "winreg.HKEYType.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "winreg.HKEYType.__int__" => "int(self)", + "winreg.HKEYType.__invert__" => "~self", + "winreg.HKEYType.__le__" => "Return self<=value.", + "winreg.HKEYType.__lshift__" => "Return self< "Return self "Return self%value.", + "winreg.HKEYType.__mul__" => "Return self*value.", + "winreg.HKEYType.__ne__" => "Return self!=value.", + "winreg.HKEYType.__neg__" => "-self", + "winreg.HKEYType.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "winreg.HKEYType.__or__" => "Return self|value.", + "winreg.HKEYType.__pos__" => "+self", + "winreg.HKEYType.__pow__" => "Return pow(self, value, mod).", + "winreg.HKEYType.__radd__" => "Return value+self.", + "winreg.HKEYType.__rand__" => "Return value&self.", + "winreg.HKEYType.__rdivmod__" => "Return divmod(value, self).", + "winreg.HKEYType.__reduce__" => "Helper for pickle.", + "winreg.HKEYType.__reduce_ex__" => "Helper for pickle.", + "winreg.HKEYType.__repr__" => "Return repr(self).", + "winreg.HKEYType.__rlshift__" => "Return value< "Return value%self.", + "winreg.HKEYType.__rmul__" => "Return value*self.", + "winreg.HKEYType.__ror__" => "Return value|self.", + "winreg.HKEYType.__rpow__" => "Return pow(value, self, mod).", + "winreg.HKEYType.__rrshift__" => "Return value>>self.", + "winreg.HKEYType.__rshift__" => "Return self>>value.", + "winreg.HKEYType.__rsub__" => "Return value-self.", + "winreg.HKEYType.__rxor__" => "Return value^self.", + "winreg.HKEYType.__setattr__" => "Implement setattr(self, name, value).", + "winreg.HKEYType.__sizeof__" => "Size of object in memory, in bytes.", + "winreg.HKEYType.__str__" => "Return str(self).", + "winreg.HKEYType.__sub__" => "Return self-value.", + "winreg.HKEYType.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "winreg.HKEYType.__xor__" => "Return self^value.", + "winreg.LoadKey" => "Insert data into the registry from a file.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that identifies the sub-key to load.\n file_name\n The name of the file to load registry data from. This file must\n have been created with the SaveKey() function. Under the file\n allocation table (FAT) file system, the filename may not have an\n extension.\n\nCreates a subkey under the specified key and stores registration\ninformation from a specified file into that subkey.\n\nA call to LoadKey() fails if the calling process does not have the\nSE_RESTORE_PRIVILEGE privilege.\n\nIf key is a handle returned by ConnectRegistry(), then the path\nspecified in fileName is relative to the remote computer.\n\nThe MSDN docs imply key must be in the HKEY_USER or HKEY_LOCAL_MACHINE\ntree.", + "winreg.OpenKey" => "Opens the specified key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that identifies the sub_key to open.\n reserved\n A reserved integer that must be zero. Default is zero.\n access\n An integer that specifies an access mask that describes the desired\n security access for the key. Default is KEY_READ.\n\nThe result is a new handle to the specified key.\nIf the function fails, an OSError exception is raised.", + "winreg.OpenKeyEx" => "Opens the specified key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that identifies the sub_key to open.\n reserved\n A reserved integer that must be zero. Default is zero.\n access\n An integer that specifies an access mask that describes the desired\n security access for the key. Default is KEY_READ.\n\nThe result is a new handle to the specified key.\nIf the function fails, an OSError exception is raised.", + "winreg.QueryInfoKey" => "Returns information about a key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n\nThe result is a tuple of 3 items:\nAn integer that identifies the number of sub keys this key has.\nAn integer that identifies the number of values this key has.\nAn integer that identifies when the key was last modified (if available)\nas 100's of nanoseconds since Jan 1, 1600.", + "winreg.QueryReflectionKey" => "Returns the reflection state for the specified key as a bool.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n\nWill generally raise NotImplementedError if executed on a 32bit OS.", + "winreg.QueryValue" => "Retrieves the unnamed value for a key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that holds the name of the subkey with which the value\n is associated. If this parameter is None or empty, the function\n retrieves the value set by the SetValue() method for the key\n identified by key.\n\nValues in the registry have name, type, and data components. This method\nretrieves the data for a key's first value that has a NULL name.\nBut since the underlying API call doesn't return the type, you'll\nprobably be happier using QueryValueEx; this function is just here for\ncompleteness.", + "winreg.QueryValueEx" => "Retrieves the type and value of a specified sub-key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n name\n A string indicating the value to query.\n\nBehaves mostly like QueryValue(), but also returns the type of the\nspecified value name associated with the given open registry key.\n\nThe return value is a tuple of the value and the type_id.", + "winreg.SaveKey" => "Saves the specified key, and all its subkeys to the specified file.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n file_name\n The name of the file to save registry data to. This file cannot\n already exist. If this filename includes an extension, it cannot be\n used on file allocation table (FAT) file systems by the LoadKey(),\n ReplaceKey() or RestoreKey() methods.\n\nIf key represents a key on a remote computer, the path described by\nfile_name is relative to the remote computer.\n\nThe caller of this method must possess the SeBackupPrivilege\nsecurity privilege. This function passes NULL for security_attributes\nto the API.", + "winreg.SetValue" => "Associates a value with a specified key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n sub_key\n A string that names the subkey with which the value is associated.\n type\n An integer that specifies the type of the data. Currently this must\n be REG_SZ, meaning only strings are supported.\n value\n A string that specifies the new value.\n\nIf the key specified by the sub_key parameter does not exist, the\nSetValue function creates it.\n\nValue lengths are limited by available memory. Long values (more than\n2048 bytes) should be stored as files with the filenames stored in\nthe configuration registry to help the registry perform efficiently.\n\nThe key identified by the key parameter must have been opened with\nKEY_SET_VALUE access.", + "winreg.SetValueEx" => "Stores data in the value field of an open registry key.\n\n key\n An already open key, or any one of the predefined HKEY_* constants.\n value_name\n A string containing the name of the value to set, or None.\n reserved\n Can be anything - zero is always passed to the API.\n type\n An integer that specifies the type of the data, one of:\n REG_BINARY -- Binary data in any form.\n REG_DWORD -- A 32-bit number.\n REG_DWORD_LITTLE_ENDIAN -- A 32-bit number in little-endian format. Equivalent to REG_DWORD\n REG_DWORD_BIG_ENDIAN -- A 32-bit number in big-endian format.\n REG_EXPAND_SZ -- A null-terminated string that contains unexpanded\n references to environment variables (for example,\n %PATH%).\n REG_LINK -- A Unicode symbolic link.\n REG_MULTI_SZ -- A sequence of null-terminated strings, terminated\n by two null characters. Note that Python handles\n this termination automatically.\n REG_NONE -- No defined value type.\n REG_QWORD -- A 64-bit number.\n REG_QWORD_LITTLE_ENDIAN -- A 64-bit number in little-endian format. Equivalent to REG_QWORD.\n REG_RESOURCE_LIST -- A device-driver resource list.\n REG_SZ -- A null-terminated string.\n value\n A string that specifies the new value.\n\nThis method can also set additional value and type information for the\nspecified key. The key identified by the key parameter must have been\nopened with KEY_SET_VALUE access.\n\nTo open the key, use the CreateKeyEx() or OpenKeyEx() methods.\n\nValue lengths are limited by available memory. Long values (more than\n2048 bytes) should be stored as files with the filenames stored in\nthe configuration registry to help the registry perform efficiently.", + "winsound" => "PlaySound(sound, flags) - play a sound\nSND_FILENAME - sound is a wav file name\nSND_ALIAS - sound is a registry sound association name\nSND_LOOP - Play the sound repeatedly; must also specify SND_ASYNC\nSND_MEMORY - sound is a memory image of a wav file\nSND_PURGE - stop all instances of the specified sound\nSND_ASYNC - PlaySound returns immediately\nSND_NODEFAULT - Do not play a default beep if the sound can not be found\nSND_NOSTOP - Do not interrupt any sounds currently playing\nSND_NOWAIT - Return immediately if the sound driver is busy\nSND_APPLICATION - sound is an application-specific alias in the registry.\nBeep(frequency, duration) - Make a beep through the PC speaker.\nMessageBeep(type) - Call Windows MessageBeep.", + "winsound.Beep" => "A wrapper around the Windows Beep API.\n\nfrequency\n Frequency of the sound in hertz.\n Must be in the range 37 through 32,767.\nduration\n How long the sound should play, in milliseconds.", + "winsound.MessageBeep" => "Call Windows MessageBeep(x).\n\nx defaults to MB_OK.", + "winsound.PlaySound" => "A wrapper around the Windows PlaySound API.\n\nsound\n The sound to play; a filename, data, or None.\nflags\n Flag values, ored together. See module documentation.", + "zlib" => "The functions in this module allow compression and decompression using the\nzlib library, which is based on GNU zip.\n\nadler32(string[, start]) -- Compute an Adler-32 checksum.\ncompress(data[, level]) -- Compress data, with compression level 0-9 or -1.\ncompressobj([level[, ...]]) -- Return a compressor object.\ncrc32(string[, start]) -- Compute a CRC-32 checksum.\ndecompress(string,[wbits],[bufsize]) -- Decompresses a compressed string.\ndecompressobj([wbits[, zdict]]) -- Return a decompressor object.\n\n'wbits' is window buffer size and container format.\nCompressor objects support compress() and flush() methods; decompressor\nobjects support decompress() and flush().", + "zlib._ZlibDecompressor" => "Create a decompressor object for decompressing data incrementally.\n\nwbits = 15\nzdict\n The predefined compression dictionary. This is a sequence of bytes\n (such as a bytes object) containing subsequences that are expected\n to occur frequently in the data that is to be compressed. Those\n subsequences that are expected to be most common should come at the\n end of the dictionary. This must be the same dictionary as used by the\n compressor that produced the input data.", + "zlib._ZlibDecompressor.__delattr__" => "Implement delattr(self, name).", + "zlib._ZlibDecompressor.__eq__" => "Return self==value.", + "zlib._ZlibDecompressor.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "zlib._ZlibDecompressor.__ge__" => "Return self>=value.", + "zlib._ZlibDecompressor.__getattribute__" => "Return getattr(self, name).", + "zlib._ZlibDecompressor.__getstate__" => "Helper for pickle.", + "zlib._ZlibDecompressor.__gt__" => "Return self>value.", + "zlib._ZlibDecompressor.__hash__" => "Return hash(self).", + "zlib._ZlibDecompressor.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "zlib._ZlibDecompressor.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "zlib._ZlibDecompressor.__le__" => "Return self<=value.", + "zlib._ZlibDecompressor.__lt__" => "Return self "Return self!=value.", + "zlib._ZlibDecompressor.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "zlib._ZlibDecompressor.__reduce__" => "Helper for pickle.", + "zlib._ZlibDecompressor.__reduce_ex__" => "Helper for pickle.", + "zlib._ZlibDecompressor.__repr__" => "Return repr(self).", + "zlib._ZlibDecompressor.__setattr__" => "Implement setattr(self, name, value).", + "zlib._ZlibDecompressor.__sizeof__" => "Size of object in memory, in bytes.", + "zlib._ZlibDecompressor.__str__" => "Return str(self).", + "zlib._ZlibDecompressor.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "zlib._ZlibDecompressor.decompress" => "Decompress *data*, returning uncompressed data as bytes.\n\nIf *max_length* is nonnegative, returns at most *max_length* bytes of\ndecompressed data. If this limit is reached and further output can be\nproduced, *self.needs_input* will be set to ``False``. In this case, the next\ncall to *decompress()* may provide *data* as b'' to obtain more of the output.\n\nIf all of the input data was decompressed and returned (either because this\nwas less than *max_length* bytes, or because *max_length* was negative),\n*self.needs_input* will be set to True.\n\nAttempting to decompress data after the end of stream is reached raises an\nEOFError. Any data found after the end of the stream is ignored and saved in\nthe unused_data attribute.", + "zlib._ZlibDecompressor.eof" => "True if the end-of-stream marker has been reached.", + "zlib._ZlibDecompressor.needs_input" => "True if more input is needed before more decompressed data can be produced.", + "zlib._ZlibDecompressor.unused_data" => "Data found after the end of the compressed stream.", + "zlib.adler32" => "Compute an Adler-32 checksum of data.\n\n value\n Starting value of the checksum.\n\nThe returned checksum is an integer.", + "zlib.compress" => "Returns a bytes object containing compressed data.\n\ndata\n Binary data to be compressed.\nlevel\n Compression level, in 0-9 or -1.\nwbits\n The window buffer size and container format.", + "zlib.compressobj" => "Return a compressor object.\n\nlevel\n The compression level (an integer in the range 0-9 or -1; default is\n currently equivalent to 6). Higher compression levels are slower,\n but produce smaller results.\nmethod\n The compression algorithm. If given, this must be DEFLATED.\nwbits\n +9 to +15: The base-two logarithm of the window size. Include a zlib\n container.\n -9 to -15: Generate a raw stream.\n +25 to +31: Include a gzip container.\nmemLevel\n Controls the amount of memory used for internal compression state.\n Valid values range from 1 to 9. Higher values result in higher memory\n usage, faster compression, and smaller output.\nstrategy\n Used to tune the compression algorithm. Possible values are\n Z_DEFAULT_STRATEGY, Z_FILTERED, and Z_HUFFMAN_ONLY.\nzdict\n The predefined compression dictionary - a sequence of bytes\n containing subsequences that are likely to occur in the input data.", + "zlib.crc32" => "Compute a CRC-32 checksum of data.\n\n value\n Starting value of the checksum.\n\nThe returned checksum is an integer.", + "zlib.decompress" => "Returns a bytes object containing the uncompressed data.\n\ndata\n Compressed data.\nwbits\n The window buffer size and container format.\nbufsize\n The initial output buffer size.", + "zlib.decompressobj" => "Return a decompressor object.\n\nwbits\n The window buffer size and container format.\nzdict\n The predefined compression dictionary. This must be the same\n dictionary as used by the compressor that produced the input data.", + "zlib.error.__cause__" => "exception cause", + "zlib.error.__context__" => "exception context", + "zlib.error.__delattr__" => "Implement delattr(self, name).", + "zlib.error.__eq__" => "Return self==value.", + "zlib.error.__format__" => "Default object formatter.\n\nReturn str(self) if format_spec is empty. Raise TypeError otherwise.", + "zlib.error.__ge__" => "Return self>=value.", + "zlib.error.__getattribute__" => "Return getattr(self, name).", + "zlib.error.__getstate__" => "Helper for pickle.", + "zlib.error.__gt__" => "Return self>value.", + "zlib.error.__hash__" => "Return hash(self).", + "zlib.error.__init__" => "Initialize self. See help(type(self)) for accurate signature.", + "zlib.error.__init_subclass__" => "This method is called when a class is subclassed.\n\nThe default implementation does nothing. It may be\noverridden to extend subclasses.", + "zlib.error.__le__" => "Return self<=value.", + "zlib.error.__lt__" => "Return self "Return self!=value.", + "zlib.error.__new__" => "Create and return a new object. See help(type) for accurate signature.", + "zlib.error.__reduce_ex__" => "Helper for pickle.", + "zlib.error.__repr__" => "Return repr(self).", + "zlib.error.__setattr__" => "Implement setattr(self, name, value).", + "zlib.error.__sizeof__" => "Size of object in memory, in bytes.", + "zlib.error.__str__" => "Return str(self).", + "zlib.error.__subclasshook__" => "Abstract classes can override this to customize issubclass().\n\nThis is invoked early on by abc.ABCMeta.__subclasscheck__().\nIt should return True, False or NotImplemented. If it returns\nNotImplemented, the normal algorithm is used. Otherwise, it\noverrides the normal algorithm (and the outcome is cached).", + "zlib.error.__weakref__" => "list of weak references to the object", + "zlib.error.add_note" => "Exception.add_note(note) --\nadd a note to the exception", + "zlib.error.with_traceback" => "Exception.with_traceback(tb) --\nset self.__traceback__ to tb and return self.", +}; diff --git a/crates/doc/src/lib.rs b/crates/doc/src/lib.rs new file mode 100644 index 00000000000..cc81f17fc55 --- /dev/null +++ b/crates/doc/src/lib.rs @@ -0,0 +1,12 @@ +include!("./data.inc.rs"); + +#[cfg(test)] +mod test { + use super::DB; + + #[test] + fn test_db() { + let doc = DB.get("array._array_reconstructor"); + assert!(doc.is_some()); + } +} diff --git a/derive-impl/Cargo.toml b/derive-impl/Cargo.toml index 66ed67cef81..a772cab1d38 100644 --- a/derive-impl/Cargo.toml +++ b/derive-impl/Cargo.toml @@ -23,4 +23,4 @@ syn-ext = { version = "0.5.0", features = ["full"] } textwrap = { version = "0.16.1", default-features = false } [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/derive-impl/src/lib.rs b/derive-impl/src/lib.rs index a1f97c96b0e..786c77e3212 100644 --- a/derive-impl/src/lib.rs +++ b/derive-impl/src/lib.rs @@ -23,7 +23,6 @@ mod pytraverse; use error::Diagnostic; use proc_macro2::TokenStream; use quote::ToTokens; -use rustpython_doc as doc; use syn::{DeriveInput, Item}; use syn_ext::types::PunctuatedNestedMeta; diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 0b0769cac3c..84559e3574e 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -6,6 +6,7 @@ use crate::util::{ }; use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{ToTokens, quote, quote_spanned}; +use rustpython_doc::DB; use std::collections::{HashMap, HashSet}; use std::str::FromStr; use syn::{Attribute, Ident, Item, Result, parse_quote, spanned::Spanned}; @@ -315,10 +316,8 @@ fn generate_class_def( ) -> Result { let doc = attrs.doc().or_else(|| { let module_name = module_name.unwrap_or("builtins"); - crate::doc::Database::shared() - .try_module_item(module_name, name) - .ok() - .flatten() + DB.get(&format!("{module_name}.{name}")) + .copied() .map(str::to_owned) }); let doc = if let Some(doc) = doc { diff --git a/derive-impl/src/pymodule.rs b/derive-impl/src/pymodule.rs index a8588a762ef..9db7128b3a1 100644 --- a/derive-impl/src/pymodule.rs +++ b/derive-impl/src/pymodule.rs @@ -6,6 +6,7 @@ use crate::util::{ }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{ToTokens, quote, quote_spanned}; +use rustpython_doc::DB; use std::{collections::HashSet, str::FromStr}; use syn::{Attribute, Ident, Item, Result, parse_quote, spanned::Spanned}; use syn_ext::ext::*; @@ -100,13 +101,7 @@ pub fn impl_pymodule(attr: PunctuatedNestedMeta, module_item: Item) -> Result Date: Fri, 14 Nov 2025 23:55:17 +0900 Subject: [PATCH 0014/1459] implement sys.implementation cache_tag (#6255) --- vm/src/stdlib/sys.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vm/src/stdlib/sys.rs b/vm/src/stdlib/sys.rs index d650f72443e..12a69c23138 100644 --- a/vm/src/stdlib/sys.rs +++ b/vm/src/stdlib/sys.rs @@ -229,11 +229,13 @@ mod sys { #[pyattr] fn implementation(vm: &VirtualMachine) -> PyRef { - // TODO: Add crate version to this namespace + const NAME: &str = "rustpython"; + + let cache_tag = format!("{NAME}-{}{}", version::MAJOR, version::MINOR); let ctx = &vm.ctx; py_namespace!(vm, { - "name" => ctx.new_str(ascii!("rustpython")), - "cache_tag" => ctx.new_str(ascii!("rustpython-01")), + "name" => ctx.new_str(NAME), + "cache_tag" => ctx.new_str(cache_tag), "_multiarch" => ctx.new_str(MULTIARCH.to_owned()), "version" => version_info(vm), "hexversion" => ctx.new_int(version::VERSION_HEX), From 5eac229eae35ed1c5547771ae9375badf091e7df Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 00:05:08 +0200 Subject: [PATCH 0015/1459] Move `pylib` -> `crates/pylib` (#6225) * Move `pylib * clean `build.rs` a bit --- Cargo.toml | 3 +-- {pylib => crates/pylib}/Cargo.toml | 0 crates/pylib/Lib | 1 + {pylib => crates/pylib}/build.rs | 8 +++++--- {pylib => crates/pylib}/src/lib.rs | 0 example_projects/frozen_stdlib/Cargo.toml | 2 +- pylib/Lib | 1 - 7 files changed, 8 insertions(+), 7 deletions(-) rename {pylib => crates/pylib}/Cargo.toml (100%) create mode 120000 crates/pylib/Lib rename {pylib => crates/pylib}/build.rs (80%) rename {pylib => crates/pylib}/src/lib.rs (100%) delete mode 120000 pylib/Lib diff --git a/Cargo.toml b/Cargo.toml index b01734ae7ae..09be0c4595d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,7 +131,6 @@ members = [ "jit", "vm", "vm/sre_engine", - "pylib", "stdlib", "derive-impl", "wtf8", @@ -157,7 +156,7 @@ 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-pylib = { path = "crates/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" } diff --git a/pylib/Cargo.toml b/crates/pylib/Cargo.toml similarity index 100% rename from pylib/Cargo.toml rename to crates/pylib/Cargo.toml diff --git a/crates/pylib/Lib b/crates/pylib/Lib new file mode 120000 index 00000000000..5665252daf7 --- /dev/null +++ b/crates/pylib/Lib @@ -0,0 +1 @@ +../../Lib/ \ No newline at end of file diff --git a/pylib/build.rs b/crates/pylib/build.rs similarity index 80% rename from pylib/build.rs rename to crates/pylib/build.rs index f85c4b5cfb8..8885870e40f 100644 --- a/pylib/build.rs +++ b/crates/pylib/build.rs @@ -1,10 +1,12 @@ +const CRATE_ROOT: &str = "../.."; + fn main() { - process_python_libs("../vm/Lib/python_builtins/*"); + process_python_libs(format!("{CRATE_ROOT}/vm/Lib/python_builtins/*").as_str()); + process_python_libs(format!("{CRATE_ROOT}/vm/Lib/core_modules/*").as_str()); - process_python_libs("../vm/Lib/core_modules/*"); #[cfg(feature = "freeze-stdlib")] if cfg!(windows) { - process_python_libs("../Lib/**/*"); + process_python_libs(format!("{CRATE_ROOT}/Lib/**/*").as_str()); } else { process_python_libs("./Lib/**/*"); } diff --git a/pylib/src/lib.rs b/crates/pylib/src/lib.rs similarity index 100% rename from pylib/src/lib.rs rename to crates/pylib/src/lib.rs diff --git a/example_projects/frozen_stdlib/Cargo.toml b/example_projects/frozen_stdlib/Cargo.toml index 78a88988d8a..6824865cbf7 100644 --- a/example_projects/frozen_stdlib/Cargo.toml +++ b/example_projects/frozen_stdlib/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] rustpython = { path = "../../", default-features = false, features = ["freeze-stdlib"] } rustpython-vm = { path = "../../vm", default-features = false, features = ["freeze-stdlib"] } -rustpython-pylib = { path = "../../pylib", default-features = false, features = ["freeze-stdlib"] } +rustpython-pylib = { path = "../../crates/pylib", default-features = false, features = ["freeze-stdlib"] } [workspace] diff --git a/pylib/Lib b/pylib/Lib deleted file mode 120000 index 47f928ff4d5..00000000000 --- a/pylib/Lib +++ /dev/null @@ -1 +0,0 @@ -../Lib \ No newline at end of file From 4b7e49a17e5f86d60e49c6aa1b37e3a5ee13f0a4 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 00:19:53 +0200 Subject: [PATCH 0016/1459] Move `common` -> `crates/common` (#6256) --- Cargo.toml | 3 +-- {common => crates/common}/Cargo.toml | 0 {common => crates/common}/src/atomic.rs | 0 {common => crates/common}/src/borrow.rs | 0 {common => crates/common}/src/boxvec.rs | 0 {common => crates/common}/src/cformat.rs | 0 {common => crates/common}/src/crt_fd.rs | 0 {common => crates/common}/src/encodings.rs | 0 {common => crates/common}/src/fileutils.rs | 0 {common => crates/common}/src/float_ops.rs | 0 {common => crates/common}/src/format.rs | 0 {common => crates/common}/src/hash.rs | 0 {common => crates/common}/src/int.rs | 0 {common => crates/common}/src/lib.rs | 0 {common => crates/common}/src/linked_list.rs | 0 {common => crates/common}/src/lock.rs | 0 {common => crates/common}/src/lock/cell_lock.rs | 0 {common => crates/common}/src/lock/immutable_mutex.rs | 0 {common => crates/common}/src/lock/thread_mutex.rs | 0 {common => crates/common}/src/macros.rs | 0 {common => crates/common}/src/os.rs | 0 {common => crates/common}/src/rand.rs | 0 {common => crates/common}/src/rc.rs | 0 {common => crates/common}/src/refcount.rs | 0 {common => crates/common}/src/static_cell.rs | 0 {common => crates/common}/src/str.rs | 0 {common => crates/common}/src/windows.rs | 0 27 files changed, 1 insertion(+), 2 deletions(-) rename {common => crates/common}/Cargo.toml (100%) rename {common => crates/common}/src/atomic.rs (100%) rename {common => crates/common}/src/borrow.rs (100%) rename {common => crates/common}/src/boxvec.rs (100%) rename {common => crates/common}/src/cformat.rs (100%) rename {common => crates/common}/src/crt_fd.rs (100%) rename {common => crates/common}/src/encodings.rs (100%) rename {common => crates/common}/src/fileutils.rs (100%) rename {common => crates/common}/src/float_ops.rs (100%) rename {common => crates/common}/src/format.rs (100%) rename {common => crates/common}/src/hash.rs (100%) rename {common => crates/common}/src/int.rs (100%) rename {common => crates/common}/src/lib.rs (100%) rename {common => crates/common}/src/linked_list.rs (100%) rename {common => crates/common}/src/lock.rs (100%) rename {common => crates/common}/src/lock/cell_lock.rs (100%) rename {common => crates/common}/src/lock/immutable_mutex.rs (100%) rename {common => crates/common}/src/lock/thread_mutex.rs (100%) rename {common => crates/common}/src/macros.rs (100%) rename {common => crates/common}/src/os.rs (100%) rename {common => crates/common}/src/rand.rs (100%) rename {common => crates/common}/src/rc.rs (100%) rename {common => crates/common}/src/refcount.rs (100%) rename {common => crates/common}/src/static_cell.rs (100%) rename {common => crates/common}/src/str.rs (100%) rename {common => crates/common}/src/windows.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 09be0c4595d..795cfb06571 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,6 @@ members = [ "compiler/codegen", "compiler/literal", ".", - "common", "derive", "jit", "vm", @@ -150,7 +149,7 @@ license = "MIT" 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-common = { path = "crates/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" } diff --git a/common/Cargo.toml b/crates/common/Cargo.toml similarity index 100% rename from common/Cargo.toml rename to crates/common/Cargo.toml diff --git a/common/src/atomic.rs b/crates/common/src/atomic.rs similarity index 100% rename from common/src/atomic.rs rename to crates/common/src/atomic.rs diff --git a/common/src/borrow.rs b/crates/common/src/borrow.rs similarity index 100% rename from common/src/borrow.rs rename to crates/common/src/borrow.rs diff --git a/common/src/boxvec.rs b/crates/common/src/boxvec.rs similarity index 100% rename from common/src/boxvec.rs rename to crates/common/src/boxvec.rs diff --git a/common/src/cformat.rs b/crates/common/src/cformat.rs similarity index 100% rename from common/src/cformat.rs rename to crates/common/src/cformat.rs diff --git a/common/src/crt_fd.rs b/crates/common/src/crt_fd.rs similarity index 100% rename from common/src/crt_fd.rs rename to crates/common/src/crt_fd.rs diff --git a/common/src/encodings.rs b/crates/common/src/encodings.rs similarity index 100% rename from common/src/encodings.rs rename to crates/common/src/encodings.rs diff --git a/common/src/fileutils.rs b/crates/common/src/fileutils.rs similarity index 100% rename from common/src/fileutils.rs rename to crates/common/src/fileutils.rs diff --git a/common/src/float_ops.rs b/crates/common/src/float_ops.rs similarity index 100% rename from common/src/float_ops.rs rename to crates/common/src/float_ops.rs diff --git a/common/src/format.rs b/crates/common/src/format.rs similarity index 100% rename from common/src/format.rs rename to crates/common/src/format.rs diff --git a/common/src/hash.rs b/crates/common/src/hash.rs similarity index 100% rename from common/src/hash.rs rename to crates/common/src/hash.rs diff --git a/common/src/int.rs b/crates/common/src/int.rs similarity index 100% rename from common/src/int.rs rename to crates/common/src/int.rs diff --git a/common/src/lib.rs b/crates/common/src/lib.rs similarity index 100% rename from common/src/lib.rs rename to crates/common/src/lib.rs diff --git a/common/src/linked_list.rs b/crates/common/src/linked_list.rs similarity index 100% rename from common/src/linked_list.rs rename to crates/common/src/linked_list.rs diff --git a/common/src/lock.rs b/crates/common/src/lock.rs similarity index 100% rename from common/src/lock.rs rename to crates/common/src/lock.rs diff --git a/common/src/lock/cell_lock.rs b/crates/common/src/lock/cell_lock.rs similarity index 100% rename from common/src/lock/cell_lock.rs rename to crates/common/src/lock/cell_lock.rs diff --git a/common/src/lock/immutable_mutex.rs b/crates/common/src/lock/immutable_mutex.rs similarity index 100% rename from common/src/lock/immutable_mutex.rs rename to crates/common/src/lock/immutable_mutex.rs diff --git a/common/src/lock/thread_mutex.rs b/crates/common/src/lock/thread_mutex.rs similarity index 100% rename from common/src/lock/thread_mutex.rs rename to crates/common/src/lock/thread_mutex.rs diff --git a/common/src/macros.rs b/crates/common/src/macros.rs similarity index 100% rename from common/src/macros.rs rename to crates/common/src/macros.rs diff --git a/common/src/os.rs b/crates/common/src/os.rs similarity index 100% rename from common/src/os.rs rename to crates/common/src/os.rs diff --git a/common/src/rand.rs b/crates/common/src/rand.rs similarity index 100% rename from common/src/rand.rs rename to crates/common/src/rand.rs diff --git a/common/src/rc.rs b/crates/common/src/rc.rs similarity index 100% rename from common/src/rc.rs rename to crates/common/src/rc.rs diff --git a/common/src/refcount.rs b/crates/common/src/refcount.rs similarity index 100% rename from common/src/refcount.rs rename to crates/common/src/refcount.rs diff --git a/common/src/static_cell.rs b/crates/common/src/static_cell.rs similarity index 100% rename from common/src/static_cell.rs rename to crates/common/src/static_cell.rs diff --git a/common/src/str.rs b/crates/common/src/str.rs similarity index 100% rename from common/src/str.rs rename to crates/common/src/str.rs diff --git a/common/src/windows.rs b/crates/common/src/windows.rs similarity index 100% rename from common/src/windows.rs rename to crates/common/src/windows.rs From 3728879baf54a7f3b6c79a6c00a380bb30766ec1 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 00:21:04 +0200 Subject: [PATCH 0017/1459] Move `wtf8` -> `crates/wtf8` (#6257) --- Cargo.toml | 3 +-- {wtf8 => crates/wtf8}/Cargo.toml | 0 {wtf8 => crates/wtf8}/src/core_char.rs | 0 {wtf8 => crates/wtf8}/src/core_str.rs | 0 {wtf8 => crates/wtf8}/src/core_str_count.rs | 0 {wtf8 => crates/wtf8}/src/lib.rs | 0 6 files changed, 1 insertion(+), 2 deletions(-) rename {wtf8 => crates/wtf8}/Cargo.toml (100%) rename {wtf8 => crates/wtf8}/src/core_char.rs (100%) rename {wtf8 => crates/wtf8}/src/core_str.rs (100%) rename {wtf8 => crates/wtf8}/src/core_str_count.rs (100%) rename {wtf8 => crates/wtf8}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 795cfb06571..6a3f3ad739b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,6 @@ members = [ "vm/sre_engine", "stdlib", "derive-impl", - "wtf8", "wasm/lib", "crates/*", ] @@ -158,7 +157,7 @@ rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/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-wtf8 = { path = "crates/wtf8", version = "0.4.0" } rustpython-doc = { path = "crates/doc", version = "0.4.0" } ruff_python_parser = { git = "https://github.com/astral-sh/ruff.git", tag = "0.14.1" } diff --git a/wtf8/Cargo.toml b/crates/wtf8/Cargo.toml similarity index 100% rename from wtf8/Cargo.toml rename to crates/wtf8/Cargo.toml diff --git a/wtf8/src/core_char.rs b/crates/wtf8/src/core_char.rs similarity index 100% rename from wtf8/src/core_char.rs rename to crates/wtf8/src/core_char.rs diff --git a/wtf8/src/core_str.rs b/crates/wtf8/src/core_str.rs similarity index 100% rename from wtf8/src/core_str.rs rename to crates/wtf8/src/core_str.rs diff --git a/wtf8/src/core_str_count.rs b/crates/wtf8/src/core_str_count.rs similarity index 100% rename from wtf8/src/core_str_count.rs rename to crates/wtf8/src/core_str_count.rs diff --git a/wtf8/src/lib.rs b/crates/wtf8/src/lib.rs similarity index 100% rename from wtf8/src/lib.rs rename to crates/wtf8/src/lib.rs From bb54c5b0e61802563535f815f9e88e29bf2b660a Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 00:21:29 +0200 Subject: [PATCH 0018/1459] Move `compiler-core` -> `crates/compiler-core` (#6258) --- Cargo.toml | 3 +-- {compiler/core => crates/compiler-core}/Cargo.toml | 0 {compiler/core => crates/compiler-core}/src/bytecode.rs | 0 {compiler/core => crates/compiler-core}/src/frozen.rs | 0 {compiler/core => crates/compiler-core}/src/lib.rs | 0 {compiler/core => crates/compiler-core}/src/marshal.rs | 0 {compiler/core => crates/compiler-core}/src/mode.rs | 0 7 files changed, 1 insertion(+), 2 deletions(-) rename {compiler/core => crates/compiler-core}/Cargo.toml (100%) rename {compiler/core => crates/compiler-core}/src/bytecode.rs (100%) rename {compiler/core => crates/compiler-core}/src/frozen.rs (100%) rename {compiler/core => crates/compiler-core}/src/lib.rs (100%) rename {compiler/core => crates/compiler-core}/src/marshal.rs (100%) rename {compiler/core => crates/compiler-core}/src/mode.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 6a3f3ad739b..324ea4a96f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,6 @@ template = "installer-config/installer.wxs" resolver = "2" members = [ "compiler", - "compiler/core", "compiler/codegen", "compiler/literal", ".", @@ -145,7 +144,7 @@ repository = "https://github.com/RustPython/RustPython" license = "MIT" [workspace.dependencies] -rustpython-compiler-core = { path = "compiler/core", version = "0.4.0" } +rustpython-compiler-core = { path = "crates/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 = "crates/common", version = "0.4.0" } diff --git a/compiler/core/Cargo.toml b/crates/compiler-core/Cargo.toml similarity index 100% rename from compiler/core/Cargo.toml rename to crates/compiler-core/Cargo.toml diff --git a/compiler/core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs similarity index 100% rename from compiler/core/src/bytecode.rs rename to crates/compiler-core/src/bytecode.rs diff --git a/compiler/core/src/frozen.rs b/crates/compiler-core/src/frozen.rs similarity index 100% rename from compiler/core/src/frozen.rs rename to crates/compiler-core/src/frozen.rs diff --git a/compiler/core/src/lib.rs b/crates/compiler-core/src/lib.rs similarity index 100% rename from compiler/core/src/lib.rs rename to crates/compiler-core/src/lib.rs diff --git a/compiler/core/src/marshal.rs b/crates/compiler-core/src/marshal.rs similarity index 100% rename from compiler/core/src/marshal.rs rename to crates/compiler-core/src/marshal.rs diff --git a/compiler/core/src/mode.rs b/crates/compiler-core/src/mode.rs similarity index 100% rename from compiler/core/src/mode.rs rename to crates/compiler-core/src/mode.rs From 2071fa2e69f30b30436ceb7d9936d3fa6718d298 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 00:27:04 +0200 Subject: [PATCH 0019/1459] Move `compiler/literal` -> `crates/literal` (#6259) * Move `compiler/literal` -> `crates/compiler-literal` * Use correct crate name --- Cargo.toml | 3 +-- {compiler => crates}/literal/Cargo.toml | 0 {compiler => crates}/literal/src/char.rs | 0 {compiler => crates}/literal/src/complex.rs | 0 {compiler => crates}/literal/src/escape.rs | 0 {compiler => crates}/literal/src/float.rs | 0 {compiler => crates}/literal/src/format.rs | 0 {compiler => crates}/literal/src/lib.rs | 0 8 files changed, 1 insertion(+), 2 deletions(-) rename {compiler => crates}/literal/Cargo.toml (100%) rename {compiler => crates}/literal/src/char.rs (100%) rename {compiler => crates}/literal/src/complex.rs (100%) rename {compiler => crates}/literal/src/escape.rs (100%) rename {compiler => crates}/literal/src/float.rs (100%) rename {compiler => crates}/literal/src/format.rs (100%) rename {compiler => crates}/literal/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 324ea4a96f5..d3bcfc88f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,7 +123,6 @@ resolver = "2" members = [ "compiler", "compiler/codegen", - "compiler/literal", ".", "derive", "jit", @@ -151,7 +150,7 @@ rustpython-common = { path = "crates/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-literal = { path = "crates/literal", version = "0.4.0" } rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/pylib", version = "0.4.0" } rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.0" } diff --git a/compiler/literal/Cargo.toml b/crates/literal/Cargo.toml similarity index 100% rename from compiler/literal/Cargo.toml rename to crates/literal/Cargo.toml diff --git a/compiler/literal/src/char.rs b/crates/literal/src/char.rs similarity index 100% rename from compiler/literal/src/char.rs rename to crates/literal/src/char.rs diff --git a/compiler/literal/src/complex.rs b/crates/literal/src/complex.rs similarity index 100% rename from compiler/literal/src/complex.rs rename to crates/literal/src/complex.rs diff --git a/compiler/literal/src/escape.rs b/crates/literal/src/escape.rs similarity index 100% rename from compiler/literal/src/escape.rs rename to crates/literal/src/escape.rs diff --git a/compiler/literal/src/float.rs b/crates/literal/src/float.rs similarity index 100% rename from compiler/literal/src/float.rs rename to crates/literal/src/float.rs diff --git a/compiler/literal/src/format.rs b/crates/literal/src/format.rs similarity index 100% rename from compiler/literal/src/format.rs rename to crates/literal/src/format.rs diff --git a/compiler/literal/src/lib.rs b/crates/literal/src/lib.rs similarity index 100% rename from compiler/literal/src/lib.rs rename to crates/literal/src/lib.rs From 477c9b32f0218e9cea42c8c6a90d1d4720a87754 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:29:20 +0900 Subject: [PATCH 0020/1459] Fix OSError.__str__ to not display 'None' for filename (#6266) --- vm/src/exceptions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3cf26c1d5e9..a8b55ae8a4d 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1522,7 +1522,7 @@ pub(super) mod types { let msg = exc.get_arg(1).unwrap().str(vm)?; let s = match obj.get_attr("filename", vm) { - Ok(filename) => match obj.get_attr("filename2", vm) { + Ok(filename) if !vm.is_none(&filename) => match obj.get_attr("filename2", vm) { Ok(filename2) if !vm.is_none(&filename2) => format!( "[Errno {}] {}: '{}' -> '{}'", errno, @@ -1532,7 +1532,7 @@ pub(super) mod types { ), _ => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?), }, - Err(_) => { + _ => { format!("[Errno {errno}] {msg}") } }; From 7f45ba4c9c734fa9bcb291849d5908e8c7836a0c Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 11:25:46 +0200 Subject: [PATCH 0021/1459] Move `compiler/codegen` -> `crates/codegen` (#6260) --- Cargo.toml | 3 +-- {compiler => crates}/codegen/Cargo.toml | 0 {compiler => crates}/codegen/src/compile.rs | 0 {compiler => crates}/codegen/src/error.rs | 0 {compiler => crates}/codegen/src/ir.rs | 0 {compiler => crates}/codegen/src/lib.rs | 0 .../snapshots/rustpython_codegen__compile__tests__if_ands.snap | 0 .../rustpython_codegen__compile__tests__if_mixed.snap | 0 .../snapshots/rustpython_codegen__compile__tests__if_ors.snap | 0 ...thon_codegen__compile__tests__nested_double_async_with.snap | 0 .../rustpython_compiler_core__compile__tests__if_ands.snap | 0 .../rustpython_compiler_core__compile__tests__if_mixed.snap | 0 .../rustpython_compiler_core__compile__tests__if_ors.snap | 0 ...ompiler_core__compile__tests__nested_double_async_with.snap | 0 {compiler => crates}/codegen/src/string_parser.rs | 0 {compiler => crates}/codegen/src/symboltable.rs | 0 {compiler => crates}/codegen/src/unparse.rs | 0 17 files changed, 1 insertion(+), 2 deletions(-) rename {compiler => crates}/codegen/Cargo.toml (100%) rename {compiler => crates}/codegen/src/compile.rs (100%) rename {compiler => crates}/codegen/src/error.rs (100%) rename {compiler => crates}/codegen/src/ir.rs (100%) rename {compiler => crates}/codegen/src/lib.rs (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap (100%) rename {compiler => crates}/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap (100%) rename {compiler => crates}/codegen/src/string_parser.rs (100%) rename {compiler => crates}/codegen/src/symboltable.rs (100%) rename {compiler => crates}/codegen/src/unparse.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index d3bcfc88f19..c53acbb86a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,6 @@ template = "installer-config/installer.wxs" resolver = "2" members = [ "compiler", - "compiler/codegen", ".", "derive", "jit", @@ -145,7 +144,7 @@ license = "MIT" [workspace.dependencies] rustpython-compiler-core = { path = "crates/compiler-core", version = "0.4.0" } rustpython-compiler = { path = "compiler", version = "0.4.0" } -rustpython-codegen = { path = "compiler/codegen", version = "0.4.0" } +rustpython-codegen = { path = "crates/codegen", version = "0.4.0" } rustpython-common = { path = "crates/common", version = "0.4.0" } rustpython-derive = { path = "derive", version = "0.4.0" } rustpython-derive-impl = { path = "derive-impl", version = "0.4.0" } diff --git a/compiler/codegen/Cargo.toml b/crates/codegen/Cargo.toml similarity index 100% rename from compiler/codegen/Cargo.toml rename to crates/codegen/Cargo.toml diff --git a/compiler/codegen/src/compile.rs b/crates/codegen/src/compile.rs similarity index 100% rename from compiler/codegen/src/compile.rs rename to crates/codegen/src/compile.rs diff --git a/compiler/codegen/src/error.rs b/crates/codegen/src/error.rs similarity index 100% rename from compiler/codegen/src/error.rs rename to crates/codegen/src/error.rs diff --git a/compiler/codegen/src/ir.rs b/crates/codegen/src/ir.rs similarity index 100% rename from compiler/codegen/src/ir.rs rename to crates/codegen/src/ir.rs diff --git a/compiler/codegen/src/lib.rs b/crates/codegen/src/lib.rs similarity index 100% rename from compiler/codegen/src/lib.rs rename to crates/codegen/src/lib.rs diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap rename to crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ands.snap diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap rename to crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_mixed.snap diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap rename to crates/codegen/src/snapshots/rustpython_codegen__compile__tests__if_ors.snap diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap rename to crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap diff --git a/compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap rename to crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap diff --git a/compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap rename to crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap diff --git a/compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap rename to crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap diff --git a/compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap similarity index 100% rename from compiler/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap rename to crates/codegen/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap diff --git a/compiler/codegen/src/string_parser.rs b/crates/codegen/src/string_parser.rs similarity index 100% rename from compiler/codegen/src/string_parser.rs rename to crates/codegen/src/string_parser.rs diff --git a/compiler/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs similarity index 100% rename from compiler/codegen/src/symboltable.rs rename to crates/codegen/src/symboltable.rs diff --git a/compiler/codegen/src/unparse.rs b/crates/codegen/src/unparse.rs similarity index 100% rename from compiler/codegen/src/unparse.rs rename to crates/codegen/src/unparse.rs From cc2e84b9fc0af690f3281cd4b02e219b4752bed7 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 11:26:24 +0200 Subject: [PATCH 0022/1459] Move `jit` -> `crates/jit` (#6262) --- Cargo.toml | 3 +-- {jit => crates/jit}/Cargo.toml | 2 +- {jit => crates/jit}/src/instructions.rs | 0 {jit => crates/jit}/src/lib.rs | 0 {jit => crates/jit}/tests/bool_tests.rs | 0 {jit => crates/jit}/tests/common.rs | 0 {jit => crates/jit}/tests/float_tests.rs | 0 {jit => crates/jit}/tests/int_tests.rs | 0 {jit => crates/jit}/tests/lib.rs | 0 {jit => crates/jit}/tests/misc_tests.rs | 0 {jit => crates/jit}/tests/none_tests.rs | 0 11 files changed, 2 insertions(+), 3 deletions(-) rename {jit => crates/jit}/Cargo.toml (91%) rename {jit => crates/jit}/src/instructions.rs (100%) rename {jit => crates/jit}/src/lib.rs (100%) rename {jit => crates/jit}/tests/bool_tests.rs (100%) rename {jit => crates/jit}/tests/common.rs (100%) rename {jit => crates/jit}/tests/float_tests.rs (100%) rename {jit => crates/jit}/tests/int_tests.rs (100%) rename {jit => crates/jit}/tests/lib.rs (100%) rename {jit => crates/jit}/tests/misc_tests.rs (100%) rename {jit => crates/jit}/tests/none_tests.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index c53acbb86a9..c3e6102d649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,7 +124,6 @@ members = [ "compiler", ".", "derive", - "jit", "vm", "vm/sre_engine", "stdlib", @@ -148,7 +147,7 @@ rustpython-codegen = { path = "crates/codegen", version = "0.4.0" } rustpython-common = { path = "crates/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-jit = { path = "crates/jit", version = "0.4.0" } rustpython-literal = { path = "crates/literal", version = "0.4.0" } rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/pylib", version = "0.4.0" } diff --git a/jit/Cargo.toml b/crates/jit/Cargo.toml similarity index 91% rename from jit/Cargo.toml rename to crates/jit/Cargo.toml index 5708ae367b5..2ef8c344a9d 100644 --- a/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -22,7 +22,7 @@ cranelift-jit = "0.119" cranelift-module = "0.119" [dev-dependencies] -rustpython-derive = { path = "../derive", version = "0.4.0" } +rustpython-derive = { workspace = true } approx = "0.5.1" diff --git a/jit/src/instructions.rs b/crates/jit/src/instructions.rs similarity index 100% rename from jit/src/instructions.rs rename to crates/jit/src/instructions.rs diff --git a/jit/src/lib.rs b/crates/jit/src/lib.rs similarity index 100% rename from jit/src/lib.rs rename to crates/jit/src/lib.rs diff --git a/jit/tests/bool_tests.rs b/crates/jit/tests/bool_tests.rs similarity index 100% rename from jit/tests/bool_tests.rs rename to crates/jit/tests/bool_tests.rs diff --git a/jit/tests/common.rs b/crates/jit/tests/common.rs similarity index 100% rename from jit/tests/common.rs rename to crates/jit/tests/common.rs diff --git a/jit/tests/float_tests.rs b/crates/jit/tests/float_tests.rs similarity index 100% rename from jit/tests/float_tests.rs rename to crates/jit/tests/float_tests.rs diff --git a/jit/tests/int_tests.rs b/crates/jit/tests/int_tests.rs similarity index 100% rename from jit/tests/int_tests.rs rename to crates/jit/tests/int_tests.rs diff --git a/jit/tests/lib.rs b/crates/jit/tests/lib.rs similarity index 100% rename from jit/tests/lib.rs rename to crates/jit/tests/lib.rs diff --git a/jit/tests/misc_tests.rs b/crates/jit/tests/misc_tests.rs similarity index 100% rename from jit/tests/misc_tests.rs rename to crates/jit/tests/misc_tests.rs diff --git a/jit/tests/none_tests.rs b/crates/jit/tests/none_tests.rs similarity index 100% rename from jit/tests/none_tests.rs rename to crates/jit/tests/none_tests.rs From 6479a2063caabe22be58687f861b6057c6147fc2 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:49:00 +0200 Subject: [PATCH 0023/1459] move `derive-impl` -> `crates/derive-impl` (#6263) --- Cargo.toml | 3 +-- {derive-impl => crates/derive-impl}/Cargo.toml | 0 {derive-impl => crates/derive-impl}/src/compile_bytecode.rs | 0 {derive-impl => crates/derive-impl}/src/error.rs | 0 {derive-impl => crates/derive-impl}/src/from_args.rs | 0 {derive-impl => crates/derive-impl}/src/lib.rs | 0 {derive-impl => crates/derive-impl}/src/pyclass.rs | 0 {derive-impl => crates/derive-impl}/src/pymodule.rs | 0 {derive-impl => crates/derive-impl}/src/pypayload.rs | 0 {derive-impl => crates/derive-impl}/src/pystructseq.rs | 0 {derive-impl => crates/derive-impl}/src/pytraverse.rs | 0 {derive-impl => crates/derive-impl}/src/util.rs | 0 12 files changed, 1 insertion(+), 2 deletions(-) rename {derive-impl => crates/derive-impl}/Cargo.toml (100%) rename {derive-impl => crates/derive-impl}/src/compile_bytecode.rs (100%) rename {derive-impl => crates/derive-impl}/src/error.rs (100%) rename {derive-impl => crates/derive-impl}/src/from_args.rs (100%) rename {derive-impl => crates/derive-impl}/src/lib.rs (100%) rename {derive-impl => crates/derive-impl}/src/pyclass.rs (100%) rename {derive-impl => crates/derive-impl}/src/pymodule.rs (100%) rename {derive-impl => crates/derive-impl}/src/pypayload.rs (100%) rename {derive-impl => crates/derive-impl}/src/pystructseq.rs (100%) rename {derive-impl => crates/derive-impl}/src/pytraverse.rs (100%) rename {derive-impl => crates/derive-impl}/src/util.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index c3e6102d649..dc4c71f9929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,7 +127,6 @@ members = [ "vm", "vm/sre_engine", "stdlib", - "derive-impl", "wasm/lib", "crates/*", ] @@ -146,7 +145,7 @@ rustpython-compiler = { path = "compiler", version = "0.4.0" } rustpython-codegen = { path = "crates/codegen", version = "0.4.0" } rustpython-common = { path = "crates/common", version = "0.4.0" } rustpython-derive = { path = "derive", version = "0.4.0" } -rustpython-derive-impl = { path = "derive-impl", version = "0.4.0" } +rustpython-derive-impl = { path = "crates/derive-impl", version = "0.4.0" } rustpython-jit = { path = "crates/jit", version = "0.4.0" } rustpython-literal = { path = "crates/literal", version = "0.4.0" } rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } diff --git a/derive-impl/Cargo.toml b/crates/derive-impl/Cargo.toml similarity index 100% rename from derive-impl/Cargo.toml rename to crates/derive-impl/Cargo.toml diff --git a/derive-impl/src/compile_bytecode.rs b/crates/derive-impl/src/compile_bytecode.rs similarity index 100% rename from derive-impl/src/compile_bytecode.rs rename to crates/derive-impl/src/compile_bytecode.rs diff --git a/derive-impl/src/error.rs b/crates/derive-impl/src/error.rs similarity index 100% rename from derive-impl/src/error.rs rename to crates/derive-impl/src/error.rs diff --git a/derive-impl/src/from_args.rs b/crates/derive-impl/src/from_args.rs similarity index 100% rename from derive-impl/src/from_args.rs rename to crates/derive-impl/src/from_args.rs diff --git a/derive-impl/src/lib.rs b/crates/derive-impl/src/lib.rs similarity index 100% rename from derive-impl/src/lib.rs rename to crates/derive-impl/src/lib.rs diff --git a/derive-impl/src/pyclass.rs b/crates/derive-impl/src/pyclass.rs similarity index 100% rename from derive-impl/src/pyclass.rs rename to crates/derive-impl/src/pyclass.rs diff --git a/derive-impl/src/pymodule.rs b/crates/derive-impl/src/pymodule.rs similarity index 100% rename from derive-impl/src/pymodule.rs rename to crates/derive-impl/src/pymodule.rs diff --git a/derive-impl/src/pypayload.rs b/crates/derive-impl/src/pypayload.rs similarity index 100% rename from derive-impl/src/pypayload.rs rename to crates/derive-impl/src/pypayload.rs diff --git a/derive-impl/src/pystructseq.rs b/crates/derive-impl/src/pystructseq.rs similarity index 100% rename from derive-impl/src/pystructseq.rs rename to crates/derive-impl/src/pystructseq.rs diff --git a/derive-impl/src/pytraverse.rs b/crates/derive-impl/src/pytraverse.rs similarity index 100% rename from derive-impl/src/pytraverse.rs rename to crates/derive-impl/src/pytraverse.rs diff --git a/derive-impl/src/util.rs b/crates/derive-impl/src/util.rs similarity index 100% rename from derive-impl/src/util.rs rename to crates/derive-impl/src/util.rs From 9e60940f1be2af30884f8b78c4fe4234ceaf2b56 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:49:48 +0200 Subject: [PATCH 0024/1459] Move `vm/sre_engine` -> `crates/sre_engine` (#6265) --- Cargo.toml | 3 +-- {vm => crates}/sre_engine/.gitignore | 0 {vm => crates}/sre_engine/Cargo.toml | 0 {vm => crates}/sre_engine/LICENSE | 0 {vm => crates}/sre_engine/benches/benches.rs | 0 {vm => crates}/sre_engine/generate_tests.py | 0 {vm => crates}/sre_engine/src/constants.rs | 0 {vm => crates}/sre_engine/src/engine.rs | 0 {vm => crates}/sre_engine/src/lib.rs | 0 {vm => crates}/sre_engine/src/string.rs | 0 {vm => crates}/sre_engine/tests/tests.rs | 0 11 files changed, 1 insertion(+), 2 deletions(-) rename {vm => crates}/sre_engine/.gitignore (100%) rename {vm => crates}/sre_engine/Cargo.toml (100%) rename {vm => crates}/sre_engine/LICENSE (100%) rename {vm => crates}/sre_engine/benches/benches.rs (100%) rename {vm => crates}/sre_engine/generate_tests.py (100%) rename {vm => crates}/sre_engine/src/constants.rs (100%) rename {vm => crates}/sre_engine/src/engine.rs (100%) rename {vm => crates}/sre_engine/src/lib.rs (100%) rename {vm => crates}/sre_engine/src/string.rs (100%) rename {vm => crates}/sre_engine/tests/tests.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index dc4c71f9929..31b57794e72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,7 +125,6 @@ members = [ ".", "derive", "vm", - "vm/sre_engine", "stdlib", "wasm/lib", "crates/*", @@ -151,7 +150,7 @@ rustpython-literal = { path = "crates/literal", version = "0.4.0" } rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/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-sre_engine = { path = "crates/sre_engine", version = "0.4.0" } rustpython-wtf8 = { path = "crates/wtf8", version = "0.4.0" } rustpython-doc = { path = "crates/doc", version = "0.4.0" } diff --git a/vm/sre_engine/.gitignore b/crates/sre_engine/.gitignore similarity index 100% rename from vm/sre_engine/.gitignore rename to crates/sre_engine/.gitignore diff --git a/vm/sre_engine/Cargo.toml b/crates/sre_engine/Cargo.toml similarity index 100% rename from vm/sre_engine/Cargo.toml rename to crates/sre_engine/Cargo.toml diff --git a/vm/sre_engine/LICENSE b/crates/sre_engine/LICENSE similarity index 100% rename from vm/sre_engine/LICENSE rename to crates/sre_engine/LICENSE diff --git a/vm/sre_engine/benches/benches.rs b/crates/sre_engine/benches/benches.rs similarity index 100% rename from vm/sre_engine/benches/benches.rs rename to crates/sre_engine/benches/benches.rs diff --git a/vm/sre_engine/generate_tests.py b/crates/sre_engine/generate_tests.py similarity index 100% rename from vm/sre_engine/generate_tests.py rename to crates/sre_engine/generate_tests.py diff --git a/vm/sre_engine/src/constants.rs b/crates/sre_engine/src/constants.rs similarity index 100% rename from vm/sre_engine/src/constants.rs rename to crates/sre_engine/src/constants.rs diff --git a/vm/sre_engine/src/engine.rs b/crates/sre_engine/src/engine.rs similarity index 100% rename from vm/sre_engine/src/engine.rs rename to crates/sre_engine/src/engine.rs diff --git a/vm/sre_engine/src/lib.rs b/crates/sre_engine/src/lib.rs similarity index 100% rename from vm/sre_engine/src/lib.rs rename to crates/sre_engine/src/lib.rs diff --git a/vm/sre_engine/src/string.rs b/crates/sre_engine/src/string.rs similarity index 100% rename from vm/sre_engine/src/string.rs rename to crates/sre_engine/src/string.rs diff --git a/vm/sre_engine/tests/tests.rs b/crates/sre_engine/tests/tests.rs similarity index 100% rename from vm/sre_engine/tests/tests.rs rename to crates/sre_engine/tests/tests.rs From 041dd306020bf4340d668895cabc931f4bc3bb75 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:50:19 +0200 Subject: [PATCH 0025/1459] Move `compiler/src` -> `crates/compiler` (#6270) --- Cargo.toml | 3 +-- {compiler => crates/compiler}/Cargo.toml | 0 {compiler => crates/compiler}/src/lib.rs | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename {compiler => crates/compiler}/Cargo.toml (100%) rename {compiler => crates/compiler}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 31b57794e72..be2d7241af2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,6 @@ template = "installer-config/installer.wxs" [workspace] resolver = "2" members = [ - "compiler", ".", "derive", "vm", @@ -140,7 +139,7 @@ license = "MIT" [workspace.dependencies] rustpython-compiler-core = { path = "crates/compiler-core", version = "0.4.0" } -rustpython-compiler = { path = "compiler", version = "0.4.0" } +rustpython-compiler = { path = "crates/compiler", version = "0.4.0" } rustpython-codegen = { path = "crates/codegen", version = "0.4.0" } rustpython-common = { path = "crates/common", version = "0.4.0" } rustpython-derive = { path = "derive", version = "0.4.0" } diff --git a/compiler/Cargo.toml b/crates/compiler/Cargo.toml similarity index 100% rename from compiler/Cargo.toml rename to crates/compiler/Cargo.toml diff --git a/compiler/src/lib.rs b/crates/compiler/src/lib.rs similarity index 100% rename from compiler/src/lib.rs rename to crates/compiler/src/lib.rs From 8968aeafb93ebd36d71de99a45a853336b2b9503 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 14:51:33 +0200 Subject: [PATCH 0026/1459] Move `vm` -> `crates/vm` (#6269) --- Cargo.toml | 3 +-- {vm => crates/vm}/Cargo.toml | 0 {vm => crates/vm}/Lib/README.md | 0 crates/vm/Lib/core_modules/codecs.py | 1 + crates/vm/Lib/core_modules/copyreg.py | 1 + crates/vm/Lib/core_modules/encodings_utf_8.py | 1 + crates/vm/Lib/python_builtins/__hello__.py | 1 + crates/vm/Lib/python_builtins/__phello__ | 1 + {vm => crates/vm}/Lib/python_builtins/__reducelib.py | 0 crates/vm/Lib/python_builtins/_frozen_importlib.py | 1 + crates/vm/Lib/python_builtins/_frozen_importlib_external.py | 1 + {vm => crates/vm}/Lib/python_builtins/_py_exceptiongroup.py | 0 crates/vm/Lib/python_builtins/_thread.py | 1 + {vm => crates/vm}/build.rs | 2 +- {vm => crates/vm}/src/anystr.rs | 0 {vm => crates/vm}/src/buffer.rs | 0 {vm => crates/vm}/src/builtins/asyncgenerator.rs | 0 {vm => crates/vm}/src/builtins/bool.rs | 0 {vm => crates/vm}/src/builtins/builtin_func.rs | 0 {vm => crates/vm}/src/builtins/bytearray.rs | 0 {vm => crates/vm}/src/builtins/bytes.rs | 0 {vm => crates/vm}/src/builtins/classmethod.rs | 0 {vm => crates/vm}/src/builtins/code.rs | 0 {vm => crates/vm}/src/builtins/complex.rs | 0 {vm => crates/vm}/src/builtins/coroutine.rs | 0 {vm => crates/vm}/src/builtins/descriptor.rs | 0 {vm => crates/vm}/src/builtins/dict.rs | 0 {vm => crates/vm}/src/builtins/enumerate.rs | 0 {vm => crates/vm}/src/builtins/filter.rs | 0 {vm => crates/vm}/src/builtins/float.rs | 0 {vm => crates/vm}/src/builtins/frame.rs | 0 {vm => crates/vm}/src/builtins/function.rs | 0 {vm => crates/vm}/src/builtins/function/jit.rs | 0 {vm => crates/vm}/src/builtins/generator.rs | 0 {vm => crates/vm}/src/builtins/genericalias.rs | 0 {vm => crates/vm}/src/builtins/getset.rs | 0 {vm => crates/vm}/src/builtins/int.rs | 0 {vm => crates/vm}/src/builtins/iter.rs | 0 {vm => crates/vm}/src/builtins/list.rs | 0 {vm => crates/vm}/src/builtins/map.rs | 0 {vm => crates/vm}/src/builtins/mappingproxy.rs | 0 {vm => crates/vm}/src/builtins/memory.rs | 0 {vm => crates/vm}/src/builtins/mod.rs | 0 {vm => crates/vm}/src/builtins/module.rs | 0 {vm => crates/vm}/src/builtins/namespace.rs | 0 {vm => crates/vm}/src/builtins/object.rs | 0 {vm => crates/vm}/src/builtins/property.rs | 0 {vm => crates/vm}/src/builtins/range.rs | 0 {vm => crates/vm}/src/builtins/set.rs | 0 {vm => crates/vm}/src/builtins/singletons.rs | 0 {vm => crates/vm}/src/builtins/slice.rs | 0 {vm => crates/vm}/src/builtins/staticmethod.rs | 0 {vm => crates/vm}/src/builtins/str.rs | 0 {vm => crates/vm}/src/builtins/super.rs | 0 {vm => crates/vm}/src/builtins/traceback.rs | 0 {vm => crates/vm}/src/builtins/tuple.rs | 0 {vm => crates/vm}/src/builtins/type.rs | 0 {vm => crates/vm}/src/builtins/union.rs | 0 {vm => crates/vm}/src/builtins/weakproxy.rs | 0 {vm => crates/vm}/src/builtins/weakref.rs | 0 {vm => crates/vm}/src/builtins/zip.rs | 0 {vm => crates/vm}/src/byte.rs | 0 {vm => crates/vm}/src/bytes_inner.rs | 0 {vm => crates/vm}/src/cformat.rs | 0 {vm => crates/vm}/src/class.rs | 0 {vm => crates/vm}/src/codecs.rs | 0 {vm => crates/vm}/src/compiler.rs | 0 {vm => crates/vm}/src/convert/into_object.rs | 0 {vm => crates/vm}/src/convert/mod.rs | 0 {vm => crates/vm}/src/convert/to_pyobject.rs | 0 {vm => crates/vm}/src/convert/transmute_from.rs | 0 {vm => crates/vm}/src/convert/try_from.rs | 0 {vm => crates/vm}/src/coroutine.rs | 0 {vm => crates/vm}/src/dict_inner.rs | 0 {vm => crates/vm}/src/eval.rs | 0 {vm => crates/vm}/src/exceptions.rs | 0 {vm => crates/vm}/src/format.rs | 0 {vm => crates/vm}/src/frame.rs | 0 {vm => crates/vm}/src/function/argument.rs | 0 {vm => crates/vm}/src/function/arithmetic.rs | 0 {vm => crates/vm}/src/function/buffer.rs | 0 {vm => crates/vm}/src/function/builtin.rs | 0 {vm => crates/vm}/src/function/either.rs | 0 {vm => crates/vm}/src/function/fspath.rs | 0 {vm => crates/vm}/src/function/getset.rs | 0 {vm => crates/vm}/src/function/method.rs | 0 {vm => crates/vm}/src/function/mod.rs | 0 {vm => crates/vm}/src/function/number.rs | 0 {vm => crates/vm}/src/function/protocol.rs | 0 {vm => crates/vm}/src/import.rs | 0 {vm => crates/vm}/src/intern.rs | 0 {vm => crates/vm}/src/iter.rs | 0 {vm => crates/vm}/src/lib.rs | 0 {vm => crates/vm}/src/macros.rs | 0 {vm => crates/vm}/src/object/core.rs | 0 {vm => crates/vm}/src/object/ext.rs | 0 {vm => crates/vm}/src/object/mod.rs | 0 {vm => crates/vm}/src/object/payload.rs | 0 {vm => crates/vm}/src/object/traverse.rs | 0 {vm => crates/vm}/src/object/traverse_object.rs | 0 {vm => crates/vm}/src/ospath.rs | 0 {vm => crates/vm}/src/prelude.rs | 0 {vm => crates/vm}/src/protocol/buffer.rs | 0 {vm => crates/vm}/src/protocol/callable.rs | 0 {vm => crates/vm}/src/protocol/iter.rs | 0 {vm => crates/vm}/src/protocol/mapping.rs | 0 {vm => crates/vm}/src/protocol/mod.rs | 0 {vm => crates/vm}/src/protocol/number.rs | 0 {vm => crates/vm}/src/protocol/object.rs | 0 {vm => crates/vm}/src/protocol/sequence.rs | 0 {vm => crates/vm}/src/py_io.rs | 0 {vm => crates/vm}/src/py_serde.rs | 0 {vm => crates/vm}/src/readline.rs | 0 {vm => crates/vm}/src/recursion.rs | 0 {vm => crates/vm}/src/scope.rs | 0 {vm => crates/vm}/src/sequence.rs | 0 {vm => crates/vm}/src/signal.rs | 0 {vm => crates/vm}/src/sliceable.rs | 0 {vm => crates/vm}/src/stdlib/ast.rs | 0 {vm => crates/vm}/src/stdlib/ast/argument.rs | 0 {vm => crates/vm}/src/stdlib/ast/basic.rs | 0 {vm => crates/vm}/src/stdlib/ast/constant.rs | 0 {vm => crates/vm}/src/stdlib/ast/elif_else_clause.rs | 0 {vm => crates/vm}/src/stdlib/ast/exception.rs | 0 {vm => crates/vm}/src/stdlib/ast/expression.rs | 0 {vm => crates/vm}/src/stdlib/ast/module.rs | 0 {vm => crates/vm}/src/stdlib/ast/node.rs | 0 {vm => crates/vm}/src/stdlib/ast/operator.rs | 0 {vm => crates/vm}/src/stdlib/ast/other.rs | 0 {vm => crates/vm}/src/stdlib/ast/parameter.rs | 0 {vm => crates/vm}/src/stdlib/ast/pattern.rs | 0 {vm => crates/vm}/src/stdlib/ast/pyast.rs | 0 {vm => crates/vm}/src/stdlib/ast/python.rs | 0 {vm => crates/vm}/src/stdlib/ast/statement.rs | 0 {vm => crates/vm}/src/stdlib/ast/string.rs | 0 {vm => crates/vm}/src/stdlib/ast/type_ignore.rs | 0 {vm => crates/vm}/src/stdlib/ast/type_parameters.rs | 0 {vm => crates/vm}/src/stdlib/atexit.rs | 0 {vm => crates/vm}/src/stdlib/builtins.rs | 0 {vm => crates/vm}/src/stdlib/codecs.rs | 0 {vm => crates/vm}/src/stdlib/collections.rs | 0 {vm => crates/vm}/src/stdlib/ctypes.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/array.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/base.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/function.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/library.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/pointer.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/structure.rs | 0 {vm => crates/vm}/src/stdlib/ctypes/union.rs | 0 {vm => crates/vm}/src/stdlib/errno.rs | 0 {vm => crates/vm}/src/stdlib/functools.rs | 0 {vm => crates/vm}/src/stdlib/imp.rs | 0 {vm => crates/vm}/src/stdlib/io.rs | 0 {vm => crates/vm}/src/stdlib/itertools.rs | 0 {vm => crates/vm}/src/stdlib/marshal.rs | 0 {vm => crates/vm}/src/stdlib/mod.rs | 0 {vm => crates/vm}/src/stdlib/msvcrt.rs | 0 {vm => crates/vm}/src/stdlib/nt.rs | 0 {vm => crates/vm}/src/stdlib/operator.rs | 0 {vm => crates/vm}/src/stdlib/os.rs | 0 {vm => crates/vm}/src/stdlib/posix.rs | 0 {vm => crates/vm}/src/stdlib/posix_compat.rs | 0 {vm => crates/vm}/src/stdlib/pwd.rs | 0 {vm => crates/vm}/src/stdlib/signal.rs | 0 {vm => crates/vm}/src/stdlib/sre.rs | 0 {vm => crates/vm}/src/stdlib/stat.rs | 0 {vm => crates/vm}/src/stdlib/string.rs | 0 {vm => crates/vm}/src/stdlib/symtable.rs | 0 {vm => crates/vm}/src/stdlib/sys.rs | 0 {vm => crates/vm}/src/stdlib/sysconfig.rs | 0 {vm => crates/vm}/src/stdlib/sysconfigdata.rs | 0 {vm => crates/vm}/src/stdlib/thread.rs | 0 {vm => crates/vm}/src/stdlib/time.rs | 0 {vm => crates/vm}/src/stdlib/typevar.rs | 0 {vm => crates/vm}/src/stdlib/typing.rs | 0 {vm => crates/vm}/src/stdlib/warnings.rs | 0 {vm => crates/vm}/src/stdlib/weakref.rs | 0 {vm => crates/vm}/src/stdlib/winapi.rs | 0 {vm => crates/vm}/src/stdlib/winreg.rs | 0 {vm => crates/vm}/src/suggestion.rs | 0 {vm => crates/vm}/src/types/mod.rs | 0 {vm => crates/vm}/src/types/slot.rs | 0 {vm => crates/vm}/src/types/structseq.rs | 0 {vm => crates/vm}/src/types/zoo.rs | 0 {vm => crates/vm}/src/utils.rs | 0 {vm => crates/vm}/src/version.rs | 0 {vm => crates/vm}/src/vm/compile.rs | 0 {vm => crates/vm}/src/vm/context.rs | 0 {vm => crates/vm}/src/vm/interpreter.rs | 0 {vm => crates/vm}/src/vm/method.rs | 0 {vm => crates/vm}/src/vm/mod.rs | 4 +++- {vm => crates/vm}/src/vm/setting.rs | 0 {vm => crates/vm}/src/vm/thread.rs | 0 {vm => crates/vm}/src/vm/vm_new.rs | 0 {vm => crates/vm}/src/vm/vm_object.rs | 0 {vm => crates/vm}/src/vm/vm_ops.rs | 0 {vm => crates/vm}/src/warn.rs | 0 {vm => crates/vm}/src/windows.rs | 0 example_projects/barebone/Cargo.toml | 2 +- example_projects/frozen_stdlib/Cargo.toml | 2 +- vm/Lib/core_modules/codecs.py | 1 - vm/Lib/core_modules/copyreg.py | 1 - vm/Lib/core_modules/encodings_utf_8.py | 1 - vm/Lib/python_builtins/__hello__.py | 1 - vm/Lib/python_builtins/__phello__ | 1 - vm/Lib/python_builtins/_frozen_importlib.py | 1 - vm/Lib/python_builtins/_frozen_importlib_external.py | 1 - vm/Lib/python_builtins/_thread.py | 1 - wasm/wasm-unknown-test/Cargo.toml | 2 +- 209 files changed, 16 insertions(+), 15 deletions(-) rename {vm => crates/vm}/Cargo.toml (100%) rename {vm => crates/vm}/Lib/README.md (100%) create mode 120000 crates/vm/Lib/core_modules/codecs.py create mode 120000 crates/vm/Lib/core_modules/copyreg.py create mode 120000 crates/vm/Lib/core_modules/encodings_utf_8.py create mode 120000 crates/vm/Lib/python_builtins/__hello__.py create mode 120000 crates/vm/Lib/python_builtins/__phello__ rename {vm => crates/vm}/Lib/python_builtins/__reducelib.py (100%) create mode 120000 crates/vm/Lib/python_builtins/_frozen_importlib.py create mode 120000 crates/vm/Lib/python_builtins/_frozen_importlib_external.py rename {vm => crates/vm}/Lib/python_builtins/_py_exceptiongroup.py (100%) create mode 120000 crates/vm/Lib/python_builtins/_thread.py rename {vm => crates/vm}/build.rs (96%) rename {vm => crates/vm}/src/anystr.rs (100%) rename {vm => crates/vm}/src/buffer.rs (100%) rename {vm => crates/vm}/src/builtins/asyncgenerator.rs (100%) rename {vm => crates/vm}/src/builtins/bool.rs (100%) rename {vm => crates/vm}/src/builtins/builtin_func.rs (100%) rename {vm => crates/vm}/src/builtins/bytearray.rs (100%) rename {vm => crates/vm}/src/builtins/bytes.rs (100%) rename {vm => crates/vm}/src/builtins/classmethod.rs (100%) rename {vm => crates/vm}/src/builtins/code.rs (100%) rename {vm => crates/vm}/src/builtins/complex.rs (100%) rename {vm => crates/vm}/src/builtins/coroutine.rs (100%) rename {vm => crates/vm}/src/builtins/descriptor.rs (100%) rename {vm => crates/vm}/src/builtins/dict.rs (100%) rename {vm => crates/vm}/src/builtins/enumerate.rs (100%) rename {vm => crates/vm}/src/builtins/filter.rs (100%) rename {vm => crates/vm}/src/builtins/float.rs (100%) rename {vm => crates/vm}/src/builtins/frame.rs (100%) rename {vm => crates/vm}/src/builtins/function.rs (100%) rename {vm => crates/vm}/src/builtins/function/jit.rs (100%) rename {vm => crates/vm}/src/builtins/generator.rs (100%) rename {vm => crates/vm}/src/builtins/genericalias.rs (100%) rename {vm => crates/vm}/src/builtins/getset.rs (100%) rename {vm => crates/vm}/src/builtins/int.rs (100%) rename {vm => crates/vm}/src/builtins/iter.rs (100%) rename {vm => crates/vm}/src/builtins/list.rs (100%) rename {vm => crates/vm}/src/builtins/map.rs (100%) rename {vm => crates/vm}/src/builtins/mappingproxy.rs (100%) rename {vm => crates/vm}/src/builtins/memory.rs (100%) rename {vm => crates/vm}/src/builtins/mod.rs (100%) rename {vm => crates/vm}/src/builtins/module.rs (100%) rename {vm => crates/vm}/src/builtins/namespace.rs (100%) rename {vm => crates/vm}/src/builtins/object.rs (100%) rename {vm => crates/vm}/src/builtins/property.rs (100%) rename {vm => crates/vm}/src/builtins/range.rs (100%) rename {vm => crates/vm}/src/builtins/set.rs (100%) rename {vm => crates/vm}/src/builtins/singletons.rs (100%) rename {vm => crates/vm}/src/builtins/slice.rs (100%) rename {vm => crates/vm}/src/builtins/staticmethod.rs (100%) rename {vm => crates/vm}/src/builtins/str.rs (100%) rename {vm => crates/vm}/src/builtins/super.rs (100%) rename {vm => crates/vm}/src/builtins/traceback.rs (100%) rename {vm => crates/vm}/src/builtins/tuple.rs (100%) rename {vm => crates/vm}/src/builtins/type.rs (100%) rename {vm => crates/vm}/src/builtins/union.rs (100%) rename {vm => crates/vm}/src/builtins/weakproxy.rs (100%) rename {vm => crates/vm}/src/builtins/weakref.rs (100%) rename {vm => crates/vm}/src/builtins/zip.rs (100%) rename {vm => crates/vm}/src/byte.rs (100%) rename {vm => crates/vm}/src/bytes_inner.rs (100%) rename {vm => crates/vm}/src/cformat.rs (100%) rename {vm => crates/vm}/src/class.rs (100%) rename {vm => crates/vm}/src/codecs.rs (100%) rename {vm => crates/vm}/src/compiler.rs (100%) rename {vm => crates/vm}/src/convert/into_object.rs (100%) rename {vm => crates/vm}/src/convert/mod.rs (100%) rename {vm => crates/vm}/src/convert/to_pyobject.rs (100%) rename {vm => crates/vm}/src/convert/transmute_from.rs (100%) rename {vm => crates/vm}/src/convert/try_from.rs (100%) rename {vm => crates/vm}/src/coroutine.rs (100%) rename {vm => crates/vm}/src/dict_inner.rs (100%) rename {vm => crates/vm}/src/eval.rs (100%) rename {vm => crates/vm}/src/exceptions.rs (100%) rename {vm => crates/vm}/src/format.rs (100%) rename {vm => crates/vm}/src/frame.rs (100%) rename {vm => crates/vm}/src/function/argument.rs (100%) rename {vm => crates/vm}/src/function/arithmetic.rs (100%) rename {vm => crates/vm}/src/function/buffer.rs (100%) rename {vm => crates/vm}/src/function/builtin.rs (100%) rename {vm => crates/vm}/src/function/either.rs (100%) rename {vm => crates/vm}/src/function/fspath.rs (100%) rename {vm => crates/vm}/src/function/getset.rs (100%) rename {vm => crates/vm}/src/function/method.rs (100%) rename {vm => crates/vm}/src/function/mod.rs (100%) rename {vm => crates/vm}/src/function/number.rs (100%) rename {vm => crates/vm}/src/function/protocol.rs (100%) rename {vm => crates/vm}/src/import.rs (100%) rename {vm => crates/vm}/src/intern.rs (100%) rename {vm => crates/vm}/src/iter.rs (100%) rename {vm => crates/vm}/src/lib.rs (100%) rename {vm => crates/vm}/src/macros.rs (100%) rename {vm => crates/vm}/src/object/core.rs (100%) rename {vm => crates/vm}/src/object/ext.rs (100%) rename {vm => crates/vm}/src/object/mod.rs (100%) rename {vm => crates/vm}/src/object/payload.rs (100%) rename {vm => crates/vm}/src/object/traverse.rs (100%) rename {vm => crates/vm}/src/object/traverse_object.rs (100%) rename {vm => crates/vm}/src/ospath.rs (100%) rename {vm => crates/vm}/src/prelude.rs (100%) rename {vm => crates/vm}/src/protocol/buffer.rs (100%) rename {vm => crates/vm}/src/protocol/callable.rs (100%) rename {vm => crates/vm}/src/protocol/iter.rs (100%) rename {vm => crates/vm}/src/protocol/mapping.rs (100%) rename {vm => crates/vm}/src/protocol/mod.rs (100%) rename {vm => crates/vm}/src/protocol/number.rs (100%) rename {vm => crates/vm}/src/protocol/object.rs (100%) rename {vm => crates/vm}/src/protocol/sequence.rs (100%) rename {vm => crates/vm}/src/py_io.rs (100%) rename {vm => crates/vm}/src/py_serde.rs (100%) rename {vm => crates/vm}/src/readline.rs (100%) rename {vm => crates/vm}/src/recursion.rs (100%) rename {vm => crates/vm}/src/scope.rs (100%) rename {vm => crates/vm}/src/sequence.rs (100%) rename {vm => crates/vm}/src/signal.rs (100%) rename {vm => crates/vm}/src/sliceable.rs (100%) rename {vm => crates/vm}/src/stdlib/ast.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/argument.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/basic.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/constant.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/elif_else_clause.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/exception.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/expression.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/module.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/node.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/operator.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/other.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/parameter.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/pattern.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/pyast.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/python.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/statement.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/string.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/type_ignore.rs (100%) rename {vm => crates/vm}/src/stdlib/ast/type_parameters.rs (100%) rename {vm => crates/vm}/src/stdlib/atexit.rs (100%) rename {vm => crates/vm}/src/stdlib/builtins.rs (100%) rename {vm => crates/vm}/src/stdlib/codecs.rs (100%) rename {vm => crates/vm}/src/stdlib/collections.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/array.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/base.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/function.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/library.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/pointer.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/structure.rs (100%) rename {vm => crates/vm}/src/stdlib/ctypes/union.rs (100%) rename {vm => crates/vm}/src/stdlib/errno.rs (100%) rename {vm => crates/vm}/src/stdlib/functools.rs (100%) rename {vm => crates/vm}/src/stdlib/imp.rs (100%) rename {vm => crates/vm}/src/stdlib/io.rs (100%) rename {vm => crates/vm}/src/stdlib/itertools.rs (100%) rename {vm => crates/vm}/src/stdlib/marshal.rs (100%) rename {vm => crates/vm}/src/stdlib/mod.rs (100%) rename {vm => crates/vm}/src/stdlib/msvcrt.rs (100%) rename {vm => crates/vm}/src/stdlib/nt.rs (100%) rename {vm => crates/vm}/src/stdlib/operator.rs (100%) rename {vm => crates/vm}/src/stdlib/os.rs (100%) rename {vm => crates/vm}/src/stdlib/posix.rs (100%) rename {vm => crates/vm}/src/stdlib/posix_compat.rs (100%) rename {vm => crates/vm}/src/stdlib/pwd.rs (100%) rename {vm => crates/vm}/src/stdlib/signal.rs (100%) rename {vm => crates/vm}/src/stdlib/sre.rs (100%) rename {vm => crates/vm}/src/stdlib/stat.rs (100%) rename {vm => crates/vm}/src/stdlib/string.rs (100%) rename {vm => crates/vm}/src/stdlib/symtable.rs (100%) rename {vm => crates/vm}/src/stdlib/sys.rs (100%) rename {vm => crates/vm}/src/stdlib/sysconfig.rs (100%) rename {vm => crates/vm}/src/stdlib/sysconfigdata.rs (100%) rename {vm => crates/vm}/src/stdlib/thread.rs (100%) rename {vm => crates/vm}/src/stdlib/time.rs (100%) rename {vm => crates/vm}/src/stdlib/typevar.rs (100%) rename {vm => crates/vm}/src/stdlib/typing.rs (100%) rename {vm => crates/vm}/src/stdlib/warnings.rs (100%) rename {vm => crates/vm}/src/stdlib/weakref.rs (100%) rename {vm => crates/vm}/src/stdlib/winapi.rs (100%) rename {vm => crates/vm}/src/stdlib/winreg.rs (100%) rename {vm => crates/vm}/src/suggestion.rs (100%) rename {vm => crates/vm}/src/types/mod.rs (100%) rename {vm => crates/vm}/src/types/slot.rs (100%) rename {vm => crates/vm}/src/types/structseq.rs (100%) rename {vm => crates/vm}/src/types/zoo.rs (100%) rename {vm => crates/vm}/src/utils.rs (100%) rename {vm => crates/vm}/src/version.rs (100%) rename {vm => crates/vm}/src/vm/compile.rs (100%) rename {vm => crates/vm}/src/vm/context.rs (100%) rename {vm => crates/vm}/src/vm/interpreter.rs (100%) rename {vm => crates/vm}/src/vm/method.rs (100%) rename {vm => crates/vm}/src/vm/mod.rs (99%) rename {vm => crates/vm}/src/vm/setting.rs (100%) rename {vm => crates/vm}/src/vm/thread.rs (100%) rename {vm => crates/vm}/src/vm/vm_new.rs (100%) rename {vm => crates/vm}/src/vm/vm_object.rs (100%) rename {vm => crates/vm}/src/vm/vm_ops.rs (100%) rename {vm => crates/vm}/src/warn.rs (100%) rename {vm => crates/vm}/src/windows.rs (100%) delete mode 120000 vm/Lib/core_modules/codecs.py delete mode 120000 vm/Lib/core_modules/copyreg.py delete mode 120000 vm/Lib/core_modules/encodings_utf_8.py delete mode 120000 vm/Lib/python_builtins/__hello__.py delete mode 120000 vm/Lib/python_builtins/__phello__ delete mode 120000 vm/Lib/python_builtins/_frozen_importlib.py delete mode 120000 vm/Lib/python_builtins/_frozen_importlib_external.py delete mode 120000 vm/Lib/python_builtins/_thread.py diff --git a/Cargo.toml b/Cargo.toml index be2d7241af2..8d344c133c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,7 +123,6 @@ resolver = "2" members = [ ".", "derive", - "vm", "stdlib", "wasm/lib", "crates/*", @@ -146,7 +145,7 @@ rustpython-derive = { path = "derive", version = "0.4.0" } rustpython-derive-impl = { path = "crates/derive-impl", version = "0.4.0" } rustpython-jit = { path = "crates/jit", version = "0.4.0" } rustpython-literal = { path = "crates/literal", version = "0.4.0" } -rustpython-vm = { path = "vm", default-features = false, version = "0.4.0" } +rustpython-vm = { path = "crates/vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/pylib", version = "0.4.0" } rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.0" } rustpython-sre_engine = { path = "crates/sre_engine", version = "0.4.0" } diff --git a/vm/Cargo.toml b/crates/vm/Cargo.toml similarity index 100% rename from vm/Cargo.toml rename to crates/vm/Cargo.toml diff --git a/vm/Lib/README.md b/crates/vm/Lib/README.md similarity index 100% rename from vm/Lib/README.md rename to crates/vm/Lib/README.md diff --git a/crates/vm/Lib/core_modules/codecs.py b/crates/vm/Lib/core_modules/codecs.py new file mode 120000 index 00000000000..4a96fdd182d --- /dev/null +++ b/crates/vm/Lib/core_modules/codecs.py @@ -0,0 +1 @@ +../../../../Lib/codecs.py \ No newline at end of file diff --git a/crates/vm/Lib/core_modules/copyreg.py b/crates/vm/Lib/core_modules/copyreg.py new file mode 120000 index 00000000000..6f4f0d4d445 --- /dev/null +++ b/crates/vm/Lib/core_modules/copyreg.py @@ -0,0 +1 @@ +../../../../Lib/copyreg.py \ No newline at end of file diff --git a/crates/vm/Lib/core_modules/encodings_utf_8.py b/crates/vm/Lib/core_modules/encodings_utf_8.py new file mode 120000 index 00000000000..0a82f3b4a41 --- /dev/null +++ b/crates/vm/Lib/core_modules/encodings_utf_8.py @@ -0,0 +1 @@ +../../../../Lib/encodings/utf_8.py \ No newline at end of file diff --git a/crates/vm/Lib/python_builtins/__hello__.py b/crates/vm/Lib/python_builtins/__hello__.py new file mode 120000 index 00000000000..e7dedd3d0aa --- /dev/null +++ b/crates/vm/Lib/python_builtins/__hello__.py @@ -0,0 +1 @@ +../../../../Lib/__hello__.py \ No newline at end of file diff --git a/crates/vm/Lib/python_builtins/__phello__ b/crates/vm/Lib/python_builtins/__phello__ new file mode 120000 index 00000000000..21833dd18d8 --- /dev/null +++ b/crates/vm/Lib/python_builtins/__phello__ @@ -0,0 +1 @@ +../../../../Lib/__phello__/ \ No newline at end of file diff --git a/vm/Lib/python_builtins/__reducelib.py b/crates/vm/Lib/python_builtins/__reducelib.py similarity index 100% rename from vm/Lib/python_builtins/__reducelib.py rename to crates/vm/Lib/python_builtins/__reducelib.py diff --git a/crates/vm/Lib/python_builtins/_frozen_importlib.py b/crates/vm/Lib/python_builtins/_frozen_importlib.py new file mode 120000 index 00000000000..9d752e80dec --- /dev/null +++ b/crates/vm/Lib/python_builtins/_frozen_importlib.py @@ -0,0 +1 @@ +../../../../Lib/importlib/_bootstrap.py \ No newline at end of file diff --git a/crates/vm/Lib/python_builtins/_frozen_importlib_external.py b/crates/vm/Lib/python_builtins/_frozen_importlib_external.py new file mode 120000 index 00000000000..a6b01510674 --- /dev/null +++ b/crates/vm/Lib/python_builtins/_frozen_importlib_external.py @@ -0,0 +1 @@ +../../../../Lib/importlib/_bootstrap_external.py \ No newline at end of file diff --git a/vm/Lib/python_builtins/_py_exceptiongroup.py b/crates/vm/Lib/python_builtins/_py_exceptiongroup.py similarity index 100% rename from vm/Lib/python_builtins/_py_exceptiongroup.py rename to crates/vm/Lib/python_builtins/_py_exceptiongroup.py diff --git a/crates/vm/Lib/python_builtins/_thread.py b/crates/vm/Lib/python_builtins/_thread.py new file mode 120000 index 00000000000..9079ca9fda3 --- /dev/null +++ b/crates/vm/Lib/python_builtins/_thread.py @@ -0,0 +1 @@ +../../../../Lib/_dummy_thread.py \ No newline at end of file diff --git a/vm/build.rs b/crates/vm/build.rs similarity index 96% rename from vm/build.rs rename to crates/vm/build.rs index 93d29c3a578..f76bf3f5cbd 100644 --- a/vm/build.rs +++ b/crates/vm/build.rs @@ -11,7 +11,7 @@ fn main() { let display = entry.display(); println!("cargo:rerun-if-changed={display}"); } - println!("cargo:rerun-if-changed=../Lib/importlib/_bootstrap.py"); + println!("cargo:rerun-if-changed=../../Lib/importlib/_bootstrap.py"); println!("cargo:rustc-env=RUSTPYTHON_GIT_HASH={}", git_hash()); println!( diff --git a/vm/src/anystr.rs b/crates/vm/src/anystr.rs similarity index 100% rename from vm/src/anystr.rs rename to crates/vm/src/anystr.rs diff --git a/vm/src/buffer.rs b/crates/vm/src/buffer.rs similarity index 100% rename from vm/src/buffer.rs rename to crates/vm/src/buffer.rs diff --git a/vm/src/builtins/asyncgenerator.rs b/crates/vm/src/builtins/asyncgenerator.rs similarity index 100% rename from vm/src/builtins/asyncgenerator.rs rename to crates/vm/src/builtins/asyncgenerator.rs diff --git a/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs similarity index 100% rename from vm/src/builtins/bool.rs rename to crates/vm/src/builtins/bool.rs diff --git a/vm/src/builtins/builtin_func.rs b/crates/vm/src/builtins/builtin_func.rs similarity index 100% rename from vm/src/builtins/builtin_func.rs rename to crates/vm/src/builtins/builtin_func.rs diff --git a/vm/src/builtins/bytearray.rs b/crates/vm/src/builtins/bytearray.rs similarity index 100% rename from vm/src/builtins/bytearray.rs rename to crates/vm/src/builtins/bytearray.rs diff --git a/vm/src/builtins/bytes.rs b/crates/vm/src/builtins/bytes.rs similarity index 100% rename from vm/src/builtins/bytes.rs rename to crates/vm/src/builtins/bytes.rs diff --git a/vm/src/builtins/classmethod.rs b/crates/vm/src/builtins/classmethod.rs similarity index 100% rename from vm/src/builtins/classmethod.rs rename to crates/vm/src/builtins/classmethod.rs diff --git a/vm/src/builtins/code.rs b/crates/vm/src/builtins/code.rs similarity index 100% rename from vm/src/builtins/code.rs rename to crates/vm/src/builtins/code.rs diff --git a/vm/src/builtins/complex.rs b/crates/vm/src/builtins/complex.rs similarity index 100% rename from vm/src/builtins/complex.rs rename to crates/vm/src/builtins/complex.rs diff --git a/vm/src/builtins/coroutine.rs b/crates/vm/src/builtins/coroutine.rs similarity index 100% rename from vm/src/builtins/coroutine.rs rename to crates/vm/src/builtins/coroutine.rs diff --git a/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs similarity index 100% rename from vm/src/builtins/descriptor.rs rename to crates/vm/src/builtins/descriptor.rs diff --git a/vm/src/builtins/dict.rs b/crates/vm/src/builtins/dict.rs similarity index 100% rename from vm/src/builtins/dict.rs rename to crates/vm/src/builtins/dict.rs diff --git a/vm/src/builtins/enumerate.rs b/crates/vm/src/builtins/enumerate.rs similarity index 100% rename from vm/src/builtins/enumerate.rs rename to crates/vm/src/builtins/enumerate.rs diff --git a/vm/src/builtins/filter.rs b/crates/vm/src/builtins/filter.rs similarity index 100% rename from vm/src/builtins/filter.rs rename to crates/vm/src/builtins/filter.rs diff --git a/vm/src/builtins/float.rs b/crates/vm/src/builtins/float.rs similarity index 100% rename from vm/src/builtins/float.rs rename to crates/vm/src/builtins/float.rs diff --git a/vm/src/builtins/frame.rs b/crates/vm/src/builtins/frame.rs similarity index 100% rename from vm/src/builtins/frame.rs rename to crates/vm/src/builtins/frame.rs diff --git a/vm/src/builtins/function.rs b/crates/vm/src/builtins/function.rs similarity index 100% rename from vm/src/builtins/function.rs rename to crates/vm/src/builtins/function.rs diff --git a/vm/src/builtins/function/jit.rs b/crates/vm/src/builtins/function/jit.rs similarity index 100% rename from vm/src/builtins/function/jit.rs rename to crates/vm/src/builtins/function/jit.rs diff --git a/vm/src/builtins/generator.rs b/crates/vm/src/builtins/generator.rs similarity index 100% rename from vm/src/builtins/generator.rs rename to crates/vm/src/builtins/generator.rs diff --git a/vm/src/builtins/genericalias.rs b/crates/vm/src/builtins/genericalias.rs similarity index 100% rename from vm/src/builtins/genericalias.rs rename to crates/vm/src/builtins/genericalias.rs diff --git a/vm/src/builtins/getset.rs b/crates/vm/src/builtins/getset.rs similarity index 100% rename from vm/src/builtins/getset.rs rename to crates/vm/src/builtins/getset.rs diff --git a/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs similarity index 100% rename from vm/src/builtins/int.rs rename to crates/vm/src/builtins/int.rs diff --git a/vm/src/builtins/iter.rs b/crates/vm/src/builtins/iter.rs similarity index 100% rename from vm/src/builtins/iter.rs rename to crates/vm/src/builtins/iter.rs diff --git a/vm/src/builtins/list.rs b/crates/vm/src/builtins/list.rs similarity index 100% rename from vm/src/builtins/list.rs rename to crates/vm/src/builtins/list.rs diff --git a/vm/src/builtins/map.rs b/crates/vm/src/builtins/map.rs similarity index 100% rename from vm/src/builtins/map.rs rename to crates/vm/src/builtins/map.rs diff --git a/vm/src/builtins/mappingproxy.rs b/crates/vm/src/builtins/mappingproxy.rs similarity index 100% rename from vm/src/builtins/mappingproxy.rs rename to crates/vm/src/builtins/mappingproxy.rs diff --git a/vm/src/builtins/memory.rs b/crates/vm/src/builtins/memory.rs similarity index 100% rename from vm/src/builtins/memory.rs rename to crates/vm/src/builtins/memory.rs diff --git a/vm/src/builtins/mod.rs b/crates/vm/src/builtins/mod.rs similarity index 100% rename from vm/src/builtins/mod.rs rename to crates/vm/src/builtins/mod.rs diff --git a/vm/src/builtins/module.rs b/crates/vm/src/builtins/module.rs similarity index 100% rename from vm/src/builtins/module.rs rename to crates/vm/src/builtins/module.rs diff --git a/vm/src/builtins/namespace.rs b/crates/vm/src/builtins/namespace.rs similarity index 100% rename from vm/src/builtins/namespace.rs rename to crates/vm/src/builtins/namespace.rs diff --git a/vm/src/builtins/object.rs b/crates/vm/src/builtins/object.rs similarity index 100% rename from vm/src/builtins/object.rs rename to crates/vm/src/builtins/object.rs diff --git a/vm/src/builtins/property.rs b/crates/vm/src/builtins/property.rs similarity index 100% rename from vm/src/builtins/property.rs rename to crates/vm/src/builtins/property.rs diff --git a/vm/src/builtins/range.rs b/crates/vm/src/builtins/range.rs similarity index 100% rename from vm/src/builtins/range.rs rename to crates/vm/src/builtins/range.rs diff --git a/vm/src/builtins/set.rs b/crates/vm/src/builtins/set.rs similarity index 100% rename from vm/src/builtins/set.rs rename to crates/vm/src/builtins/set.rs diff --git a/vm/src/builtins/singletons.rs b/crates/vm/src/builtins/singletons.rs similarity index 100% rename from vm/src/builtins/singletons.rs rename to crates/vm/src/builtins/singletons.rs diff --git a/vm/src/builtins/slice.rs b/crates/vm/src/builtins/slice.rs similarity index 100% rename from vm/src/builtins/slice.rs rename to crates/vm/src/builtins/slice.rs diff --git a/vm/src/builtins/staticmethod.rs b/crates/vm/src/builtins/staticmethod.rs similarity index 100% rename from vm/src/builtins/staticmethod.rs rename to crates/vm/src/builtins/staticmethod.rs diff --git a/vm/src/builtins/str.rs b/crates/vm/src/builtins/str.rs similarity index 100% rename from vm/src/builtins/str.rs rename to crates/vm/src/builtins/str.rs diff --git a/vm/src/builtins/super.rs b/crates/vm/src/builtins/super.rs similarity index 100% rename from vm/src/builtins/super.rs rename to crates/vm/src/builtins/super.rs diff --git a/vm/src/builtins/traceback.rs b/crates/vm/src/builtins/traceback.rs similarity index 100% rename from vm/src/builtins/traceback.rs rename to crates/vm/src/builtins/traceback.rs diff --git a/vm/src/builtins/tuple.rs b/crates/vm/src/builtins/tuple.rs similarity index 100% rename from vm/src/builtins/tuple.rs rename to crates/vm/src/builtins/tuple.rs diff --git a/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs similarity index 100% rename from vm/src/builtins/type.rs rename to crates/vm/src/builtins/type.rs diff --git a/vm/src/builtins/union.rs b/crates/vm/src/builtins/union.rs similarity index 100% rename from vm/src/builtins/union.rs rename to crates/vm/src/builtins/union.rs diff --git a/vm/src/builtins/weakproxy.rs b/crates/vm/src/builtins/weakproxy.rs similarity index 100% rename from vm/src/builtins/weakproxy.rs rename to crates/vm/src/builtins/weakproxy.rs diff --git a/vm/src/builtins/weakref.rs b/crates/vm/src/builtins/weakref.rs similarity index 100% rename from vm/src/builtins/weakref.rs rename to crates/vm/src/builtins/weakref.rs diff --git a/vm/src/builtins/zip.rs b/crates/vm/src/builtins/zip.rs similarity index 100% rename from vm/src/builtins/zip.rs rename to crates/vm/src/builtins/zip.rs diff --git a/vm/src/byte.rs b/crates/vm/src/byte.rs similarity index 100% rename from vm/src/byte.rs rename to crates/vm/src/byte.rs diff --git a/vm/src/bytes_inner.rs b/crates/vm/src/bytes_inner.rs similarity index 100% rename from vm/src/bytes_inner.rs rename to crates/vm/src/bytes_inner.rs diff --git a/vm/src/cformat.rs b/crates/vm/src/cformat.rs similarity index 100% rename from vm/src/cformat.rs rename to crates/vm/src/cformat.rs diff --git a/vm/src/class.rs b/crates/vm/src/class.rs similarity index 100% rename from vm/src/class.rs rename to crates/vm/src/class.rs diff --git a/vm/src/codecs.rs b/crates/vm/src/codecs.rs similarity index 100% rename from vm/src/codecs.rs rename to crates/vm/src/codecs.rs diff --git a/vm/src/compiler.rs b/crates/vm/src/compiler.rs similarity index 100% rename from vm/src/compiler.rs rename to crates/vm/src/compiler.rs diff --git a/vm/src/convert/into_object.rs b/crates/vm/src/convert/into_object.rs similarity index 100% rename from vm/src/convert/into_object.rs rename to crates/vm/src/convert/into_object.rs diff --git a/vm/src/convert/mod.rs b/crates/vm/src/convert/mod.rs similarity index 100% rename from vm/src/convert/mod.rs rename to crates/vm/src/convert/mod.rs diff --git a/vm/src/convert/to_pyobject.rs b/crates/vm/src/convert/to_pyobject.rs similarity index 100% rename from vm/src/convert/to_pyobject.rs rename to crates/vm/src/convert/to_pyobject.rs diff --git a/vm/src/convert/transmute_from.rs b/crates/vm/src/convert/transmute_from.rs similarity index 100% rename from vm/src/convert/transmute_from.rs rename to crates/vm/src/convert/transmute_from.rs diff --git a/vm/src/convert/try_from.rs b/crates/vm/src/convert/try_from.rs similarity index 100% rename from vm/src/convert/try_from.rs rename to crates/vm/src/convert/try_from.rs diff --git a/vm/src/coroutine.rs b/crates/vm/src/coroutine.rs similarity index 100% rename from vm/src/coroutine.rs rename to crates/vm/src/coroutine.rs diff --git a/vm/src/dict_inner.rs b/crates/vm/src/dict_inner.rs similarity index 100% rename from vm/src/dict_inner.rs rename to crates/vm/src/dict_inner.rs diff --git a/vm/src/eval.rs b/crates/vm/src/eval.rs similarity index 100% rename from vm/src/eval.rs rename to crates/vm/src/eval.rs diff --git a/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs similarity index 100% rename from vm/src/exceptions.rs rename to crates/vm/src/exceptions.rs diff --git a/vm/src/format.rs b/crates/vm/src/format.rs similarity index 100% rename from vm/src/format.rs rename to crates/vm/src/format.rs diff --git a/vm/src/frame.rs b/crates/vm/src/frame.rs similarity index 100% rename from vm/src/frame.rs rename to crates/vm/src/frame.rs diff --git a/vm/src/function/argument.rs b/crates/vm/src/function/argument.rs similarity index 100% rename from vm/src/function/argument.rs rename to crates/vm/src/function/argument.rs diff --git a/vm/src/function/arithmetic.rs b/crates/vm/src/function/arithmetic.rs similarity index 100% rename from vm/src/function/arithmetic.rs rename to crates/vm/src/function/arithmetic.rs diff --git a/vm/src/function/buffer.rs b/crates/vm/src/function/buffer.rs similarity index 100% rename from vm/src/function/buffer.rs rename to crates/vm/src/function/buffer.rs diff --git a/vm/src/function/builtin.rs b/crates/vm/src/function/builtin.rs similarity index 100% rename from vm/src/function/builtin.rs rename to crates/vm/src/function/builtin.rs diff --git a/vm/src/function/either.rs b/crates/vm/src/function/either.rs similarity index 100% rename from vm/src/function/either.rs rename to crates/vm/src/function/either.rs diff --git a/vm/src/function/fspath.rs b/crates/vm/src/function/fspath.rs similarity index 100% rename from vm/src/function/fspath.rs rename to crates/vm/src/function/fspath.rs diff --git a/vm/src/function/getset.rs b/crates/vm/src/function/getset.rs similarity index 100% rename from vm/src/function/getset.rs rename to crates/vm/src/function/getset.rs diff --git a/vm/src/function/method.rs b/crates/vm/src/function/method.rs similarity index 100% rename from vm/src/function/method.rs rename to crates/vm/src/function/method.rs diff --git a/vm/src/function/mod.rs b/crates/vm/src/function/mod.rs similarity index 100% rename from vm/src/function/mod.rs rename to crates/vm/src/function/mod.rs diff --git a/vm/src/function/number.rs b/crates/vm/src/function/number.rs similarity index 100% rename from vm/src/function/number.rs rename to crates/vm/src/function/number.rs diff --git a/vm/src/function/protocol.rs b/crates/vm/src/function/protocol.rs similarity index 100% rename from vm/src/function/protocol.rs rename to crates/vm/src/function/protocol.rs diff --git a/vm/src/import.rs b/crates/vm/src/import.rs similarity index 100% rename from vm/src/import.rs rename to crates/vm/src/import.rs diff --git a/vm/src/intern.rs b/crates/vm/src/intern.rs similarity index 100% rename from vm/src/intern.rs rename to crates/vm/src/intern.rs diff --git a/vm/src/iter.rs b/crates/vm/src/iter.rs similarity index 100% rename from vm/src/iter.rs rename to crates/vm/src/iter.rs diff --git a/vm/src/lib.rs b/crates/vm/src/lib.rs similarity index 100% rename from vm/src/lib.rs rename to crates/vm/src/lib.rs diff --git a/vm/src/macros.rs b/crates/vm/src/macros.rs similarity index 100% rename from vm/src/macros.rs rename to crates/vm/src/macros.rs diff --git a/vm/src/object/core.rs b/crates/vm/src/object/core.rs similarity index 100% rename from vm/src/object/core.rs rename to crates/vm/src/object/core.rs diff --git a/vm/src/object/ext.rs b/crates/vm/src/object/ext.rs similarity index 100% rename from vm/src/object/ext.rs rename to crates/vm/src/object/ext.rs diff --git a/vm/src/object/mod.rs b/crates/vm/src/object/mod.rs similarity index 100% rename from vm/src/object/mod.rs rename to crates/vm/src/object/mod.rs diff --git a/vm/src/object/payload.rs b/crates/vm/src/object/payload.rs similarity index 100% rename from vm/src/object/payload.rs rename to crates/vm/src/object/payload.rs diff --git a/vm/src/object/traverse.rs b/crates/vm/src/object/traverse.rs similarity index 100% rename from vm/src/object/traverse.rs rename to crates/vm/src/object/traverse.rs diff --git a/vm/src/object/traverse_object.rs b/crates/vm/src/object/traverse_object.rs similarity index 100% rename from vm/src/object/traverse_object.rs rename to crates/vm/src/object/traverse_object.rs diff --git a/vm/src/ospath.rs b/crates/vm/src/ospath.rs similarity index 100% rename from vm/src/ospath.rs rename to crates/vm/src/ospath.rs diff --git a/vm/src/prelude.rs b/crates/vm/src/prelude.rs similarity index 100% rename from vm/src/prelude.rs rename to crates/vm/src/prelude.rs diff --git a/vm/src/protocol/buffer.rs b/crates/vm/src/protocol/buffer.rs similarity index 100% rename from vm/src/protocol/buffer.rs rename to crates/vm/src/protocol/buffer.rs diff --git a/vm/src/protocol/callable.rs b/crates/vm/src/protocol/callable.rs similarity index 100% rename from vm/src/protocol/callable.rs rename to crates/vm/src/protocol/callable.rs diff --git a/vm/src/protocol/iter.rs b/crates/vm/src/protocol/iter.rs similarity index 100% rename from vm/src/protocol/iter.rs rename to crates/vm/src/protocol/iter.rs diff --git a/vm/src/protocol/mapping.rs b/crates/vm/src/protocol/mapping.rs similarity index 100% rename from vm/src/protocol/mapping.rs rename to crates/vm/src/protocol/mapping.rs diff --git a/vm/src/protocol/mod.rs b/crates/vm/src/protocol/mod.rs similarity index 100% rename from vm/src/protocol/mod.rs rename to crates/vm/src/protocol/mod.rs diff --git a/vm/src/protocol/number.rs b/crates/vm/src/protocol/number.rs similarity index 100% rename from vm/src/protocol/number.rs rename to crates/vm/src/protocol/number.rs diff --git a/vm/src/protocol/object.rs b/crates/vm/src/protocol/object.rs similarity index 100% rename from vm/src/protocol/object.rs rename to crates/vm/src/protocol/object.rs diff --git a/vm/src/protocol/sequence.rs b/crates/vm/src/protocol/sequence.rs similarity index 100% rename from vm/src/protocol/sequence.rs rename to crates/vm/src/protocol/sequence.rs diff --git a/vm/src/py_io.rs b/crates/vm/src/py_io.rs similarity index 100% rename from vm/src/py_io.rs rename to crates/vm/src/py_io.rs diff --git a/vm/src/py_serde.rs b/crates/vm/src/py_serde.rs similarity index 100% rename from vm/src/py_serde.rs rename to crates/vm/src/py_serde.rs diff --git a/vm/src/readline.rs b/crates/vm/src/readline.rs similarity index 100% rename from vm/src/readline.rs rename to crates/vm/src/readline.rs diff --git a/vm/src/recursion.rs b/crates/vm/src/recursion.rs similarity index 100% rename from vm/src/recursion.rs rename to crates/vm/src/recursion.rs diff --git a/vm/src/scope.rs b/crates/vm/src/scope.rs similarity index 100% rename from vm/src/scope.rs rename to crates/vm/src/scope.rs diff --git a/vm/src/sequence.rs b/crates/vm/src/sequence.rs similarity index 100% rename from vm/src/sequence.rs rename to crates/vm/src/sequence.rs diff --git a/vm/src/signal.rs b/crates/vm/src/signal.rs similarity index 100% rename from vm/src/signal.rs rename to crates/vm/src/signal.rs diff --git a/vm/src/sliceable.rs b/crates/vm/src/sliceable.rs similarity index 100% rename from vm/src/sliceable.rs rename to crates/vm/src/sliceable.rs diff --git a/vm/src/stdlib/ast.rs b/crates/vm/src/stdlib/ast.rs similarity index 100% rename from vm/src/stdlib/ast.rs rename to crates/vm/src/stdlib/ast.rs diff --git a/vm/src/stdlib/ast/argument.rs b/crates/vm/src/stdlib/ast/argument.rs similarity index 100% rename from vm/src/stdlib/ast/argument.rs rename to crates/vm/src/stdlib/ast/argument.rs diff --git a/vm/src/stdlib/ast/basic.rs b/crates/vm/src/stdlib/ast/basic.rs similarity index 100% rename from vm/src/stdlib/ast/basic.rs rename to crates/vm/src/stdlib/ast/basic.rs diff --git a/vm/src/stdlib/ast/constant.rs b/crates/vm/src/stdlib/ast/constant.rs similarity index 100% rename from vm/src/stdlib/ast/constant.rs rename to crates/vm/src/stdlib/ast/constant.rs diff --git a/vm/src/stdlib/ast/elif_else_clause.rs b/crates/vm/src/stdlib/ast/elif_else_clause.rs similarity index 100% rename from vm/src/stdlib/ast/elif_else_clause.rs rename to crates/vm/src/stdlib/ast/elif_else_clause.rs diff --git a/vm/src/stdlib/ast/exception.rs b/crates/vm/src/stdlib/ast/exception.rs similarity index 100% rename from vm/src/stdlib/ast/exception.rs rename to crates/vm/src/stdlib/ast/exception.rs diff --git a/vm/src/stdlib/ast/expression.rs b/crates/vm/src/stdlib/ast/expression.rs similarity index 100% rename from vm/src/stdlib/ast/expression.rs rename to crates/vm/src/stdlib/ast/expression.rs diff --git a/vm/src/stdlib/ast/module.rs b/crates/vm/src/stdlib/ast/module.rs similarity index 100% rename from vm/src/stdlib/ast/module.rs rename to crates/vm/src/stdlib/ast/module.rs diff --git a/vm/src/stdlib/ast/node.rs b/crates/vm/src/stdlib/ast/node.rs similarity index 100% rename from vm/src/stdlib/ast/node.rs rename to crates/vm/src/stdlib/ast/node.rs diff --git a/vm/src/stdlib/ast/operator.rs b/crates/vm/src/stdlib/ast/operator.rs similarity index 100% rename from vm/src/stdlib/ast/operator.rs rename to crates/vm/src/stdlib/ast/operator.rs diff --git a/vm/src/stdlib/ast/other.rs b/crates/vm/src/stdlib/ast/other.rs similarity index 100% rename from vm/src/stdlib/ast/other.rs rename to crates/vm/src/stdlib/ast/other.rs diff --git a/vm/src/stdlib/ast/parameter.rs b/crates/vm/src/stdlib/ast/parameter.rs similarity index 100% rename from vm/src/stdlib/ast/parameter.rs rename to crates/vm/src/stdlib/ast/parameter.rs diff --git a/vm/src/stdlib/ast/pattern.rs b/crates/vm/src/stdlib/ast/pattern.rs similarity index 100% rename from vm/src/stdlib/ast/pattern.rs rename to crates/vm/src/stdlib/ast/pattern.rs diff --git a/vm/src/stdlib/ast/pyast.rs b/crates/vm/src/stdlib/ast/pyast.rs similarity index 100% rename from vm/src/stdlib/ast/pyast.rs rename to crates/vm/src/stdlib/ast/pyast.rs diff --git a/vm/src/stdlib/ast/python.rs b/crates/vm/src/stdlib/ast/python.rs similarity index 100% rename from vm/src/stdlib/ast/python.rs rename to crates/vm/src/stdlib/ast/python.rs diff --git a/vm/src/stdlib/ast/statement.rs b/crates/vm/src/stdlib/ast/statement.rs similarity index 100% rename from vm/src/stdlib/ast/statement.rs rename to crates/vm/src/stdlib/ast/statement.rs diff --git a/vm/src/stdlib/ast/string.rs b/crates/vm/src/stdlib/ast/string.rs similarity index 100% rename from vm/src/stdlib/ast/string.rs rename to crates/vm/src/stdlib/ast/string.rs diff --git a/vm/src/stdlib/ast/type_ignore.rs b/crates/vm/src/stdlib/ast/type_ignore.rs similarity index 100% rename from vm/src/stdlib/ast/type_ignore.rs rename to crates/vm/src/stdlib/ast/type_ignore.rs diff --git a/vm/src/stdlib/ast/type_parameters.rs b/crates/vm/src/stdlib/ast/type_parameters.rs similarity index 100% rename from vm/src/stdlib/ast/type_parameters.rs rename to crates/vm/src/stdlib/ast/type_parameters.rs diff --git a/vm/src/stdlib/atexit.rs b/crates/vm/src/stdlib/atexit.rs similarity index 100% rename from vm/src/stdlib/atexit.rs rename to crates/vm/src/stdlib/atexit.rs diff --git a/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs similarity index 100% rename from vm/src/stdlib/builtins.rs rename to crates/vm/src/stdlib/builtins.rs diff --git a/vm/src/stdlib/codecs.rs b/crates/vm/src/stdlib/codecs.rs similarity index 100% rename from vm/src/stdlib/codecs.rs rename to crates/vm/src/stdlib/codecs.rs diff --git a/vm/src/stdlib/collections.rs b/crates/vm/src/stdlib/collections.rs similarity index 100% rename from vm/src/stdlib/collections.rs rename to crates/vm/src/stdlib/collections.rs diff --git a/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs similarity index 100% rename from vm/src/stdlib/ctypes.rs rename to crates/vm/src/stdlib/ctypes.rs diff --git a/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs similarity index 100% rename from vm/src/stdlib/ctypes/array.rs rename to crates/vm/src/stdlib/ctypes/array.rs diff --git a/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs similarity index 100% rename from vm/src/stdlib/ctypes/base.rs rename to crates/vm/src/stdlib/ctypes/base.rs diff --git a/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs similarity index 100% rename from vm/src/stdlib/ctypes/function.rs rename to crates/vm/src/stdlib/ctypes/function.rs diff --git a/vm/src/stdlib/ctypes/library.rs b/crates/vm/src/stdlib/ctypes/library.rs similarity index 100% rename from vm/src/stdlib/ctypes/library.rs rename to crates/vm/src/stdlib/ctypes/library.rs diff --git a/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs similarity index 100% rename from vm/src/stdlib/ctypes/pointer.rs rename to crates/vm/src/stdlib/ctypes/pointer.rs diff --git a/vm/src/stdlib/ctypes/structure.rs b/crates/vm/src/stdlib/ctypes/structure.rs similarity index 100% rename from vm/src/stdlib/ctypes/structure.rs rename to crates/vm/src/stdlib/ctypes/structure.rs diff --git a/vm/src/stdlib/ctypes/union.rs b/crates/vm/src/stdlib/ctypes/union.rs similarity index 100% rename from vm/src/stdlib/ctypes/union.rs rename to crates/vm/src/stdlib/ctypes/union.rs diff --git a/vm/src/stdlib/errno.rs b/crates/vm/src/stdlib/errno.rs similarity index 100% rename from vm/src/stdlib/errno.rs rename to crates/vm/src/stdlib/errno.rs diff --git a/vm/src/stdlib/functools.rs b/crates/vm/src/stdlib/functools.rs similarity index 100% rename from vm/src/stdlib/functools.rs rename to crates/vm/src/stdlib/functools.rs diff --git a/vm/src/stdlib/imp.rs b/crates/vm/src/stdlib/imp.rs similarity index 100% rename from vm/src/stdlib/imp.rs rename to crates/vm/src/stdlib/imp.rs diff --git a/vm/src/stdlib/io.rs b/crates/vm/src/stdlib/io.rs similarity index 100% rename from vm/src/stdlib/io.rs rename to crates/vm/src/stdlib/io.rs diff --git a/vm/src/stdlib/itertools.rs b/crates/vm/src/stdlib/itertools.rs similarity index 100% rename from vm/src/stdlib/itertools.rs rename to crates/vm/src/stdlib/itertools.rs diff --git a/vm/src/stdlib/marshal.rs b/crates/vm/src/stdlib/marshal.rs similarity index 100% rename from vm/src/stdlib/marshal.rs rename to crates/vm/src/stdlib/marshal.rs diff --git a/vm/src/stdlib/mod.rs b/crates/vm/src/stdlib/mod.rs similarity index 100% rename from vm/src/stdlib/mod.rs rename to crates/vm/src/stdlib/mod.rs diff --git a/vm/src/stdlib/msvcrt.rs b/crates/vm/src/stdlib/msvcrt.rs similarity index 100% rename from vm/src/stdlib/msvcrt.rs rename to crates/vm/src/stdlib/msvcrt.rs diff --git a/vm/src/stdlib/nt.rs b/crates/vm/src/stdlib/nt.rs similarity index 100% rename from vm/src/stdlib/nt.rs rename to crates/vm/src/stdlib/nt.rs diff --git a/vm/src/stdlib/operator.rs b/crates/vm/src/stdlib/operator.rs similarity index 100% rename from vm/src/stdlib/operator.rs rename to crates/vm/src/stdlib/operator.rs diff --git a/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs similarity index 100% rename from vm/src/stdlib/os.rs rename to crates/vm/src/stdlib/os.rs diff --git a/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs similarity index 100% rename from vm/src/stdlib/posix.rs rename to crates/vm/src/stdlib/posix.rs diff --git a/vm/src/stdlib/posix_compat.rs b/crates/vm/src/stdlib/posix_compat.rs similarity index 100% rename from vm/src/stdlib/posix_compat.rs rename to crates/vm/src/stdlib/posix_compat.rs diff --git a/vm/src/stdlib/pwd.rs b/crates/vm/src/stdlib/pwd.rs similarity index 100% rename from vm/src/stdlib/pwd.rs rename to crates/vm/src/stdlib/pwd.rs diff --git a/vm/src/stdlib/signal.rs b/crates/vm/src/stdlib/signal.rs similarity index 100% rename from vm/src/stdlib/signal.rs rename to crates/vm/src/stdlib/signal.rs diff --git a/vm/src/stdlib/sre.rs b/crates/vm/src/stdlib/sre.rs similarity index 100% rename from vm/src/stdlib/sre.rs rename to crates/vm/src/stdlib/sre.rs diff --git a/vm/src/stdlib/stat.rs b/crates/vm/src/stdlib/stat.rs similarity index 100% rename from vm/src/stdlib/stat.rs rename to crates/vm/src/stdlib/stat.rs diff --git a/vm/src/stdlib/string.rs b/crates/vm/src/stdlib/string.rs similarity index 100% rename from vm/src/stdlib/string.rs rename to crates/vm/src/stdlib/string.rs diff --git a/vm/src/stdlib/symtable.rs b/crates/vm/src/stdlib/symtable.rs similarity index 100% rename from vm/src/stdlib/symtable.rs rename to crates/vm/src/stdlib/symtable.rs diff --git a/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs similarity index 100% rename from vm/src/stdlib/sys.rs rename to crates/vm/src/stdlib/sys.rs diff --git a/vm/src/stdlib/sysconfig.rs b/crates/vm/src/stdlib/sysconfig.rs similarity index 100% rename from vm/src/stdlib/sysconfig.rs rename to crates/vm/src/stdlib/sysconfig.rs diff --git a/vm/src/stdlib/sysconfigdata.rs b/crates/vm/src/stdlib/sysconfigdata.rs similarity index 100% rename from vm/src/stdlib/sysconfigdata.rs rename to crates/vm/src/stdlib/sysconfigdata.rs diff --git a/vm/src/stdlib/thread.rs b/crates/vm/src/stdlib/thread.rs similarity index 100% rename from vm/src/stdlib/thread.rs rename to crates/vm/src/stdlib/thread.rs diff --git a/vm/src/stdlib/time.rs b/crates/vm/src/stdlib/time.rs similarity index 100% rename from vm/src/stdlib/time.rs rename to crates/vm/src/stdlib/time.rs diff --git a/vm/src/stdlib/typevar.rs b/crates/vm/src/stdlib/typevar.rs similarity index 100% rename from vm/src/stdlib/typevar.rs rename to crates/vm/src/stdlib/typevar.rs diff --git a/vm/src/stdlib/typing.rs b/crates/vm/src/stdlib/typing.rs similarity index 100% rename from vm/src/stdlib/typing.rs rename to crates/vm/src/stdlib/typing.rs diff --git a/vm/src/stdlib/warnings.rs b/crates/vm/src/stdlib/warnings.rs similarity index 100% rename from vm/src/stdlib/warnings.rs rename to crates/vm/src/stdlib/warnings.rs diff --git a/vm/src/stdlib/weakref.rs b/crates/vm/src/stdlib/weakref.rs similarity index 100% rename from vm/src/stdlib/weakref.rs rename to crates/vm/src/stdlib/weakref.rs diff --git a/vm/src/stdlib/winapi.rs b/crates/vm/src/stdlib/winapi.rs similarity index 100% rename from vm/src/stdlib/winapi.rs rename to crates/vm/src/stdlib/winapi.rs diff --git a/vm/src/stdlib/winreg.rs b/crates/vm/src/stdlib/winreg.rs similarity index 100% rename from vm/src/stdlib/winreg.rs rename to crates/vm/src/stdlib/winreg.rs diff --git a/vm/src/suggestion.rs b/crates/vm/src/suggestion.rs similarity index 100% rename from vm/src/suggestion.rs rename to crates/vm/src/suggestion.rs diff --git a/vm/src/types/mod.rs b/crates/vm/src/types/mod.rs similarity index 100% rename from vm/src/types/mod.rs rename to crates/vm/src/types/mod.rs diff --git a/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs similarity index 100% rename from vm/src/types/slot.rs rename to crates/vm/src/types/slot.rs diff --git a/vm/src/types/structseq.rs b/crates/vm/src/types/structseq.rs similarity index 100% rename from vm/src/types/structseq.rs rename to crates/vm/src/types/structseq.rs diff --git a/vm/src/types/zoo.rs b/crates/vm/src/types/zoo.rs similarity index 100% rename from vm/src/types/zoo.rs rename to crates/vm/src/types/zoo.rs diff --git a/vm/src/utils.rs b/crates/vm/src/utils.rs similarity index 100% rename from vm/src/utils.rs rename to crates/vm/src/utils.rs diff --git a/vm/src/version.rs b/crates/vm/src/version.rs similarity index 100% rename from vm/src/version.rs rename to crates/vm/src/version.rs diff --git a/vm/src/vm/compile.rs b/crates/vm/src/vm/compile.rs similarity index 100% rename from vm/src/vm/compile.rs rename to crates/vm/src/vm/compile.rs diff --git a/vm/src/vm/context.rs b/crates/vm/src/vm/context.rs similarity index 100% rename from vm/src/vm/context.rs rename to crates/vm/src/vm/context.rs diff --git a/vm/src/vm/interpreter.rs b/crates/vm/src/vm/interpreter.rs similarity index 100% rename from vm/src/vm/interpreter.rs rename to crates/vm/src/vm/interpreter.rs diff --git a/vm/src/vm/method.rs b/crates/vm/src/vm/method.rs similarity index 100% rename from vm/src/vm/method.rs rename to crates/vm/src/vm/method.rs diff --git a/vm/src/vm/mod.rs b/crates/vm/src/vm/mod.rs similarity index 99% rename from vm/src/vm/mod.rs rename to crates/vm/src/vm/mod.rs index a973140a99a..b793a283525 100644 --- a/vm/src/vm/mod.rs +++ b/crates/vm/src/vm/mod.rs @@ -1036,7 +1036,9 @@ fn test_nested_frozen() { vm::Interpreter::with_init(Default::default(), |vm| { // vm.add_native_modules(rustpython_stdlib::get_module_inits()); - vm.add_frozen(rustpython_vm::py_freeze!(dir = "../extra_tests/snippets")); + vm.add_frozen(rustpython_vm::py_freeze!( + dir = "../../extra_tests/snippets" + )); }) .enter(|vm| { let scope = vm.new_scope_with_builtins(); diff --git a/vm/src/vm/setting.rs b/crates/vm/src/vm/setting.rs similarity index 100% rename from vm/src/vm/setting.rs rename to crates/vm/src/vm/setting.rs diff --git a/vm/src/vm/thread.rs b/crates/vm/src/vm/thread.rs similarity index 100% rename from vm/src/vm/thread.rs rename to crates/vm/src/vm/thread.rs diff --git a/vm/src/vm/vm_new.rs b/crates/vm/src/vm/vm_new.rs similarity index 100% rename from vm/src/vm/vm_new.rs rename to crates/vm/src/vm/vm_new.rs diff --git a/vm/src/vm/vm_object.rs b/crates/vm/src/vm/vm_object.rs similarity index 100% rename from vm/src/vm/vm_object.rs rename to crates/vm/src/vm/vm_object.rs diff --git a/vm/src/vm/vm_ops.rs b/crates/vm/src/vm/vm_ops.rs similarity index 100% rename from vm/src/vm/vm_ops.rs rename to crates/vm/src/vm/vm_ops.rs diff --git a/vm/src/warn.rs b/crates/vm/src/warn.rs similarity index 100% rename from vm/src/warn.rs rename to crates/vm/src/warn.rs diff --git a/vm/src/windows.rs b/crates/vm/src/windows.rs similarity index 100% rename from vm/src/windows.rs rename to crates/vm/src/windows.rs diff --git a/example_projects/barebone/Cargo.toml b/example_projects/barebone/Cargo.toml index 8bc49c237f3..b69617bdaf7 100644 --- a/example_projects/barebone/Cargo.toml +++ b/example_projects/barebone/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -rustpython-vm = { path = "../../vm", default-features = false } +rustpython-vm = { path = "../../crates/vm", default-features = false } [workspace] diff --git a/example_projects/frozen_stdlib/Cargo.toml b/example_projects/frozen_stdlib/Cargo.toml index 6824865cbf7..91bb14a083b 100644 --- a/example_projects/frozen_stdlib/Cargo.toml +++ b/example_projects/frozen_stdlib/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] rustpython = { path = "../../", default-features = false, features = ["freeze-stdlib"] } -rustpython-vm = { path = "../../vm", default-features = false, features = ["freeze-stdlib"] } +rustpython-vm = { path = "../../crates/vm", default-features = false, features = ["freeze-stdlib"] } rustpython-pylib = { path = "../../crates/pylib", default-features = false, features = ["freeze-stdlib"] } [workspace] diff --git a/vm/Lib/core_modules/codecs.py b/vm/Lib/core_modules/codecs.py deleted file mode 120000 index db3231198d9..00000000000 --- a/vm/Lib/core_modules/codecs.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/codecs.py \ No newline at end of file diff --git a/vm/Lib/core_modules/copyreg.py b/vm/Lib/core_modules/copyreg.py deleted file mode 120000 index 4ac7f40c43d..00000000000 --- a/vm/Lib/core_modules/copyreg.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/copyreg.py \ No newline at end of file diff --git a/vm/Lib/core_modules/encodings_utf_8.py b/vm/Lib/core_modules/encodings_utf_8.py deleted file mode 120000 index a8f34066264..00000000000 --- a/vm/Lib/core_modules/encodings_utf_8.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/encodings/utf_8.py \ No newline at end of file diff --git a/vm/Lib/python_builtins/__hello__.py b/vm/Lib/python_builtins/__hello__.py deleted file mode 120000 index f6cae8932f0..00000000000 --- a/vm/Lib/python_builtins/__hello__.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/__hello__.py \ No newline at end of file diff --git a/vm/Lib/python_builtins/__phello__ b/vm/Lib/python_builtins/__phello__ deleted file mode 120000 index 113aeb15040..00000000000 --- a/vm/Lib/python_builtins/__phello__ +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/__phello__ \ No newline at end of file diff --git a/vm/Lib/python_builtins/_frozen_importlib.py b/vm/Lib/python_builtins/_frozen_importlib.py deleted file mode 120000 index d1d4364abac..00000000000 --- a/vm/Lib/python_builtins/_frozen_importlib.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/importlib/_bootstrap.py \ No newline at end of file diff --git a/vm/Lib/python_builtins/_frozen_importlib_external.py b/vm/Lib/python_builtins/_frozen_importlib_external.py deleted file mode 120000 index ba8aff58051..00000000000 --- a/vm/Lib/python_builtins/_frozen_importlib_external.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/importlib/_bootstrap_external.py \ No newline at end of file diff --git a/vm/Lib/python_builtins/_thread.py b/vm/Lib/python_builtins/_thread.py deleted file mode 120000 index fa4a34c4fb9..00000000000 --- a/vm/Lib/python_builtins/_thread.py +++ /dev/null @@ -1 +0,0 @@ -../../../Lib/_dummy_thread.py \ No newline at end of file diff --git a/wasm/wasm-unknown-test/Cargo.toml b/wasm/wasm-unknown-test/Cargo.toml index ed8c9fcb02e..277a9810b99 100644 --- a/wasm/wasm-unknown-test/Cargo.toml +++ b/wasm/wasm-unknown-test/Cargo.toml @@ -8,7 +8,7 @@ crate-type = ["cdylib"] [dependencies] getrandom = "0.3" -rustpython-vm = { path = "../../vm", default-features = false, features = ["compiler"] } +rustpython-vm = { path = "../../crates/vm", default-features = false, features = ["compiler"] } [workspace] From 8715ae76f173ece3413f9be111c2fda2fbc579b2 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 15 Nov 2025 15:54:25 +0200 Subject: [PATCH 0027/1459] Move `derive` -> `crates/derive` (#6264) --- Cargo.toml | 3 +-- {derive => crates/derive}/Cargo.toml | 0 {derive => crates/derive}/src/lib.rs | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename {derive => crates/derive}/Cargo.toml (100%) rename {derive => crates/derive}/src/lib.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 8d344c133c8..54e4f1b6429 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,6 @@ template = "installer-config/installer.wxs" resolver = "2" members = [ ".", - "derive", "stdlib", "wasm/lib", "crates/*", @@ -141,7 +140,7 @@ rustpython-compiler-core = { path = "crates/compiler-core", version = "0.4.0" } rustpython-compiler = { path = "crates/compiler", version = "0.4.0" } rustpython-codegen = { path = "crates/codegen", version = "0.4.0" } rustpython-common = { path = "crates/common", version = "0.4.0" } -rustpython-derive = { path = "derive", version = "0.4.0" } +rustpython-derive = { path = "crates/derive", version = "0.4.0" } rustpython-derive-impl = { path = "crates/derive-impl", version = "0.4.0" } rustpython-jit = { path = "crates/jit", version = "0.4.0" } rustpython-literal = { path = "crates/literal", version = "0.4.0" } diff --git a/derive/Cargo.toml b/crates/derive/Cargo.toml similarity index 100% rename from derive/Cargo.toml rename to crates/derive/Cargo.toml diff --git a/derive/src/lib.rs b/crates/derive/src/lib.rs similarity index 100% rename from derive/src/lib.rs rename to crates/derive/src/lib.rs From 4f8ef16937a1311f3dd0c236cbcb4dcaa932d954 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 15 Nov 2025 20:57:33 +0900 Subject: [PATCH 0028/1459] Re-organize vm.run_script inner functions --- crates/vm/src/vm/compile.rs | 59 ++++++++++++++++++++++++++++++++----- crates/vm/src/vm/context.rs | 29 +++++++++--------- src/lib.rs | 8 +++-- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/crates/vm/src/vm/compile.rs b/crates/vm/src/vm/compile.rs index ee813bf7b46..26daf993d8d 100644 --- a/crates/vm/src/vm/compile.rs +++ b/crates/vm/src/vm/compile.rs @@ -26,7 +26,9 @@ impl VirtualMachine { compiler::compile(source, mode, &source_path, opts).map(|code| self.ctx.new_code(code)) } + // pymain_run_file_obj pub fn run_script(&self, scope: Scope, path: &str) -> PyResult<()> { + // when pymain_run_module? if get_importer(path, self)?.is_some() { self.insert_sys_path(self.new_pyobj(path))?; let runpy = self.import("runpy", 0)?; @@ -35,6 +37,7 @@ impl VirtualMachine { return Ok(()); } + // TODO: check if this is proper place if !self.state.settings.safe_path { let dir = std::path::Path::new(path) .parent() @@ -44,19 +47,61 @@ impl VirtualMachine { self.insert_sys_path(self.new_pyobj(dir))?; } - match std::fs::read_to_string(path) { - Ok(source) => { - self.run_code_string(scope, &source, path.to_owned())?; + self.run_any_file(scope, path) + } + + // = _PyRun_AnyFileObject + fn run_any_file(&self, scope: Scope, path: &str) -> PyResult<()> { + let path = if path.is_empty() { "???" } else { path }; + + self.run_simple_file(scope, path) + } + + // = _PyRun_SimpleFileObject + fn run_simple_file(&self, scope: Scope, path: &str) -> PyResult<()> { + // __main__ is given by scope + let sys_modules = self.sys_module.get_attr(identifier!(self, modules), self)?; + let main_module = sys_modules.get_item(identifier!(self, __main__), self)?; + let module_dict = main_module.dict().expect("main module must have __dict__"); + if !module_dict.contains_key(identifier!(self, __file__), self) { + module_dict.set_item( + identifier!(self, __file__), + self.ctx.new_str(path).into(), + self, + )?; + } + + // Consider to use enum to distinguish `path` + // https://github.com/RustPython/RustPython/pull/6276#discussion_r2529849479 + + // TODO: check .pyc here + let pyc = false; + if pyc { + todo!("running pyc is not implemented yet"); + } else { + if path != "" { + // TODO: set_main_loader(dict, filename, "SourceFileLoader"); } - Err(err) => { - error!("Failed reading file '{path}': {err}"); - // TODO: Need to change to ExitCode or Termination - std::process::exit(1); + // TODO: replace to something equivalent to py_run_file + match std::fs::read_to_string(path) { + Ok(source) => { + let code_obj = self + .compile(&source, compiler::Mode::Exec, path.to_owned()) + .map_err(|err| self.new_syntax_error(&err, Some(&source)))?; + // trace!("Code object: {:?}", code_obj.borrow()); + self.run_code_obj(code_obj, scope)?; + } + Err(err) => { + error!("Failed reading file '{path}': {err}"); + // TODO: Need to change to ExitCode or Termination + std::process::exit(1); + } } } Ok(()) } + // TODO: deprecate or reimplement using other primitive functions pub fn run_code_string(&self, scope: Scope, source: &str, source_path: String) -> PyResult { let code_obj = self .compile(source, compiler::Mode::Exec, source_path.clone()) diff --git a/crates/vm/src/vm/context.rs b/crates/vm/src/vm/context.rs index 4411aa0ff67..0e5b5498434 100644 --- a/crates/vm/src/vm/context.rs +++ b/crates/vm/src/vm/context.rs @@ -102,8 +102,8 @@ declare_const_name! { __ceil__, __cformat__, __class__, - __classcell__, __class_getitem__, + __classcell__, __complex__, __contains__, __copy__, @@ -234,26 +234,27 @@ declare_const_name! { _attributes, _fields, _showwarnmsg, + backslashreplace, + close, + copy, decode, encode, - keys, - items, - values, - version, - update, - copy, flush, - close, - WarningMessage, - strict, ignore, - replace, - xmlcharrefreplace, - backslashreplace, + items, + keys, + modules, namereplace, - surrogatepass, + replace, + strict, surrogateescape, + surrogatepass, + update, utf_8: "utf-8", + values, + version, + WarningMessage, + xmlcharrefreplace, } // Basic objects: diff --git a/src/lib.rs b/src/lib.rs index 362adfba490..dafc3fcd0d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -154,6 +154,7 @@ fn install_pip(installer: InstallPipMode, scope: Scope, vm: &VirtualMachine) -> } } +// pymain_run_python fn run_rustpython(vm: &VirtualMachine, run_mode: RunMode) -> PyResult<()> { #[cfg(feature = "flame-it")] let main_guard = flame::start_guard("RustPython main"); @@ -206,9 +207,10 @@ fn run_rustpython(vm: &VirtualMachine, run_mode: RunMode) -> PyResult<()> { vm.run_module(&module) } RunMode::InstallPip(installer) => install_pip(installer, scope.clone(), vm), - RunMode::Script(script) => { - debug!("Running script {}", &script); - vm.run_script(scope.clone(), &script) + RunMode::Script(script_path) => { + // pymain_run_file + debug!("Running script {}", &script_path); + vm.run_script(scope.clone(), &script_path) } RunMode::Repl => Ok(()), }; From 6991a80e13c06deaa7b4a0131fcfbf139621a465 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 15 Nov 2025 20:56:16 +0900 Subject: [PATCH 0029/1459] Add __main__.__cached__ --- crates/vm/src/vm/compile.rs | 1 + crates/vm/src/vm/context.rs | 1 + extra_tests/snippets/builtin___main__.py | 5 +++++ 3 files changed, 7 insertions(+) create mode 100644 extra_tests/snippets/builtin___main__.py diff --git a/crates/vm/src/vm/compile.rs b/crates/vm/src/vm/compile.rs index 26daf993d8d..6f1ea734926 100644 --- a/crates/vm/src/vm/compile.rs +++ b/crates/vm/src/vm/compile.rs @@ -69,6 +69,7 @@ impl VirtualMachine { self.ctx.new_str(path).into(), self, )?; + module_dict.set_item(identifier!(self, __cached__), self.ctx.none(), self)?; } // Consider to use enum to distinguish `path` diff --git a/crates/vm/src/vm/context.rs b/crates/vm/src/vm/context.rs index 0e5b5498434..3fc25f3b992 100644 --- a/crates/vm/src/vm/context.rs +++ b/crates/vm/src/vm/context.rs @@ -98,6 +98,7 @@ declare_const_name! { __build_class__, __builtins__, __bytes__, + __cached__, __call__, __ceil__, __cformat__, diff --git a/extra_tests/snippets/builtin___main__.py b/extra_tests/snippets/builtin___main__.py new file mode 100644 index 00000000000..97e76ce324a --- /dev/null +++ b/extra_tests/snippets/builtin___main__.py @@ -0,0 +1,5 @@ +import sys + +main_module = sys.modules["__main__"] +assert main_module.__file__.endswith("builtin___main__.py") +assert main_module.__cached__ is None From 93e4e42b53a48d316aa8cf0707b34ad6567728aa Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 16 Nov 2025 12:14:35 +0200 Subject: [PATCH 0030/1459] Move `compiler/source` -> `crates/compiler-source` (#6261) --- .github/workflows/ci.yaml | 6 +++--- {compiler/source => crates/compiler-source}/Cargo.toml | 0 {compiler/source => crates/compiler-source}/src/lib.rs | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename {compiler/source => crates/compiler-source}/Cargo.toml (100%) rename {compiler/source => crates/compiler-source}/src/lib.rs (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9ab4610ed11..dcae77693af 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -137,13 +137,13 @@ jobs: if: runner.os == 'macOS' - 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 --exclude rustpython_wasm --exclude rustpython-compiler-source -- -Dwarnings - name: run rust tests - run: cargo test --workspace --exclude rustpython_wasm --verbose --features threading ${{ env.CARGO_ARGS }} + run: cargo test --workspace --exclude rustpython_wasm --exclude rustpython-compiler-source --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 }} + run: cargo test --workspace --exclude rustpython_wasm --exclude rustpython-jit --exclude rustpython-compiler-source --verbose --features threading ${{ env.CARGO_ARGS }} if: runner.os == 'macOS' - name: check compilation without threading diff --git a/compiler/source/Cargo.toml b/crates/compiler-source/Cargo.toml similarity index 100% rename from compiler/source/Cargo.toml rename to crates/compiler-source/Cargo.toml diff --git a/compiler/source/src/lib.rs b/crates/compiler-source/src/lib.rs similarity index 100% rename from compiler/source/src/lib.rs rename to crates/compiler-source/src/lib.rs From 1568d2a7fbe7c8c70b85ac0239053c4ff6a6a7ec Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 16 Nov 2025 12:15:32 +0200 Subject: [PATCH 0031/1459] Remove leftovers after vm crate move (#6279) --- vm/.gitignore | 5 ----- vm/sre_engine/.vscode/launch.json | 21 --------------------- 2 files changed, 26 deletions(-) delete mode 100644 vm/.gitignore delete mode 100644 vm/sre_engine/.vscode/launch.json diff --git a/vm/.gitignore b/vm/.gitignore deleted file mode 100644 index 4931874f785..00000000000 --- a/vm/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -venv/ -tests/*.bytecode -Cargo.lock -python_compiler/target -target/ diff --git a/vm/sre_engine/.vscode/launch.json b/vm/sre_engine/.vscode/launch.json deleted file mode 100644 index 5ebfe34f05a..00000000000 --- a/vm/sre_engine/.vscode/launch.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug Unit Test", - "cargo": { - "args": [ - "test", - "--no-run" - ], - "filter": { - "kind": "test" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - } - ] -} \ No newline at end of file From 7c4c1eabf04fe3a26bb4dee4fa1ed8214b8bae7b Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Sun, 16 Nov 2025 22:16:34 +0900 Subject: [PATCH 0032/1459] Fix wasm32-unknown-unknown target build (#6278) --- Cargo.toml | 2 +- crates/vm/Cargo.toml | 3 +-- wasm/wasm-unknown-test/src/lib.rs | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 54e4f1b6429..5f8f7a5b555 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,7 +162,7 @@ ascii = "1.1" bitflags = "2.9.4" bstr = "1" cfg-if = "1.0" -chrono = "0.4.42" +chrono = { version = "0.4.42", default-features = false, features = ["clock", "oldtime", "std"] } constant_time_eq = "0.4" criterion = { version = "0.7", features = ["html_reports"] } crossbeam-utils = "0.8.21" diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index ffd58361789..1e12454a6ba 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -46,7 +46,7 @@ bitflags = { workspace = true } bstr = { workspace = true } cfg-if = { workspace = true } crossbeam-utils = { workspace = true } -chrono = { workspace = true, features = ["wasmbind"] } +chrono = { workspace = true } constant_time_eq = { workspace = true } flame = { workspace = true, optional = true } getrandom = { workspace = true } @@ -150,7 +150,6 @@ features = [ [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] wasm-bindgen = { workspace = true, optional = true } -getrandom = { workspace = true } [build-dependencies] glob = { workspace = true } diff --git a/wasm/wasm-unknown-test/src/lib.rs b/wasm/wasm-unknown-test/src/lib.rs index a3beead8900..aae922864dd 100644 --- a/wasm/wasm-unknown-test/src/lib.rs +++ b/wasm/wasm-unknown-test/src/lib.rs @@ -1,5 +1,6 @@ use rustpython_vm::{Interpreter, eval}; +#[unsafe(no_mangle)] pub unsafe extern "C" fn eval(s: *const u8, l: usize) -> u32 { let src = std::slice::from_raw_parts(s, l); let src = std::str::from_utf8(src).unwrap(); From 1a783fc9ec16bd824967449bc9899661a5ee5761 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Sun, 16 Nov 2025 22:17:35 +0900 Subject: [PATCH 0033/1459] Replace SSL backend to rustls (#6244) --- .cspell.dict/cpython.txt | 7 +- .cspell.dict/rust-more.txt | 2 + .github/workflows/ci.yaml | 17 +- Cargo.lock | 825 ++- Cargo.toml | 8 +- Lib/test/test_ssl.py | 68 +- README.md | 17 +- crates/vm/src/frame.rs | 2 +- src/lib.rs | 10 +- stdlib/Cargo.toml | 25 +- stdlib/build.rs | 37 +- stdlib/src/lib.rs | 14 +- stdlib/src/openssl.rs | 3705 +++++++++++ stdlib/src/openssl/cert.rs | 229 + stdlib/src/{ssl => openssl}/ssl_data_111.rs | 0 stdlib/src/{ssl => openssl}/ssl_data_300.rs | 0 stdlib/src/{ssl => openssl}/ssl_data_31.rs | 0 stdlib/src/ssl.rs | 6628 +++++++++++-------- stdlib/src/ssl/cert.rs | 1932 +++++- stdlib/src/ssl/compat.rs | 1786 +++++ stdlib/src/ssl/oid.rs | 464 ++ 21 files changed, 12700 insertions(+), 3076 deletions(-) create mode 100644 stdlib/src/openssl.rs create mode 100644 stdlib/src/openssl/cert.rs rename stdlib/src/{ssl => openssl}/ssl_data_111.rs (100%) rename stdlib/src/{ssl => openssl}/ssl_data_300.rs (100%) rename stdlib/src/{ssl => openssl}/ssl_data_31.rs (100%) create mode 100644 stdlib/src/ssl/compat.rs create mode 100644 stdlib/src/ssl/oid.rs diff --git a/.cspell.dict/cpython.txt b/.cspell.dict/cpython.txt index 8c733e343d1..5676549ec1a 100644 --- a/.cspell.dict/cpython.txt +++ b/.cspell.dict/cpython.txt @@ -2,11 +2,14 @@ argtypes asdl asname augassign +badcert badsyntax basetype boolop bxor cached_tsver +cadata +cafile cellarg cellvar cellvars @@ -23,8 +26,8 @@ freevars fromlist heaptype HIGHRES -Itertool IMMUTABLETYPE +Itertool kwonlyarg kwonlyargs lasti @@ -47,6 +50,7 @@ stackdepth stringlib structseq subparams +ticketer tok_oldval tvars unaryop @@ -56,6 +60,7 @@ VARKEYWORDS varkwarg wbits weakreflist +webpki withitem withs xstat diff --git a/.cspell.dict/rust-more.txt b/.cspell.dict/rust-more.txt index 6f89fdfafe1..f27e53bd6ed 100644 --- a/.cspell.dict/rust-more.txt +++ b/.cspell.dict/rust-more.txt @@ -50,6 +50,7 @@ nanos nonoverlapping objclass peekable +pemfile powc powf powi @@ -61,6 +62,7 @@ rposition rsplitn rustc rustfmt +rustls rustyline seedable seekfrom diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index dcae77693af..6963ee5f7c4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,8 @@ concurrency: cancel-in-progress: true env: - CARGO_ARGS: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl + CARGO_ARGS: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite,ssl-rustls + CARGO_ARGS_NO_SSL: --no-default-features --features stdlib,importlib,stdio,encodings,sqlite # Skip additional tests on Windows. They are checked on Linux and MacOS. # test_glob: many failing tests # test_io: many failing tests @@ -169,7 +170,7 @@ jobs: target: aarch64-apple-ios if: runner.os == 'macOS' - name: Check compilation for iOS - run: cargo check --target aarch64-apple-ios + run: cargo check --target aarch64-apple-ios ${{ env.CARGO_ARGS_NO_SSL }} if: runner.os == 'macOS' exotic_targets: @@ -186,14 +187,14 @@ jobs: - 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 + run: cargo check --target i686-unknown-linux-gnu ${{ env.CARGO_ARGS_NO_SSL }} - uses: dtolnay/rust-toolchain@stable with: target: aarch64-linux-android - name: Check compilation for android - run: cargo check --target aarch64-linux-android + run: cargo check --target aarch64-linux-android ${{ env.CARGO_ARGS_NO_SSL }} - uses: dtolnay/rust-toolchain@stable with: @@ -202,28 +203,28 @@ jobs: - 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 + run: cargo check --target aarch64-unknown-linux-gnu ${{ env.CARGO_ARGS_NO_SSL }} - uses: dtolnay/rust-toolchain@stable with: target: i686-unknown-linux-musl - name: Check compilation for musl - run: cargo check --target i686-unknown-linux-musl + run: cargo check --target i686-unknown-linux-musl ${{ env.CARGO_ARGS_NO_SSL }} - uses: dtolnay/rust-toolchain@stable with: target: x86_64-unknown-freebsd - name: Check compilation for freebsd - run: cargo check --target x86_64-unknown-freebsd + run: cargo check --target x86_64-unknown-freebsd ${{ env.CARGO_ARGS_NO_SSL }} - uses: dtolnay/rust-toolchain@stable with: target: x86_64-unknown-freebsd - name: Check compilation for freeBSD - run: cargo check --target x86_64-unknown-freebsd + run: cargo check --target x86_64-unknown-freebsd ${{ env.CARGO_ARGS_NO_SSL }} # - name: Prepare repository for redox compilation # run: bash scripts/redox/uncomment-cargo.sh diff --git a/Cargo.lock b/Cargo.lock index e1b1d958d30..cec9f7afb47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.12" @@ -134,6 +145,45 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic" version = "0.6.1" @@ -179,12 +229,57 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-fips-sys" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede71ad84efb06d748d9af3bc500b14957a96282a69a6833b1420dcacb411cc3" +dependencies = [ + "bindgen 0.72.1", + "cc", + "cmake", + "dunce", + "fs_extra", + "regex", +] + +[[package]] +name = "aws-lc-rs" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +dependencies = [ + "aws-lc-fips-sys", + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +dependencies = [ + "bindgen 0.72.1", + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bindgen" version = "0.71.1" @@ -205,6 +300,26 @@ dependencies = [ "syn", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.9.4", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -235,6 +350,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "bstr" version = "1.12.0" @@ -261,6 +385,12 @@ version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + [[package]] name = "bzip2" version = "0.6.1" @@ -294,6 +424,15 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + [[package]] name = "cc" version = "1.2.41" @@ -301,9 +440,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -365,6 +512,16 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -410,6 +567,15 @@ dependencies = [ "error-code", ] +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "collection_literals" version = "1.0.3" @@ -422,6 +588,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.9.0" @@ -458,6 +634,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.4.2" @@ -474,6 +656,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -751,6 +943,59 @@ dependencies = [ "memchr", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive-where" version = "1.6.0" @@ -794,6 +1039,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dns-lookup" version = "3.0.0" @@ -806,6 +1062,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -916,13 +1178,19 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +[[package]] +name = "flagset" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" + [[package]] name = "flame" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc2706461e1ee94f55cab2ed2e3d34ae9536cfa830358ef80acff1a3dacab30" dependencies = [ - "lazy_static", + "lazy_static 0.2.11", "serde", "serde_derive", "serde_json", @@ -984,6 +1252,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "generic-array" version = "0.14.9" @@ -1128,6 +1402,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.11" @@ -1177,6 +1460,16 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "block-padding", + "generic-array", +] + [[package]] name = "insta" version = "1.43.2" @@ -1260,6 +1553,38 @@ dependencies = [ "syn", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.81" @@ -1295,6 +1620,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lexical-parse-float" version = "1.0.6" @@ -1650,6 +1981,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -1659,6 +2000,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1708,6 +2055,15 @@ dependencies = [ "syn", ] +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1825,6 +2181,25 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "phf" version = "0.11.3" @@ -1919,6 +2294,33 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der", + "pbkdf2", + "scrypt", + "sha2", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "pkcs5", + "rand_core 0.6.4", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -1979,6 +2381,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2325,6 +2733,20 @@ dependencies = [ "syn", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ruff_python_ast" version = "0.0.0" @@ -2398,6 +2820,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "1.1.2" @@ -2411,6 +2842,89 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustpython" version = "0.4.0" @@ -2597,13 +3111,16 @@ dependencies = [ "adler32", "ahash", "ascii", + "aws-lc-rs", "base64", "blake2", "bzip2", "cfg-if", + "chrono", "crc32fast", "crossbeam-utils", "csv-core", + "der", "digest", "dns-lookup", "dyn-clone", @@ -2628,16 +3145,23 @@ dependencies = [ "num-integer", "num-traits", "num_enum", + "oid-registry", "openssl", "openssl-probe", "openssl-sys", "page_size", "parking_lot", "paste", + "pem-rfc7468", "phf 0.11.3", + "pkcs8", "pymath", "rand_core 0.9.3", "rustix", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-platform-verifier", "rustpython-common", "rustpython-derive", "rustpython-vm", @@ -2661,8 +3185,11 @@ dependencies = [ "unicode-casing", "unicode_names2 2.0.0", "uuid", + "webpki-roots 0.26.11", "widestring", "windows-sys 0.59.0", + "x509-cert", + "x509-parser", "xml", "xz2", ] @@ -2815,6 +3342,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -2845,6 +3381,40 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.9.4", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -2919,6 +3489,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2945,7 +3526,7 @@ name = "shared-build" version = "0.2.0" source = "git+https://github.com/arihant2math/tkinter.git?tag=v0.2.0#198fc35b1f18f4eda401f97a641908f321b1403a" dependencies = [ - "bindgen", + "bindgen 0.71.1", ] [[package]] @@ -2954,6 +3535,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -2988,6 +3578,16 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3046,6 +3646,17 @@ dependencies = [ "syn", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -3053,7 +3664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.4", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3157,6 +3768,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "timsort" version = "0.1.3" @@ -3197,6 +3839,27 @@ dependencies = [ "shared-build", ] +[[package]] +name = "tls_codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "toml" version = "0.8.23" @@ -3457,6 +4120,18 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "utf8parse" version = "0.2.2" @@ -3605,6 +4280,33 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.4", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "8.0.0" @@ -3741,6 +4443,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -3777,6 +4488,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3810,6 +4536,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3822,6 +4554,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3834,6 +4572,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3858,6 +4602,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3870,6 +4620,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3882,6 +4638,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3894,6 +4656,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3947,6 +4715,37 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature", + "spki", + "tls_codec", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static 1.5.0", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + [[package]] name = "xml" version = "1.0.1" @@ -3982,6 +4781,26 @@ dependencies = [ "syn", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zlib-rs" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 5f8f7a5b555..c1ee8eaa3d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ repository.workspace = true license.workspace = true [features] -default = ["threading", "stdlib", "stdio", "importlib"] +default = ["threading", "stdlib", "stdio", "importlib", "ssl-rustls"] importlib = ["rustpython-vm/importlib"] encodings = ["rustpython-vm/encodings"] stdio = ["rustpython-vm/stdio"] @@ -20,8 +20,10 @@ freeze-stdlib = ["stdlib", "rustpython-vm/freeze-stdlib", "rustpython-pylib?/fre 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] diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index fea3a2ce692..f073def5bc1 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -426,7 +426,6 @@ def test_random(self): ssl.RAND_add(b"this is a random bytes object", 75.0) ssl.RAND_add(bytearray(b"this is a random bytearray object"), 75.0) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_parse_cert(self): # note that this uses an 'unofficial' function in _ssl.c, # provided solely for this test, to exercise the certificate @@ -506,7 +505,6 @@ def test_parse_cert_CVE_2013_4238(self): self.assertEqual(p['subjectAltName'], san) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_parse_all_sans(self): p = ssl._ssl._test_decode_cert(ALLSANFILE) self.assertEqual(p['subjectAltName'], @@ -927,7 +925,6 @@ def test_connect_ex_error(self): ) self.assertIn(rc, errors) - @unittest.skip("TODO: RUSTPYTHON; hangs") def test_read_write_zero(self): # empty reads and writes now work, bpo-42854, bpo-31711 client_context, server_context, hostname = testing_context() @@ -993,7 +990,6 @@ def test_get_ciphers(self): len(intersection), 2, f"\ngot: {sorted(names)}\nexpected: {sorted(expected)}" ) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_options(self): # Test default SSLContext options ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) @@ -1066,8 +1062,8 @@ def test_hostname_checks_common_name(self): with self.assertRaises(AttributeError): ctx.hostname_checks_common_name = True - @ignore_deprecation @unittest.expectedFailure # TODO: RUSTPYTHON + @ignore_deprecation def test_min_max_version(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # OpenSSL default is MINIMUM_SUPPORTED, however some vendors like @@ -1185,7 +1181,6 @@ def test_verify_flags(self): with self.assertRaises(TypeError): ctx.verify_flags = None - @unittest.expectedFailure # TODO: RUSTPYTHON def test_load_cert_chain(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # Combined key and cert in a single file @@ -1294,7 +1289,6 @@ def race(): self.assertIsNone(cm.exc_value) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_load_verify_locations(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_verify_locations(CERTFILE) @@ -1314,7 +1308,6 @@ def test_load_verify_locations(self): # Issue #10989: crash if the second argument type is invalid self.assertRaises(TypeError, ctx.load_verify_locations, None, True) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_load_verify_cadata(self): # test cadata with open(CAFILE_CACERT) as f: @@ -1380,7 +1373,6 @@ def test_load_verify_cadata(self): with self.assertRaises(ssl.SSLError): ctx.load_verify_locations(cadata=cacert_der + b"A") - @unittest.expectedFailure # TODO: RUSTPYTHON def test_load_dh_params(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) try: @@ -1473,7 +1465,6 @@ def test_cert_store_stats(self): self.assertEqual(ctx.cert_store_stats(), {'x509_ca': 1, 'crl': 0, 'x509': 2}) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_get_ca_certs(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) self.assertEqual(ctx.get_ca_certs(), []) @@ -1732,7 +1723,6 @@ def test_lib_reason(self): s = str(cm.exception) self.assertTrue("NO_START_LINE" in s, s) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_subclass(self): # Check that the appropriate SSLError subclass is raised # (this only tests one of them) @@ -1751,7 +1741,6 @@ def test_subclass(self): self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_bad_server_hostname(self): ctx = ssl.create_default_context() with self.assertRaises(ValueError): @@ -1838,7 +1827,6 @@ def test_private_init(self): with self.assertRaisesRegex(TypeError, "public constructor"): ssl.SSLObject(bio, bio) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_unwrap(self): client_ctx, server_ctx, hostname = testing_context() c_in = ssl.MemoryBIO() @@ -2193,7 +2181,6 @@ def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs): % (count, func.__name__)) return ret - @unittest.expectedFailure # TODO: RUSTPYTHON def test_bio_handshake(self): sock = socket.socket(socket.AF_INET) self.addCleanup(sock.close) @@ -2230,7 +2217,6 @@ def test_bio_handshake(self): pass self.assertRaises(ssl.SSLError, sslobj.write, b'foo') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_bio_read_write_data(self): sock = socket.socket(socket.AF_INET) self.addCleanup(sock.close) @@ -2248,7 +2234,6 @@ def test_bio_read_write_data(self): self.assertEqual(buf, b'foo\n') self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_transport_eof(self): client_context, server_context, hostname = testing_context() with socket.socket(socket.AF_INET) as sock: @@ -3565,7 +3550,6 @@ def test_socketserver(self): f.close() self.assertEqual(d1, d2) - @unittest.skip("TODO: RUSTPYTHON; hangs") def test_asyncore_server(self): """Check the example asyncore integration.""" if support.verbose: @@ -3595,7 +3579,6 @@ def test_asyncore_server(self): if support.verbose: sys.stdout.write(" client: connection closed.\n") - @unittest.skip("TODO: RUSTPYTHON; hangs") def test_recv_send(self): """Test recv(), send() and friends.""" if support.verbose: @@ -3732,7 +3715,6 @@ def _recvfrom_into(): s.close() - @unittest.expectedFailure # TODO: RUSTPYTHON def test_recv_zero(self): server = ThreadedEchoServer(CERTFILE) self.enterContext(server) @@ -4040,6 +4022,7 @@ def test_default_ecdh_curve(self): s.connect((HOST, server.port)) self.assertIn("ECDH", s.cipher()[0]) + @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipUnless("tls-unique" in ssl.CHANNEL_BINDING_TYPES, "'tls-unique' channel binding not available") def test_tls_unique_channel_binding(self): @@ -4212,7 +4195,6 @@ def test_selected_alpn_protocol_if_server_uses_alpn(self): sni_name=hostname) self.assertIs(stats['client_alpn_protocol'], None) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_alpn_protocols(self): server_protocols = ['foo', 'bar', 'milkshake'] protocol_tests = [ @@ -4263,7 +4245,6 @@ def check_common_name(self, stats, name): cert = stats['peercert'] self.assertIn((('commonName', name),), cert['subject']) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_sni_callback(self): calls = [] server_context, other_context, client_context = self.sni_contexts() @@ -4514,7 +4495,6 @@ def test_session_handling(self): 'Session refers to a different SSLContext.') @requires_tls_version('TLSv1_2') - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') def test_psk(self): psk = bytes.fromhex('deadbeef') @@ -4583,7 +4563,6 @@ def server_callback(identity): s.connect((HOST, server.port)) @requires_tls_version('TLSv1_3') - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipUnless(ssl.HAS_PSK, 'TLS-PSK disabled on this OpenSSL build') def test_psk_tls1_3(self): psk = bytes.fromhex('deadbeef') @@ -4616,6 +4595,43 @@ def server_callback(identity): with client_context.wrap_socket(socket.socket()) as s: s.connect((HOST, server.port)) + @unittest.skip("TODO: rustpython") + def test_thread_recv_while_main_thread_sends(self): + # GH-137583: Locking was added to calls to send() and recv() on SSL + # socket objects. This seemed fine at the surface level because those + # calls weren't re-entrant, but recv() calls would implicitly mimick + # holding a lock by blocking until it received data. This means that + # if a thread started to infinitely block until data was received, calls + # to send() would deadlock, because it would wait forever on the lock + # that the recv() call held. + data = b"1" * 1024 + event = threading.Event() + def background(sock): + event.set() + received = sock.recv(len(data)) + self.assertEqual(received, data) + + client_context, server_context, hostname = testing_context() + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as sock: + sock.connect((HOST, server.port)) + sock.settimeout(1) + sock.setblocking(1) + # Ensure that the server is ready to accept requests + sock.sendall(b"123") + self.assertEqual(sock.recv(3), b"123") + with threading_helper.catch_threading_exception() as cm: + thread = threading.Thread(target=background, + args=(sock,), daemon=True) + thread.start() + event.wait() + sock.sendall(data) + thread.join() + if cm.exc_value is not None: + raise cm.exc_value + @unittest.skipUnless(has_tls_version('TLSv1_3'), "Test needs TLS 1.3") class TestPostHandshakeAuth(unittest.TestCase): @@ -4736,6 +4752,7 @@ def test_pha_optional(self): s.write(b'HASCERT') self.assertEqual(s.recv(1024), b'TRUE\n') + @unittest.expectedFailure # TODO: RUSTPYTHON def test_pha_optional_nocert(self): if support.verbose: sys.stdout.write("\n") @@ -4775,6 +4792,7 @@ def test_pha_no_pha_client(self): s.write(b'PHA') self.assertIn(b'extension not received', s.recv(1024)) + @unittest.expectedFailure # TODO: RUSTPYTHON def test_pha_no_pha_server(self): # server doesn't have PHA enabled, cert is requested in handshake client_context, server_context, hostname = testing_context() @@ -4844,7 +4862,6 @@ def test_bpo37428_pha_cert_none(self): # server cert has not been validated self.assertEqual(s.getpeercert(), {}) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_internal_chain_client(self): client_context, server_context, hostname = testing_context( server_chain=False @@ -4916,7 +4933,6 @@ def test_certificate_chain(self): self.assertEqual(ee, uvc[0]) self.assertNotEqual(ee, ca) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_internal_chain_server(self): client_context, server_context, hostname = testing_context() client_context.load_cert_chain(SIGNED_CERTFILE) @@ -5040,7 +5056,6 @@ def test_keylog_env(self): ctx = ssl._create_stdlib_context() self.assertEqual(ctx.keylog_filename, os_helper.TESTFN) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_msg_callback(self): client_context, server_context, hostname = testing_context() @@ -5085,7 +5100,6 @@ def msg_cb(conn, direction, version, content_type, msg_type, data): msg ) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_msg_callback_deadlock_bpo43577(self): client_context, server_context, hostname = testing_context() server_context2 = testing_context()[1] diff --git a/README.md b/README.md index ce5f02bee23..86d0738ec8e 100644 --- a/README.md +++ b/README.md @@ -66,17 +66,11 @@ Welcome to the magnificent Rust Python interpreter >>>>> ``` -If you'd like to make https requests, you can enable the `ssl` feature, which -also lets you install the `pip` package manager. Note that on Windows, you may -need to install OpenSSL, or you can enable the `ssl-vendor` feature instead, -which compiles OpenSSL for you but requires a C compiler, perl, and `make`. -OpenSSL version 3 is expected and tested in CI. Older versions may not work. - -Once you've installed rustpython with SSL support, you can install pip by +You can install pip by running: ```bash -cargo install --git https://github.com/RustPython/RustPython --features ssl +cargo install --git https://github.com/RustPython/RustPython rustpython --install-pip ``` @@ -88,6 +82,13 @@ conda install rustpython -c conda-forge rustpython ``` +### SSL provider + +For HTTPS requests, `ssl-rustls` feature is enabled by default. You can replace it with `ssl-openssl` feature if your environment requires OpenSSL. +Note that to use OpenSSL on Windows, you may need to install OpenSSL, or you can enable the `ssl-vendor` feature instead, +which compiles OpenSSL for you but requires a C compiler, perl, and `make`. +OpenSSL version 3 is expected and tested in CI. Older versions may not work. + ### WASI You can compile RustPython to a standalone WebAssembly WASI module so it can run anywhere. diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index 89d19f15c8a..ab90f52b2df 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -520,7 +520,7 @@ impl ExecutingFrame<'_> { trace!(" {:#?}", self); trace!( " Executing op code: {}", - instruction.display(arg, &self.code.code).to_string() + instruction.display(arg, &self.code.code) ); trace!("======="); } diff --git a/src/lib.rs b/src/lib.rs index dafc3fcd0d5..976d0bc0a34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,14 @@ pub use rustpython_vm as vm; pub use settings::{InstallPipMode, RunMode, parse_opts}; pub use shell::run_shell; +#[cfg(all( + feature = "ssl", + not(any(feature = "ssl-rustls", feature = "ssl-openssl")) +))] +compile_error!( + "Feature \"ssl\" is now enabled by either \"ssl-rustls\" or \"ssl-openssl\" to be enabled. Do not manually pass \"ssl\" feature. To enable ssl-openssl, use --no-default-features to disable ssl-rustls" +); + /// The main cli of the `rustpython` interpreter. This function will return `std::process::ExitCode` /// based on the return code of the python code ran through the cli. pub fn run(init: impl FnOnce(&mut VirtualMachine) + 'static) -> ExitCode { @@ -141,7 +149,7 @@ __import__("io").TextIOWrapper( } fn install_pip(installer: InstallPipMode, scope: Scope, vm: &VirtualMachine) -> PyResult<()> { - if cfg!(not(feature = "ssl")) { + if !cfg!(feature = "ssl") { return Err(vm.new_exception_msg( vm.ctx.exceptions.system_error.to_owned(), "install-pip requires rustpython be build with '--features=ssl'".to_owned(), diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml index 7f64802d352..e62872324ea 100644 --- a/stdlib/Cargo.toml +++ b/stdlib/Cargo.toml @@ -15,8 +15,12 @@ default = ["compiler"] compiler = ["rustpython-vm/compiler"] threading = ["rustpython-common/threading", "rustpython-vm/threading"] sqlite = ["dep:libsqlite3-sys"] -ssl = ["openssl", "openssl-sys", "foreign-types-shared", "openssl-probe"] -ssl-vendor = ["ssl", "openssl/vendored"] +# SSL backends - default to rustls +ssl = [] +ssl-rustls = ["ssl", "rustls", "rustls-native-certs", "rustls-pemfile", "rustls-platform-verifier", "x509-cert", "x509-parser", "der", "pem-rfc7468", "webpki-roots", "aws-lc-rs", "oid-registry", "pkcs8"] +ssl-rustls-fips = ["ssl-rustls", "aws-lc-rs/fips"] +ssl-openssl = ["ssl", "openssl", "openssl-sys", "foreign-types-shared", "openssl-probe"] +ssl-vendor = ["ssl-openssl", "openssl/vendored"] tkinter = ["dep:tk-sys", "dep:tcl-sys"] [dependencies] @@ -86,6 +90,7 @@ bzip2 = "0.6" # tkinter tk-sys = { git = "https://github.com/arihant2math/tkinter.git", tag = "v0.2.0", optional = true } tcl-sys = { git = "https://github.com/arihant2math/tkinter.git", tag = "v0.2.0", optional = true } +chrono.workspace = true # uuid [target.'cfg(not(any(target_os = "ios", target_os = "android", target_os = "windows", target_arch = "wasm32", target_os = "redox")))'.dependencies] @@ -107,11 +112,27 @@ rustix = { workspace = true } gethostname = "1.0.2" socket2 = { version = "0.6.0", features = ["all"] } dns-lookup = "3.0" + +# OpenSSL dependencies (optional, for ssl-openssl feature) openssl = { version = "0.10.72", optional = true } openssl-sys = { version = "0.9.110", optional = true } openssl-probe = { version = "0.1.5", optional = true } foreign-types-shared = { version = "0.1.1", optional = true } +# Rustls dependencies (optional, for ssl-rustls feature) +rustls = { version = "0.23.35", default-features = false, features = ["std", "tls12", "aws_lc_rs"], optional = true } +rustls-native-certs = { version = "0.8", optional = true } +rustls-pemfile = { version = "2.2", optional = true } +rustls-platform-verifier = { version = "0.6", optional = true } +x509-cert = { version = "0.2.5", features = ["pem", "builder"], optional = true } +x509-parser = { version = "0.16", optional = true } +der = { version = "0.7", features = ["alloc", "oid"], optional = true } +pem-rfc7468 = { version = "0.7", optional = true } +webpki-roots = { version = "0.26", optional = true } +aws-lc-rs = { version = "1.14.1", optional = true } +oid-registry = { version = "0.7", features = ["x509", "pkcs1", "nist_algs"], optional = true } +pkcs8 = { version = "0.10", features = ["encryption", "pkcs5", "pem"], optional = true } + [target.'cfg(not(any(target_os = "android", target_arch = "wasm32")))'.dependencies] libsqlite3-sys = { version = "0.28", features = ["bundled"], optional = true } lzma-sys = "0.1" diff --git a/stdlib/build.rs b/stdlib/build.rs index b7bf6307157..83ebd81ead6 100644 --- a/stdlib/build.rs +++ b/stdlib/build.rs @@ -23,25 +23,28 @@ fn main() { println!("cargo::rustc-check-cfg=cfg({cfg})"); } - #[allow(clippy::unusual_byte_groupings)] - if let Ok(v) = std::env::var("DEP_OPENSSL_VERSION_NUMBER") { - println!("cargo:rustc-env=OPENSSL_API_VERSION={v}"); - // cfg setup from openssl crate's build script - let version = u64::from_str_radix(&v, 16).unwrap(); - for (ver, cfg) in ossl_vers { - if version >= ver { - println!("cargo:rustc-cfg={cfg}"); + #[cfg(feature = "ssl-openssl")] + { + #[allow(clippy::unusual_byte_groupings)] + if let Ok(v) = std::env::var("DEP_OPENSSL_VERSION_NUMBER") { + println!("cargo:rustc-env=OPENSSL_API_VERSION={v}"); + // cfg setup from openssl crate's build script + let version = u64::from_str_radix(&v, 16).unwrap(); + for (ver, cfg) in ossl_vers { + if version >= ver { + println!("cargo:rustc-cfg={cfg}"); + } } } - } - if let Ok(v) = std::env::var("DEP_OPENSSL_CONF") { - for conf in v.split(',') { - println!("cargo:rustc-cfg=osslconf=\"{conf}\""); + if let Ok(v) = std::env::var("DEP_OPENSSL_CONF") { + for conf in v.split(',') { + println!("cargo:rustc-cfg=osslconf=\"{conf}\""); + } + } + // it's possible for openssl-sys to link against the system openssl under certain conditions, + // so let the ssl module know to only perform a probe if we're actually vendored + if std::env::var("DEP_OPENSSL_VENDORED").is_ok_and(|s| s == "1") { + println!("cargo::rustc-cfg=openssl_vendored") } - } - // it's possible for openssl-sys to link against the system openssl under certain conditions, - // so let the ssl module know to only perform a probe if we're actually vendored - if std::env::var("DEP_OPENSSL_VENDORED").is_ok_and(|s| s == "1") { - println!("cargo::rustc-cfg=openssl_vendored") } } diff --git a/stdlib/src/lib.rs b/stdlib/src/lib.rs index 706ce0ef210..01a27b76609 100644 --- a/stdlib/src/lib.rs +++ b/stdlib/src/lib.rs @@ -75,8 +75,14 @@ mod select; not(any(target_os = "android", target_arch = "wasm32")) ))] mod sqlite; -#[cfg(all(not(target_arch = "wasm32"), feature = "ssl"))] + +#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))] +mod openssl; +#[cfg(all(not(target_arch = "wasm32"), feature = "ssl-rustls"))] mod ssl; +#[cfg(all(feature = "ssl-openssl", feature = "ssl-rustls"))] +compile_error!("features \"ssl-openssl\" and \"ssl-rustls\" are mutually exclusive"); + #[cfg(all(unix, not(target_os = "redox"), not(target_os = "ios")))] mod termios; #[cfg(not(any( @@ -167,10 +173,14 @@ pub fn get_module_inits() -> impl Iterator, StdlibInit { "_sqlite3" => sqlite::make_module, } - #[cfg(feature = "ssl")] + #[cfg(all(not(target_arch = "wasm32"), feature = "ssl-rustls"))] { "_ssl" => ssl::make_module, } + #[cfg(all(not(target_arch = "wasm32"), feature = "ssl-openssl"))] + { + "_ssl" => openssl::make_module, + } #[cfg(windows)] { "_overlapped" => overlapped::make_module, diff --git a/stdlib/src/openssl.rs b/stdlib/src/openssl.rs new file mode 100644 index 00000000000..ea67d605f76 --- /dev/null +++ b/stdlib/src/openssl.rs @@ -0,0 +1,3705 @@ +// spell-checker:disable + +mod cert; + +// Conditional compilation for OpenSSL version-specific error codes +cfg_if::cfg_if! { + if #[cfg(ossl310)] { + // OpenSSL 3.1.0+ + mod ssl_data_31; + use ssl_data_31 as ssl_data; + } else if #[cfg(ossl300)] { + // OpenSSL 3.0.0+ + mod ssl_data_300; + use ssl_data_300 as ssl_data; + } else { + // OpenSSL 1.1.1+ (fallback) + mod ssl_data_111; + use ssl_data_111 as ssl_data; + } +} + +use crate::vm::{PyRef, VirtualMachine, builtins::PyModule}; +use openssl_probe::ProbeResult; + +pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef { + // if openssl is vendored, it doesn't know the locations + // of system certificates - cache the probe result now. + #[cfg(openssl_vendored)] + LazyLock::force(&PROBE); + _ssl::make_module(vm) +} + +// define our own copy of ProbeResult so we can handle the vendor case +// easily, without having to have a bunch of cfgs +cfg_if::cfg_if! { + if #[cfg(openssl_vendored)] { + use std::sync::LazyLock; + static PROBE: LazyLock = LazyLock::new(openssl_probe::probe); + fn probe() -> &'static ProbeResult { &PROBE } + } else { + fn probe() -> &'static ProbeResult { + &ProbeResult { cert_file: None, cert_dir: None } + } + } +} + +#[allow(non_upper_case_globals)] +#[pymodule(with(cert::ssl_cert, ossl101, ossl111, windows))] +mod _ssl { + use super::{bio, probe}; + use crate::{ + common::lock::{ + PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard, + }, + socket::{self, PySocket}, + vm::{ + AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, + builtins::{ + PyBaseExceptionRef, PyBytesRef, PyListRef, PyOSError, PyStrRef, PyTypeRef, PyWeak, + }, + class_or_notimplemented, + convert::ToPyException, + exceptions, + function::{ + ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath, + OptionalArg, PyComparisonValue, + }, + types::{Comparable, Constructor, PyComparisonOp}, + utils::ToCString, + }, + }; + use crossbeam_utils::atomic::AtomicCell; + use foreign_types_shared::{ForeignType, ForeignTypeRef}; + use openssl::{ + asn1::{Asn1Object, Asn1ObjectRef}, + error::ErrorStack, + nid::Nid, + ssl::{self, SslContextBuilder, SslOptions, SslVerifyMode}, + x509::X509, + }; + use openssl_sys as sys; + use rustpython_vm::ospath::OsPath; + use std::{ + ffi::CStr, + fmt, + io::{Read, Write}, + path::{Path, PathBuf}, + sync::LazyLock, + time::Instant, + }; + + // Import certificate types from parent module + use super::cert::{self, cert_to_certificate, cert_to_py}; + + // Re-export PySSLCertificate to make it available in the _ssl module + // It will be automatically exposed to Python via #[pyclass] + #[allow(unused_imports)] + use super::cert::PySSLCertificate; + + // Constants + #[pyattr] + use sys::{ + // SSL Alert Descriptions that are exported by openssl_sys + SSL_AD_DECODE_ERROR, + SSL_AD_ILLEGAL_PARAMETER, + SSL_AD_UNRECOGNIZED_NAME, + // SSL_ERROR_INVALID_ERROR_CODE, + SSL_ERROR_SSL, + // SSL_ERROR_WANT_X509_LOOKUP, + SSL_ERROR_SYSCALL, + SSL_ERROR_WANT_CONNECT, + SSL_ERROR_WANT_READ, + SSL_ERROR_WANT_WRITE, + SSL_ERROR_ZERO_RETURN, + SSL_OP_CIPHER_SERVER_PREFERENCE as OP_CIPHER_SERVER_PREFERENCE, + SSL_OP_ENABLE_MIDDLEBOX_COMPAT as OP_ENABLE_MIDDLEBOX_COMPAT, + SSL_OP_LEGACY_SERVER_CONNECT as OP_LEGACY_SERVER_CONNECT, + SSL_OP_NO_SSLv2 as OP_NO_SSLv2, + SSL_OP_NO_SSLv3 as OP_NO_SSLv3, + SSL_OP_NO_TICKET as OP_NO_TICKET, + SSL_OP_NO_TLSv1 as OP_NO_TLSv1, + SSL_OP_SINGLE_DH_USE as OP_SINGLE_DH_USE, + SSL_OP_SINGLE_ECDH_USE as OP_SINGLE_ECDH_USE, + X509_V_FLAG_ALLOW_PROXY_CERTS as VERIFY_ALLOW_PROXY_CERTS, + X509_V_FLAG_CRL_CHECK as VERIFY_CRL_CHECK_LEAF, + X509_V_FLAG_PARTIAL_CHAIN as VERIFY_X509_PARTIAL_CHAIN, + X509_V_FLAG_TRUSTED_FIRST as VERIFY_X509_TRUSTED_FIRST, + X509_V_FLAG_X509_STRICT as VERIFY_X509_STRICT, + }; + + // SSL Alert Descriptions (RFC 5246 and extensions) + // Hybrid approach: use openssl_sys constants where available, hardcode others + #[pyattr] + const ALERT_DESCRIPTION_CLOSE_NOTIFY: libc::c_int = 0; + #[pyattr] + const ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: libc::c_int = 10; + #[pyattr] + const ALERT_DESCRIPTION_BAD_RECORD_MAC: libc::c_int = 20; + #[pyattr] + const ALERT_DESCRIPTION_RECORD_OVERFLOW: libc::c_int = 22; + #[pyattr] + const ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: libc::c_int = 30; + #[pyattr] + const ALERT_DESCRIPTION_HANDSHAKE_FAILURE: libc::c_int = 40; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE: libc::c_int = 42; + #[pyattr] + const ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: libc::c_int = 43; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_REVOKED: libc::c_int = 44; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: libc::c_int = 45; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: libc::c_int = 46; + #[pyattr] + const ALERT_DESCRIPTION_ILLEGAL_PARAMETER: libc::c_int = SSL_AD_ILLEGAL_PARAMETER; + #[pyattr] + const ALERT_DESCRIPTION_UNKNOWN_CA: libc::c_int = 48; + #[pyattr] + const ALERT_DESCRIPTION_ACCESS_DENIED: libc::c_int = 49; + #[pyattr] + const ALERT_DESCRIPTION_DECODE_ERROR: libc::c_int = SSL_AD_DECODE_ERROR; + #[pyattr] + const ALERT_DESCRIPTION_DECRYPT_ERROR: libc::c_int = 51; + #[pyattr] + const ALERT_DESCRIPTION_PROTOCOL_VERSION: libc::c_int = 70; + #[pyattr] + const ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: libc::c_int = 71; + #[pyattr] + const ALERT_DESCRIPTION_INTERNAL_ERROR: libc::c_int = 80; + #[pyattr] + const ALERT_DESCRIPTION_USER_CANCELLED: libc::c_int = 90; + #[pyattr] + const ALERT_DESCRIPTION_NO_RENEGOTIATION: libc::c_int = 100; + #[pyattr] + const ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: libc::c_int = 110; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: libc::c_int = 111; + #[pyattr] + const ALERT_DESCRIPTION_UNRECOGNIZED_NAME: libc::c_int = SSL_AD_UNRECOGNIZED_NAME; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: libc::c_int = 113; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: libc::c_int = 114; + #[pyattr] + const ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: libc::c_int = 115; + + // CRL verification constants + #[pyattr] + const VERIFY_CRL_CHECK_CHAIN: libc::c_ulong = + sys::X509_V_FLAG_CRL_CHECK | sys::X509_V_FLAG_CRL_CHECK_ALL; + + // taken from CPython, should probably be kept up to date with their version if it ever changes + #[pyattr] + const _DEFAULT_CIPHERS: &str = + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"; + // #[pyattr] PROTOCOL_SSLv2: u32 = SslVersion::Ssl2 as u32; // unsupported + // #[pyattr] PROTOCOL_SSLv3: u32 = SslVersion::Ssl3 as u32; + #[pyattr] + const PROTOCOL_SSLv23: u32 = SslVersion::Tls as u32; + #[pyattr] + const PROTOCOL_TLS: u32 = SslVersion::Tls as u32; + #[pyattr] + const PROTOCOL_TLS_CLIENT: u32 = SslVersion::TlsClient as u32; + #[pyattr] + const PROTOCOL_TLS_SERVER: u32 = SslVersion::TlsServer as u32; + #[pyattr] + const PROTOCOL_TLSv1: u32 = SslVersion::Tls1 as u32; + #[pyattr] + const PROTOCOL_TLSv1_1: u32 = SslVersion::Tls1_1 as u32; + #[pyattr] + const PROTOCOL_TLSv1_2: u32 = SslVersion::Tls1_2 as u32; + #[pyattr] + const PROTO_MINIMUM_SUPPORTED: i32 = ProtoVersion::MinSupported as i32; + #[pyattr] + const PROTO_SSLv3: i32 = ProtoVersion::Ssl3 as i32; + #[pyattr] + const PROTO_TLSv1: i32 = ProtoVersion::Tls1 as i32; + #[pyattr] + const PROTO_TLSv1_1: i32 = ProtoVersion::Tls1_1 as i32; + #[pyattr] + const PROTO_TLSv1_2: i32 = ProtoVersion::Tls1_2 as i32; + #[pyattr] + const PROTO_TLSv1_3: i32 = ProtoVersion::Tls1_3 as i32; + #[pyattr] + const PROTO_MAXIMUM_SUPPORTED: i32 = ProtoVersion::MaxSupported as i32; + #[pyattr] + const OP_ALL: libc::c_ulong = (sys::SSL_OP_ALL & !sys::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) as _; + #[pyattr] + const HAS_TLS_UNIQUE: bool = true; + #[pyattr] + const CERT_NONE: u32 = CertRequirements::None as u32; + #[pyattr] + const CERT_OPTIONAL: u32 = CertRequirements::Optional as u32; + #[pyattr] + const CERT_REQUIRED: u32 = CertRequirements::Required as u32; + #[pyattr] + const VERIFY_DEFAULT: u32 = 0; + #[pyattr] + const SSL_ERROR_EOF: u32 = 8; // custom for python + #[pyattr] + const HAS_SNI: bool = true; + #[pyattr] + const HAS_ECDH: bool = true; + #[pyattr] + const HAS_NPN: bool = false; + #[pyattr] + const HAS_ALPN: bool = true; + #[pyattr] + const HAS_SSLv2: bool = false; + #[pyattr] + const HAS_SSLv3: bool = false; + #[pyattr] + const HAS_TLSv1: bool = true; + #[pyattr] + const HAS_TLSv1_1: bool = true; + #[pyattr] + const HAS_TLSv1_2: bool = true; + #[pyattr] + const HAS_TLSv1_3: bool = cfg!(ossl111); + #[pyattr] + const HAS_PSK: bool = true; + + // Encoding constants for Certificate.public_bytes() + #[pyattr] + pub(crate) const ENCODING_PEM: i32 = sys::X509_FILETYPE_PEM; + #[pyattr] + pub(crate) const ENCODING_DER: i32 = sys::X509_FILETYPE_ASN1; + #[pyattr] + const ENCODING_PEM_AUX: i32 = sys::X509_FILETYPE_PEM + 0x100; + + // OpenSSL error codes for unexpected EOF detection + const ERR_LIB_SSL: i32 = 20; + const SSL_R_UNEXPECTED_EOF_WHILE_READING: i32 = 294; + + // SSL_VERIFY constants for post-handshake authentication + #[cfg(ossl111)] + const SSL_VERIFY_POST_HANDSHAKE: libc::c_int = 0x20; + + // the openssl version from the API headers + + #[pyattr(name = "OPENSSL_VERSION")] + fn openssl_version(_vm: &VirtualMachine) -> &str { + openssl::version::version() + } + #[pyattr(name = "OPENSSL_VERSION_NUMBER")] + fn openssl_version_number(_vm: &VirtualMachine) -> i64 { + openssl::version::number() + } + #[pyattr(name = "OPENSSL_VERSION_INFO")] + fn openssl_version_info(_vm: &VirtualMachine) -> OpensslVersionInfo { + parse_version_info(openssl::version::number()) + } + + #[pyattr(name = "_OPENSSL_API_VERSION")] + fn _openssl_api_version(_vm: &VirtualMachine) -> OpensslVersionInfo { + let openssl_api_version = i64::from_str_radix(env!("OPENSSL_API_VERSION"), 16) + .expect("OPENSSL_API_VERSION is malformed"); + parse_version_info(openssl_api_version) + } + + // SSL Exception Types + + /// An error occurred in the SSL implementation. + #[pyattr] + #[pyexception(name = "SSLError", base = PyOSError)] + #[derive(Debug)] + pub struct PySslError {} + + #[pyexception] + impl PySslError { + // Returns strerror attribute if available, otherwise str(args) + #[pymethod] + fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult { + // Try to get strerror attribute first (OSError compatibility) + if let Ok(strerror) = exc.as_object().get_attr("strerror", vm) + && !vm.is_none(&strerror) + { + return strerror.str(vm); + } + + // Otherwise return str(args) + exc.args().as_object().str(vm) + } + } + + /// A certificate could not be verified. + #[pyattr] + #[pyexception(name = "SSLCertVerificationError", base = PySslError)] + #[derive(Debug)] + pub struct PySslCertVerificationError {} + + #[pyexception] + impl PySslCertVerificationError {} + + /// SSL/TLS session closed cleanly. + #[pyattr] + #[pyexception(name = "SSLZeroReturnError", base = PySslError)] + #[derive(Debug)] + pub struct PySslZeroReturnError {} + + #[pyexception] + impl PySslZeroReturnError {} + + /// Non-blocking SSL socket needs to read more data. + #[pyattr] + #[pyexception(name = "SSLWantReadError", base = PySslError)] + #[derive(Debug)] + pub struct PySslWantReadError {} + + #[pyexception] + impl PySslWantReadError {} + + /// Non-blocking SSL socket needs to write more data. + #[pyattr] + #[pyexception(name = "SSLWantWriteError", base = PySslError)] + #[derive(Debug)] + pub struct PySslWantWriteError {} + + #[pyexception] + impl PySslWantWriteError {} + + /// System error when attempting SSL operation. + #[pyattr] + #[pyexception(name = "SSLSyscallError", base = PySslError)] + #[derive(Debug)] + pub struct PySslSyscallError {} + + #[pyexception] + impl PySslSyscallError {} + + /// SSL/TLS connection terminated abruptly. + #[pyattr] + #[pyexception(name = "SSLEOFError", base = PySslError)] + #[derive(Debug)] + pub struct PySslEOFError {} + + #[pyexception] + impl PySslEOFError {} + + type OpensslVersionInfo = (u8, u8, u8, u8, u8); + const fn parse_version_info(mut n: i64) -> OpensslVersionInfo { + let status = (n & 0xF) as u8; + n >>= 4; + let patch = (n & 0xFF) as u8; + n >>= 8; + let fix = (n & 0xFF) as u8; + n >>= 8; + let minor = (n & 0xFF) as u8; + n >>= 8; + let major = (n & 0xFF) as u8; + (major, minor, fix, patch, status) + } + + #[derive(Copy, Clone, num_enum::IntoPrimitive, num_enum::TryFromPrimitive, PartialEq)] + #[repr(i32)] + enum SslVersion { + Ssl2, + Ssl3 = 1, + Tls, + Tls1, + Tls1_1, + Tls1_2, + TlsClient = 0x10, + TlsServer, + } + + #[derive(Copy, Clone, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] + #[repr(i32)] + enum ProtoVersion { + MinSupported = -2, + Ssl3 = sys::SSL3_VERSION, + Tls1 = sys::TLS1_VERSION, + Tls1_1 = sys::TLS1_1_VERSION, + Tls1_2 = sys::TLS1_2_VERSION, + #[cfg(ossl111)] + Tls1_3 = sys::TLS1_3_VERSION, + #[cfg(not(ossl111))] + Tls1_3 = 0x304, + MaxSupported = -1, + } + + #[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] + #[repr(i32)] + enum CertRequirements { + None, + Optional, + Required, + } + + #[derive(Debug, PartialEq)] + enum SslServerOrClient { + Client, + Server, + } + + unsafe fn ptr2obj(ptr: *mut sys::ASN1_OBJECT) -> Option { + if ptr.is_null() { + None + } else { + Some(unsafe { Asn1Object::from_ptr(ptr) }) + } + } + + fn _txt2obj(s: &CStr, no_name: bool) -> Option { + unsafe { ptr2obj(sys::OBJ_txt2obj(s.as_ptr(), i32::from(no_name))) } + } + fn _nid2obj(nid: Nid) -> Option { + unsafe { ptr2obj(sys::OBJ_nid2obj(nid.as_raw())) } + } + + type PyNid = (libc::c_int, String, String, Option); + fn obj2py(obj: &Asn1ObjectRef, vm: &VirtualMachine) -> PyResult { + let nid = obj.nid(); + let short_name = nid + .short_name() + .map_err(|_| vm.new_value_error("NID has no short name".to_owned()))? + .to_owned(); + let long_name = nid + .long_name() + .map_err(|_| vm.new_value_error("NID has no long name".to_owned()))? + .to_owned(); + Ok(( + nid.as_raw(), + short_name, + long_name, + cert::obj2txt(obj, true), + )) + } + + #[derive(FromArgs)] + struct Txt2ObjArgs { + txt: PyStrRef, + #[pyarg(any, default = false)] + name: bool, + } + + #[pyfunction] + fn txt2obj(args: Txt2ObjArgs, vm: &VirtualMachine) -> PyResult { + _txt2obj(&args.txt.to_cstring(vm)?, !args.name) + .as_deref() + .ok_or_else(|| vm.new_value_error(format!("unknown object '{}'", args.txt))) + .and_then(|obj| obj2py(obj, vm)) + } + + #[pyfunction] + fn nid2obj(nid: libc::c_int, vm: &VirtualMachine) -> PyResult { + _nid2obj(Nid::from_raw(nid)) + .as_deref() + .ok_or_else(|| vm.new_value_error(format!("unknown NID {nid}"))) + .and_then(|obj| obj2py(obj, vm)) + } + + // Lazily compute and cache cert file/dir paths + static CERT_PATHS: LazyLock<(PathBuf, PathBuf)> = LazyLock::new(|| { + fn path_from_cstr(c: &CStr) -> PathBuf { + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + std::ffi::OsStr::from_bytes(c.to_bytes()).into() + } + #[cfg(windows)] + { + // Use lossy conversion for potential non-UTF8 + PathBuf::from(c.to_string_lossy().as_ref()) + } + } + + let probe = probe(); + let cert_file = probe + .cert_file + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| { + path_from_cstr(unsafe { CStr::from_ptr(sys::X509_get_default_cert_file()) }) + }); + let cert_dir = probe + .cert_dir + .as_ref() + .map(PathBuf::from) + .unwrap_or_else(|| { + path_from_cstr(unsafe { CStr::from_ptr(sys::X509_get_default_cert_dir()) }) + }); + (cert_file, cert_dir) + }); + + fn get_cert_file_dir() -> (&'static Path, &'static Path) { + let (cert_file, cert_dir) = &*CERT_PATHS; + (cert_file.as_path(), cert_dir.as_path()) + } + + // Lazily compute and cache cert environment variable names + static CERT_ENV_NAMES: LazyLock<(String, String)> = LazyLock::new(|| { + let cert_file_env = unsafe { CStr::from_ptr(sys::X509_get_default_cert_file_env()) } + .to_string_lossy() + .into_owned(); + let cert_dir_env = unsafe { CStr::from_ptr(sys::X509_get_default_cert_dir_env()) } + .to_string_lossy() + .into_owned(); + (cert_file_env, cert_dir_env) + }); + + #[pyfunction] + fn get_default_verify_paths( + vm: &VirtualMachine, + ) -> PyResult<(&'static str, PyObjectRef, &'static str, PyObjectRef)> { + let (cert_file_env, cert_dir_env) = &*CERT_ENV_NAMES; + let (cert_file, cert_dir) = get_cert_file_dir(); + let cert_file = OsPath::new_str(cert_file).filename(vm); + let cert_dir = OsPath::new_str(cert_dir).filename(vm); + Ok(( + cert_file_env.as_str(), + cert_file, + cert_dir_env.as_str(), + cert_dir, + )) + } + + #[pyfunction(name = "RAND_status")] + fn rand_status() -> i32 { + unsafe { sys::RAND_status() } + } + + #[pyfunction(name = "RAND_add")] + fn rand_add(string: ArgStrOrBytesLike, entropy: f64) { + let f = |b: &[u8]| { + for buf in b.chunks(libc::c_int::MAX as usize) { + unsafe { sys::RAND_add(buf.as_ptr() as *const _, buf.len() as _, entropy) } + } + }; + f(&string.borrow_bytes()) + } + + #[pyfunction(name = "RAND_bytes")] + fn rand_bytes(n: i32, vm: &VirtualMachine) -> PyResult> { + if n < 0 { + return Err(vm.new_value_error("num must be positive")); + } + let mut buf = vec![0; n as usize]; + openssl::rand::rand_bytes(&mut buf).map_err(|e| convert_openssl_error(vm, e))?; + Ok(buf) + } + + // Callback data stored in SSL context for SNI + struct SniCallbackData { + ssl_context: PyRef, + vm_ptr: *const VirtualMachine, + } + + impl Drop for SniCallbackData { + fn drop(&mut self) { + // PyRef will handle reference counting + } + } + + // Get or create an ex_data index for SNI callback data + fn get_sni_ex_data_index() -> libc::c_int { + use std::sync::LazyLock; + static SNI_EX_DATA_IDX: LazyLock = LazyLock::new(|| unsafe { + sys::SSL_get_ex_new_index( + 0, + std::ptr::null_mut(), + None, + None, + Some(sni_callback_data_free), + ) + }); + *SNI_EX_DATA_IDX + } + + // Free function for callback data + unsafe extern "C" fn sni_callback_data_free( + _parent: *mut libc::c_void, + ptr: *mut libc::c_void, + _ad: *mut sys::CRYPTO_EX_DATA, + _idx: libc::c_int, + _argl: libc::c_long, + _argp: *mut libc::c_void, + ) { + if !ptr.is_null() { + unsafe { + let _ = Box::from_raw(ptr as *mut SniCallbackData); + } + } + } + + // SNI callback function called by OpenSSL + unsafe extern "C" fn _servername_callback( + ssl_ptr: *mut sys::SSL, + al: *mut libc::c_int, + arg: *mut libc::c_void, + ) -> libc::c_int { + const SSL_TLSEXT_ERR_OK: libc::c_int = 0; + const SSL_TLSEXT_ERR_ALERT_FATAL: libc::c_int = 2; + const SSL_AD_INTERNAL_ERROR: libc::c_int = 80; + const TLSEXT_NAMETYPE_host_name: libc::c_int = 0; + + if arg.is_null() { + return SSL_TLSEXT_ERR_OK; + } + + unsafe { + let ctx = &*(arg as *const PySslContext); + + // Get the callback + let callback_opt = ctx.sni_callback.lock().clone(); + let Some(callback) = callback_opt else { + return SSL_TLSEXT_ERR_OK; + }; + + // Get callback data from SSL ex_data + let idx = get_sni_ex_data_index(); + let data_ptr = sys::SSL_get_ex_data(ssl_ptr, idx); + if data_ptr.is_null() { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + let callback_data = &*(data_ptr as *const SniCallbackData); + + // SAFETY: vm_ptr is stored during wrap_socket and is valid for the lifetime + // of the SSL connection. The handshake happens synchronously in the same thread. + let vm = &*callback_data.vm_ptr; + + // Get server name + let servername = sys::SSL_get_servername(ssl_ptr, TLSEXT_NAMETYPE_host_name); + let server_name_arg = if servername.is_null() { + vm.ctx.none() + } else { + let name_cstr = std::ffi::CStr::from_ptr(servername); + match name_cstr.to_str() { + Ok(name_str) => vm.ctx.new_str(name_str).into(), + Err(_) => vm.ctx.none(), + } + }; + + // Get SSL socket from SSL ex_data (stored as PySslSocket pointer) + let ssl_socket_ptr = sys::SSL_get_ex_data(ssl_ptr, 0); // Index 0 for SSL socket + let ssl_socket_obj = if !ssl_socket_ptr.is_null() { + let ssl_socket = &*(ssl_socket_ptr as *const PySslSocket); + // Try to get owner first + ssl_socket + .owner + .read() + .as_ref() + .and_then(|weak| weak.upgrade()) + .unwrap_or_else(|| vm.ctx.none()) + } else { + vm.ctx.none() + }; + + // Call the Python callback + match callback.call( + ( + ssl_socket_obj, + server_name_arg, + callback_data.ssl_context.to_owned(), + ), + vm, + ) { + Ok(result) => { + // Check return value type (must be None or integer) + if vm.is_none(&result) { + // None is OK + SSL_TLSEXT_ERR_OK + } else { + // Try to convert to integer + match result.try_to_value::(vm) { + Ok(alert_code) => { + // Valid integer - use as alert code + *al = alert_code; + SSL_TLSEXT_ERR_ALERT_FATAL + } + Err(_) => { + // Type conversion failed - raise TypeError + let type_error = vm.new_type_error(format!( + "servername callback must return None or an integer, not '{}'", + result.class().name() + )); + vm.run_unraisable(type_error, None, result); + *al = SSL_AD_INTERNAL_ERROR; + SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } + } + Err(exc) => { + // Log the exception but don't propagate it + vm.run_unraisable(exc, None, vm.ctx.none()); + *al = SSL_AD_INTERNAL_ERROR; + SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } + } + + // Message callback function called by OpenSSL + // Based on CPython's _PySSL_msg_callback in Modules/_ssl/debughelpers.c + unsafe extern "C" fn _msg_callback( + write_p: libc::c_int, + version: libc::c_int, + content_type: libc::c_int, + buf: *const libc::c_void, + len: usize, + ssl_ptr: *mut sys::SSL, + _arg: *mut libc::c_void, + ) { + if ssl_ptr.is_null() { + return; + } + + unsafe { + // Get SSL socket from SSL_get_app_data (index 0) + let ssl_socket_ptr = sys::SSL_get_ex_data(ssl_ptr, 0); + if ssl_socket_ptr.is_null() { + return; + } + + let ssl_socket = &*(ssl_socket_ptr as *const PySslSocket); + + // Get the callback from the context + let callback_opt = ssl_socket.ctx.read().msg_callback.lock().clone(); + let Some(callback) = callback_opt else { + return; + }; + + // Get callback data from SSL ex_data (for VM) + let idx = get_sni_ex_data_index(); + let data_ptr = sys::SSL_get_ex_data(ssl_ptr, idx); + if data_ptr.is_null() { + return; + } + + let callback_data = &*(data_ptr as *const SniCallbackData); + let vm = &*callback_data.vm_ptr; + + // Get SSL socket owner object + let ssl_socket_obj = ssl_socket + .owner + .read() + .as_ref() + .and_then(|weak| weak.upgrade()) + .unwrap_or_else(|| vm.ctx.none()); + + // Create the message bytes + let buf_slice = std::slice::from_raw_parts(buf as *const u8, len); + let msg_bytes = vm.ctx.new_bytes(buf_slice.to_vec()); + + // Determine direction string + let direction_str = if write_p != 0 { "write" } else { "read" }; + + // Call the Python callback + // Signature: callback(conn, direction, version, content_type, msg_type, data) + // For simplicity, we'll pass msg_type as 0 (would need more parsing to get the actual type) + match callback.call( + ( + ssl_socket_obj, + vm.ctx.new_str(direction_str), + vm.ctx.new_int(version), + vm.ctx.new_int(content_type), + vm.ctx.new_int(0), // msg_type - would need parsing + msg_bytes, + ), + vm, + ) { + Ok(_) => {} + Err(exc) => { + // Log the exception but don't propagate it + vm.run_unraisable(exc, None, vm.ctx.none()); + } + } + } + } + + #[pyfunction(name = "RAND_pseudo_bytes")] + fn rand_pseudo_bytes(n: i32, vm: &VirtualMachine) -> PyResult<(Vec, bool)> { + if n < 0 { + return Err(vm.new_value_error("num must be positive")); + } + let mut buf = vec![0; n as usize]; + let ret = unsafe { sys::RAND_bytes(buf.as_mut_ptr(), n) }; + match ret { + 0 | 1 => Ok((buf, ret == 1)), + _ => Err(convert_openssl_error(vm, ErrorStack::get())), + } + } + + #[pyattr] + #[pyclass(module = "ssl", name = "_SSLContext")] + #[derive(PyPayload)] + struct PySslContext { + ctx: PyRwLock, + check_hostname: AtomicCell, + protocol: SslVersion, + post_handshake_auth: PyMutex, + sni_callback: PyMutex>, + msg_callback: PyMutex>, + } + + impl fmt::Debug for PySslContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("_SSLContext") + } + } + + fn builder_as_ctx(x: &SslContextBuilder) -> &ssl::SslContextRef { + unsafe { ssl::SslContextRef::from_ptr(x.as_ptr()) } + } + + impl Constructor for PySslContext { + type Args = i32; + + fn py_new(cls: PyTypeRef, proto_version: Self::Args, vm: &VirtualMachine) -> PyResult { + let proto = SslVersion::try_from(proto_version) + .map_err(|_| vm.new_value_error("invalid protocol version"))?; + let method = match proto { + // SslVersion::Ssl3 => unsafe { ssl::SslMethod::from_ptr(sys::SSLv3_method()) }, + SslVersion::Tls => ssl::SslMethod::tls(), + SslVersion::Tls1 => ssl::SslMethod::tls(), + SslVersion::Tls1_1 => ssl::SslMethod::tls(), + SslVersion::Tls1_2 => ssl::SslMethod::tls(), + SslVersion::TlsClient => ssl::SslMethod::tls_client(), + SslVersion::TlsServer => ssl::SslMethod::tls_server(), + _ => return Err(vm.new_value_error("invalid protocol version")), + }; + let mut builder = + SslContextBuilder::new(method).map_err(|e| convert_openssl_error(vm, e))?; + + #[cfg(target_os = "android")] + android::load_client_ca_list(vm, &mut builder)?; + + let check_hostname = proto == SslVersion::TlsClient; + builder.set_verify(if check_hostname { + SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT + } else { + SslVerifyMode::NONE + }); + + let mut options = SslOptions::ALL & !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; + if proto != SslVersion::Ssl2 { + options |= SslOptions::NO_SSLV2; + } + if proto != SslVersion::Ssl3 { + options |= SslOptions::NO_SSLV3; + } + options |= SslOptions::NO_COMPRESSION; + options |= SslOptions::CIPHER_SERVER_PREFERENCE; + options |= SslOptions::SINGLE_DH_USE; + options |= SslOptions::SINGLE_ECDH_USE; + options |= SslOptions::ENABLE_MIDDLEBOX_COMPAT; + builder.set_options(options); + + let mode = ssl::SslMode::ACCEPT_MOVING_WRITE_BUFFER | ssl::SslMode::AUTO_RETRY; + builder.set_mode(mode); + + #[cfg(ossl111)] + unsafe { + sys::SSL_CTX_set_post_handshake_auth(builder.as_ptr(), 0); + } + + // Note: Unlike some other implementations, we do NOT set session_id_context at the + // context level. CPython sets it only on individual SSL objects (server-side only). + // This matches CPython's behavior in _ssl.c where SSL_set_session_id_context is called + // in newPySSLSocket() at line 862, not during context creation. + + // Set protocol version limits based on the protocol version + unsafe { + let ctx_ptr = builder.as_ptr(); + match proto { + SslVersion::Tls1 => { + sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_VERSION); + sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_VERSION); + } + SslVersion::Tls1_1 => { + sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_1_VERSION); + sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_1_VERSION); + } + SslVersion::Tls1_2 => { + sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_2_VERSION); + sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_2_VERSION); + } + _ => { + // For Tls, TlsClient, TlsServer, use default (no restrictions) + } + } + } + + // Set default verify flags: VERIFY_X509_TRUSTED_FIRST + unsafe { + let ctx_ptr = builder.as_ptr(); + let param = sys::SSL_CTX_get0_param(ctx_ptr); + sys::X509_VERIFY_PARAM_set_flags(param, sys::X509_V_FLAG_TRUSTED_FIRST); + } + + PySslContext { + ctx: PyRwLock::new(builder), + check_hostname: AtomicCell::new(check_hostname), + protocol: proto, + post_handshake_auth: PyMutex::new(false), + sni_callback: PyMutex::new(None), + msg_callback: PyMutex::new(None), + } + .into_ref_with_type(vm, cls) + .map(Into::into) + } + } + + #[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor))] + impl PySslContext { + fn builder(&self) -> PyRwLockWriteGuard<'_, SslContextBuilder> { + self.ctx.write() + } + fn ctx(&self) -> PyMappedRwLockReadGuard<'_, ssl::SslContextRef> { + PyRwLockReadGuard::map(self.ctx.read(), builder_as_ctx) + } + + #[pygetset] + fn post_handshake_auth(&self) -> bool { + *self.post_handshake_auth.lock() + } + #[pygetset(setter)] + fn set_post_handshake_auth( + &self, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + let value = value.ok_or_else(|| vm.new_attribute_error("cannot delete attribute"))?; + *self.post_handshake_auth.lock() = value.is_true(vm)?; + Ok(()) + } + + #[cfg(ossl110)] + #[pygetset] + fn security_level(&self) -> i32 { + unsafe { SSL_CTX_get_security_level(self.ctx().as_ptr()) } + } + + #[pymethod] + fn set_ciphers(&self, cipherlist: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + let ciphers = cipherlist.as_str(); + if ciphers.contains('\0') { + return Err(exceptions::cstring_error(vm)); + } + self.builder().set_cipher_list(ciphers).map_err(|_| { + vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "No cipher can be selected.".to_owned(), + ) + }) + } + + #[pymethod] + fn get_ciphers(&self, vm: &VirtualMachine) -> PyResult { + let ctx = self.ctx(); + let ssl = ssl::Ssl::new(&ctx).map_err(|e| convert_openssl_error(vm, e))?; + + unsafe { + let ciphers_ptr = SSL_get_ciphers(ssl.as_ptr()); + if ciphers_ptr.is_null() { + return Ok(vm.ctx.new_list(vec![])); + } + + let num_ciphers = sys::OPENSSL_sk_num(ciphers_ptr as *const _); + let mut result = Vec::new(); + + for i in 0..num_ciphers { + let cipher_ptr = + sys::OPENSSL_sk_value(ciphers_ptr as *const _, i) as *const sys::SSL_CIPHER; + let cipher = ssl::SslCipherRef::from_ptr(cipher_ptr as *mut _); + + let (name, version, bits) = cipher_to_tuple(cipher); + let dict = vm.ctx.new_dict(); + dict.set_item("name", vm.ctx.new_str(name).into(), vm)?; + dict.set_item("protocol", vm.ctx.new_str(version).into(), vm)?; + dict.set_item("secret_bits", vm.ctx.new_int(bits).into(), vm)?; + + // Add description field + let description = cipher_description(cipher_ptr); + dict.set_item("description", vm.ctx.new_str(description).into(), vm)?; + + result.push(dict.into()); + } + + Ok(vm.ctx.new_list(result)) + } + } + + #[pymethod] + fn set_ecdh_curve( + &self, + name: Either, + vm: &VirtualMachine, + ) -> PyResult<()> { + use openssl::ec::{EcGroup, EcKey}; + + // Convert name to CString, supporting both str and bytes + let name_cstr = match name { + Either::A(s) => { + if s.as_str().contains('\0') { + return Err(exceptions::cstring_error(vm)); + } + s.to_cstring(vm)? + } + Either::B(b) => std::ffi::CString::new(b.borrow_buf().to_vec()) + .map_err(|_| exceptions::cstring_error(vm))?, + }; + + // Find the NID for the curve name using OBJ_sn2nid + let nid_raw = unsafe { sys::OBJ_sn2nid(name_cstr.as_ptr()) }; + if nid_raw == 0 { + return Err(vm.new_value_error("unknown curve name")); + } + let nid = Nid::from_raw(nid_raw); + + // Create EC key from the curve + let group = EcGroup::from_curve_name(nid).map_err(|e| convert_openssl_error(vm, e))?; + let key = EcKey::from_group(&group).map_err(|e| convert_openssl_error(vm, e))?; + + // Set the temporary ECDH key + self.builder() + .set_tmp_ecdh(&key) + .map_err(|e| convert_openssl_error(vm, e)) + } + + #[pygetset] + fn options(&self) -> libc::c_ulong { + self.ctx.read().options().bits() as _ + } + #[pygetset(setter)] + fn set_options(&self, opts: libc::c_ulong) { + self.builder() + .set_options(SslOptions::from_bits_truncate(opts as _)); + } + #[pygetset] + fn protocol(&self) -> i32 { + self.protocol as i32 + } + #[pygetset] + fn verify_mode(&self) -> i32 { + let mode = self.ctx().verify_mode(); + if mode == SslVerifyMode::NONE { + CertRequirements::None.into() + } else if mode == SslVerifyMode::PEER { + CertRequirements::Optional.into() + } else if mode == SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT { + CertRequirements::Required.into() + } else { + unreachable!() + } + } + #[pygetset(setter)] + fn set_verify_mode(&self, cert: i32, vm: &VirtualMachine) -> PyResult<()> { + let mut ctx = self.builder(); + let cert_req = CertRequirements::try_from(cert) + .map_err(|_| vm.new_value_error("invalid value for verify_mode"))?; + let mode = match cert_req { + CertRequirements::None if self.check_hostname.load() => { + return Err(vm.new_value_error( + "Cannot set verify_mode to CERT_NONE when check_hostname is enabled.", + )); + } + CertRequirements::None => SslVerifyMode::NONE, + CertRequirements::Optional => SslVerifyMode::PEER, + CertRequirements::Required => { + SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT + } + }; + ctx.set_verify(mode); + Ok(()) + } + #[pygetset] + fn verify_flags(&self) -> libc::c_ulong { + unsafe { + let ctx_ptr = self.ctx().as_ptr(); + let param = sys::SSL_CTX_get0_param(ctx_ptr); + sys::X509_VERIFY_PARAM_get_flags(param) + } + } + #[pygetset(setter)] + fn set_verify_flags(&self, new_flags: libc::c_ulong, vm: &VirtualMachine) -> PyResult<()> { + unsafe { + let ctx_ptr = self.ctx().as_ptr(); + let param = sys::SSL_CTX_get0_param(ctx_ptr); + let flags = sys::X509_VERIFY_PARAM_get_flags(param); + let clear = flags & !new_flags; + let set = !flags & new_flags; + + if clear != 0 && sys::X509_VERIFY_PARAM_clear_flags(param, clear) == 0 { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "Failed to clear verify flags".to_owned(), + )); + } + if set != 0 && sys::X509_VERIFY_PARAM_set_flags(param, set) == 0 { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "Failed to set verify flags".to_owned(), + )); + } + Ok(()) + } + } + #[pygetset] + fn check_hostname(&self) -> bool { + self.check_hostname.load() + } + #[pygetset(setter)] + fn set_check_hostname(&self, ch: bool) { + let mut ctx = self.builder(); + if ch && builder_as_ctx(&ctx).verify_mode() == SslVerifyMode::NONE { + ctx.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT); + } + self.check_hostname.store(ch); + } + + // PY_PROTO_MINIMUM_SUPPORTED = -2, PY_PROTO_MAXIMUM_SUPPORTED = -1 + #[pygetset] + fn minimum_version(&self) -> i32 { + let ctx = self.ctx(); + let version = unsafe { sys::SSL_CTX_get_min_proto_version(ctx.as_ptr()) }; + if version == 0 { + -2 // PY_PROTO_MINIMUM_SUPPORTED + } else { + version + } + } + #[pygetset(setter)] + fn set_minimum_version(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { + // Handle special values + let proto_version = match value { + -2 => { + // PY_PROTO_MINIMUM_SUPPORTED -> use minimum available (TLS 1.2) + sys::TLS1_2_VERSION + } + -1 => { + // PY_PROTO_MAXIMUM_SUPPORTED -> use maximum available + // For max on min_proto_version, we use the newest available + sys::TLS1_3_VERSION + } + _ => value, + }; + + let ctx = self.builder(); + let result = unsafe { sys::SSL_CTX_set_min_proto_version(ctx.as_ptr(), proto_version) }; + if result == 0 { + return Err(vm.new_value_error("invalid protocol version")); + } + Ok(()) + } + + #[pygetset] + fn maximum_version(&self) -> i32 { + let ctx = self.ctx(); + let version = unsafe { sys::SSL_CTX_get_max_proto_version(ctx.as_ptr()) }; + if version == 0 { + -1 // PY_PROTO_MAXIMUM_SUPPORTED + } else { + version + } + } + #[pygetset(setter)] + fn set_maximum_version(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { + // Handle special values + let proto_version = match value { + -1 => { + // PY_PROTO_MAXIMUM_SUPPORTED -> use 0 for OpenSSL (means no limit) + 0 + } + -2 => { + // PY_PROTO_MINIMUM_SUPPORTED -> use minimum available (TLS 1.2) + sys::TLS1_2_VERSION + } + _ => value, + }; + + let ctx = self.builder(); + let result = unsafe { sys::SSL_CTX_set_max_proto_version(ctx.as_ptr(), proto_version) }; + if result == 0 { + return Err(vm.new_value_error("invalid protocol version")); + } + Ok(()) + } + + #[pygetset] + fn num_tickets(&self, _vm: &VirtualMachine) -> PyResult { + // Only supported for TLS 1.3 + #[cfg(ossl110)] + { + let ctx = self.ctx(); + let num = unsafe { sys::SSL_CTX_get_num_tickets(ctx.as_ptr()) }; + Ok(num) + } + #[cfg(not(ossl110))] + { + Ok(0) + } + } + #[pygetset(setter)] + fn set_num_tickets(&self, value: isize, vm: &VirtualMachine) -> PyResult<()> { + // Check for negative values + if value < 0 { + return Err( + vm.new_value_error("num_tickets must be a non-negative integer".to_owned()) + ); + } + + // Check that this is a server context + if self.protocol != SslVersion::TlsServer { + return Err(vm.new_value_error("SSLContext is not a server context.".to_owned())); + } + + #[cfg(ossl110)] + { + let ctx = self.builder(); + let result = unsafe { sys::SSL_CTX_set_num_tickets(ctx.as_ptr(), value as usize) }; + if result != 1 { + return Err(vm.new_value_error("failed to set num tickets.")); + } + Ok(()) + } + #[cfg(not(ossl110))] + { + let _ = (value, vm); + Ok(()) + } + } + + #[pymethod] + fn set_default_verify_paths(&self, vm: &VirtualMachine) -> PyResult<()> { + cfg_if::cfg_if! { + if #[cfg(openssl_vendored)] { + let (cert_file, cert_dir) = get_cert_file_dir(); + self.builder() + .load_verify_locations(Some(cert_file), Some(cert_dir)) + .map_err(|e| convert_openssl_error(vm, e)) + } else { + self.builder() + .set_default_verify_paths() + .map_err(|e| convert_openssl_error(vm, e)) + } + } + } + + #[pymethod] + fn _set_alpn_protocols(&self, protos: ArgBytesLike, vm: &VirtualMachine) -> PyResult<()> { + #[cfg(ossl102)] + { + let mut ctx = self.builder(); + let server = protos.with_ref(|pbuf| { + if pbuf.len() > libc::c_uint::MAX as usize { + return Err(vm.new_overflow_error(format!( + "protocols longer than {} bytes", + libc::c_uint::MAX + ))); + } + ctx.set_alpn_protos(pbuf) + .map_err(|e| convert_openssl_error(vm, e))?; + Ok(pbuf.to_vec()) + })?; + ctx.set_alpn_select_callback(move |_, client| { + let proto = + ssl::select_next_proto(&server, client).ok_or(ssl::AlpnError::NOACK)?; + let pos = memchr::memmem::find(client, proto) + .expect("selected alpn proto should be present in client protos"); + Ok(&client[pos..proto.len()]) + }); + Ok(()) + } + #[cfg(not(ossl102))] + { + Err(vm.new_not_implemented_error( + "The NPN extension requires OpenSSL 1.0.1 or later.", + )) + } + } + + #[pymethod] + fn load_verify_locations( + &self, + args: LoadVerifyLocationsArgs, + vm: &VirtualMachine, + ) -> PyResult<()> { + if let (None, None, None) = (&args.cafile, &args.capath, &args.cadata) { + return Err(vm.new_type_error("cafile, capath and cadata cannot be all omitted")); + } + + #[cold] + fn invalid_cadata(vm: &VirtualMachine) -> PyBaseExceptionRef { + vm.new_type_error("cadata should be an ASCII string or a bytes-like object") + } + + let mut ctx = self.builder(); + + // validate cadata type and load cadata + if let Some(cadata) = args.cadata { + let certs = match cadata { + Either::A(s) => { + if !s.is_ascii() { + return Err(invalid_cadata(vm)); + } + X509::stack_from_pem(s.as_bytes()) + } + Either::B(b) => b.with_ref(x509_stack_from_der), + }; + let certs = certs.map_err(|e| convert_openssl_error(vm, e))?; + let store = ctx.cert_store_mut(); + for cert in certs { + store + .add_cert(cert) + .map_err(|e| convert_openssl_error(vm, e))?; + } + } + + if args.cafile.is_some() || args.capath.is_some() { + let cafile_path = args.cafile.map(|p| p.to_path_buf(vm)).transpose()?; + let capath_path = args.capath.map(|p| p.to_path_buf(vm)).transpose()?; + ctx.load_verify_locations(cafile_path.as_deref(), capath_path.as_deref()) + .map_err(|e| convert_openssl_error(vm, e))?; + } + + Ok(()) + } + + #[pymethod] + fn get_ca_certs( + &self, + binary_form: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let binary_form = binary_form.unwrap_or(false); + let ctx = self.ctx(); + #[cfg(ossl300)] + let certs = ctx.cert_store().all_certificates(); + #[cfg(not(ossl300))] + let certs = ctx.cert_store().objects().iter().filter_map(|x| x.x509()); + + // Filter to only include CA certificates (Basic Constraints: CA=TRUE) + let certs = certs + .into_iter() + .filter(|cert| { + unsafe { + // X509_check_ca() returns 1 for CA certificates + X509_check_ca(cert.as_ptr()) == 1 + } + }) + .map(|ref cert| cert_to_py(vm, cert, binary_form)) + .collect::, _>>()?; + Ok(certs) + } + + #[pymethod] + fn cert_store_stats(&self, vm: &VirtualMachine) -> PyResult { + let ctx = self.ctx(); + let store_ptr = unsafe { sys::SSL_CTX_get_cert_store(ctx.as_ptr()) }; + + if store_ptr.is_null() { + return Err(vm.new_memory_error("failed to get cert store".to_owned())); + } + + let objs_ptr = unsafe { sys::X509_STORE_get0_objects(store_ptr) }; + if objs_ptr.is_null() { + return Err(vm.new_memory_error("failed to query cert store".to_owned())); + } + + let mut x509_count = 0; + let mut crl_count = 0; + let mut ca_count = 0; + + unsafe { + let num_objs = sys::OPENSSL_sk_num(objs_ptr as *const _); + for i in 0..num_objs { + let obj_ptr = + sys::OPENSSL_sk_value(objs_ptr as *const _, i) as *const sys::X509_OBJECT; + let obj_type = X509_OBJECT_get_type(obj_ptr); + + match obj_type { + X509_LU_X509 => { + x509_count += 1; + let x509_ptr = sys::X509_OBJECT_get0_X509(obj_ptr); + if !x509_ptr.is_null() && X509_check_ca(x509_ptr) == 1 { + ca_count += 1; + } + } + X509_LU_CRL => { + crl_count += 1; + } + _ => { + // Ignore unrecognized types + } + } + } + // Note: No need to free objs_ptr as X509_STORE_get0_objects returns + // a pointer to internal data that should not be freed by the caller + } + + let dict = vm.ctx.new_dict(); + dict.set_item("x509", vm.ctx.new_int(x509_count).into(), vm)?; + dict.set_item("crl", vm.ctx.new_int(crl_count).into(), vm)?; + dict.set_item("x509_ca", vm.ctx.new_int(ca_count).into(), vm)?; + Ok(dict.into()) + } + + #[pymethod] + fn session_stats(&self, vm: &VirtualMachine) -> PyResult { + let ctx = self.ctx(); + let ctx_ptr = ctx.as_ptr(); + + let dict = vm.ctx.new_dict(); + + macro_rules! add_stat { + ($key:expr, $func:ident) => { + let value = unsafe { $func(ctx_ptr) }; + dict.set_item($key, vm.ctx.new_int(value).into(), vm)?; + }; + } + + add_stat!("number", SSL_CTX_sess_number); + add_stat!("connect", SSL_CTX_sess_connect); + add_stat!("connect_good", SSL_CTX_sess_connect_good); + add_stat!("connect_renegotiate", SSL_CTX_sess_connect_renegotiate); + add_stat!("accept", SSL_CTX_sess_accept); + add_stat!("accept_good", SSL_CTX_sess_accept_good); + add_stat!("accept_renegotiate", SSL_CTX_sess_accept_renegotiate); + add_stat!("hits", SSL_CTX_sess_hits); + add_stat!("misses", SSL_CTX_sess_misses); + add_stat!("timeouts", SSL_CTX_sess_timeouts); + add_stat!("cache_full", SSL_CTX_sess_cache_full); + + Ok(dict.into()) + } + + #[pymethod] + fn load_dh_params(&self, filepath: FsPath, vm: &VirtualMachine) -> PyResult<()> { + let path = filepath.to_path_buf(vm)?; + + // Open the file using fopen (cross-platform) + let fp = + rustpython_common::fileutils::fopen(path.as_path(), "rb").map_err(|e| { + match e.kind() { + std::io::ErrorKind::NotFound => vm.new_exception_msg( + vm.ctx.exceptions.file_not_found_error.to_owned(), + e.to_string(), + ), + _ => vm.new_os_error(e.to_string()), + } + })?; + + // Read DH parameters + let dh = unsafe { + PEM_read_DHparams( + fp, + std::ptr::null_mut(), + std::ptr::null_mut(), + std::ptr::null_mut(), + ) + }; + unsafe { + libc::fclose(fp); + } + + if dh.is_null() { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + + // Set temporary DH parameters + let ctx = self.builder(); + let result = unsafe { sys::SSL_CTX_set_tmp_dh(ctx.as_ptr(), dh) }; + unsafe { + sys::DH_free(dh); + } + + if result != 1 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + + Ok(()) + } + + #[pygetset] + fn sni_callback(&self) -> Option { + self.sni_callback.lock().clone() + } + + #[pygetset(setter)] + fn set_sni_callback( + &self, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + // Check if this is a server context + if self.protocol == SslVersion::TlsClient { + return Err(vm.new_value_error( + "sni_callback cannot be set on TLS_CLIENT context".to_owned(), + )); + } + + let mut callback_guard = self.sni_callback.lock(); + + if let Some(callback_obj) = value { + if !vm.is_none(&callback_obj) { + // Check if callable + if !callback_obj.is_callable() { + return Err(vm.new_type_error("not a callable object".to_owned())); + } + + // Set the callback + *callback_guard = Some(callback_obj); + + // Set OpenSSL callback + unsafe { + sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( + self.ctx().as_ptr(), + Some(_servername_callback), + ); + sys::SSL_CTX_set_tlsext_servername_arg( + self.ctx().as_ptr(), + self as *const _ as *mut _, + ); + } + } else { + // Clear callback + *callback_guard = None; + unsafe { + sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( + self.ctx().as_ptr(), + None, + ); + } + } + } else { + // Clear callback + *callback_guard = None; + unsafe { + sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( + self.ctx().as_ptr(), + None, + ); + } + } + + Ok(()) + } + + #[pymethod] + fn set_servername_callback( + &self, + callback: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + self.set_sni_callback(callback, vm) + } + + #[pygetset(name = "_msg_callback")] + fn msg_callback(&self) -> Option { + self.msg_callback.lock().clone() + } + + #[pygetset(setter, name = "_msg_callback")] + fn set_msg_callback( + &self, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + let mut callback_guard = self.msg_callback.lock(); + + if let Some(callback_obj) = value { + if !vm.is_none(&callback_obj) { + // Check if callable + if !callback_obj.is_callable() { + return Err(vm.new_type_error("not a callable object".to_owned())); + } + + // Set the callback + *callback_guard = Some(callback_obj); + + // Set OpenSSL callback + unsafe { + SSL_CTX_set_msg_callback(self.ctx().as_ptr(), Some(_msg_callback)); + } + } else { + // Clear callback + *callback_guard = None; + unsafe { + SSL_CTX_set_msg_callback(self.ctx().as_ptr(), None); + } + } + } else { + // Clear callback when value is None + *callback_guard = None; + unsafe { + SSL_CTX_set_msg_callback(self.ctx().as_ptr(), None); + } + } + + Ok(()) + } + + #[pymethod] + fn load_cert_chain(&self, args: LoadCertChainArgs, vm: &VirtualMachine) -> PyResult<()> { + let LoadCertChainArgs { + certfile, + keyfile, + password, + } = args; + // TODO: requires passing a callback to C + if password.is_some() { + return Err(vm.new_not_implemented_error("password arg not yet supported")); + } + let mut ctx = self.builder(); + let key_path = keyfile.map(|path| path.to_path_buf(vm)).transpose()?; + let cert_path = certfile.to_path_buf(vm)?; + ctx.set_certificate_chain_file(&cert_path) + .and_then(|()| { + ctx.set_private_key_file( + key_path.as_ref().unwrap_or(&cert_path), + ssl::SslFiletype::PEM, + ) + }) + .and_then(|()| ctx.check_private_key()) + .map_err(|e| convert_openssl_error(vm, e)) + } + + // Helper function to create SSL socket + // = CPython's newPySSLSocket() + fn new_py_ssl_socket( + ctx_ref: PyRef, + server_side: bool, + server_hostname: Option, + vm: &VirtualMachine, + ) -> PyResult<(ssl::Ssl, SslServerOrClient, Option)> { + // Validate socket type and context protocol + if server_side && ctx_ref.protocol == SslVersion::TlsClient { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "Cannot create a server socket with a PROTOCOL_TLS_CLIENT context".to_owned(), + )); + } + if !server_side && ctx_ref.protocol == SslVersion::TlsServer { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "Cannot create a client socket with a PROTOCOL_TLS_SERVER context".to_owned(), + )); + } + + // Create SSL object + let mut ssl = + ssl::Ssl::new(&ctx_ref.ctx()).map_err(|e| convert_openssl_error(vm, e))?; + + // Set session id context for server-side sockets + let socket_type = if server_side { + unsafe { + const SID_CTX: &[u8] = b"Python"; + let ret = SSL_set_session_id_context( + ssl.as_ptr(), + SID_CTX.as_ptr(), + SID_CTX.len() as libc::c_uint, + ); + if ret == 0 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + } + SslServerOrClient::Server + } else { + SslServerOrClient::Client + }; + + // Configure server hostname + if let Some(hostname) = &server_hostname { + let hostname_str = hostname.as_str(); + if hostname_str.is_empty() || hostname_str.starts_with('.') { + return Err(vm.new_value_error( + "server_hostname cannot be an empty string or start with a leading dot.", + )); + } + if hostname_str.contains('\0') { + return Err(vm.new_value_error("embedded null byte in server_hostname")); + } + let ip = hostname_str.parse::(); + if ip.is_err() { + ssl.set_hostname(hostname_str) + .map_err(|e| convert_openssl_error(vm, e))?; + } + if ctx_ref.check_hostname.load() { + if let Ok(ip) = ip { + ssl.param_mut() + .set_ip(ip) + .map_err(|e| convert_openssl_error(vm, e))?; + } else { + ssl.param_mut() + .set_host(hostname_str) + .map_err(|e| convert_openssl_error(vm, e))?; + } + } + } + + // Configure post-handshake authentication + #[cfg(ossl111)] + if *ctx_ref.post_handshake_auth.lock() { + unsafe { + if server_side { + // Server socket: add SSL_VERIFY_POST_HANDSHAKE flag + // Only in combination with SSL_VERIFY_PEER + let mode = sys::SSL_get_verify_mode(ssl.as_ptr()); + if (mode & sys::SSL_VERIFY_PEER as libc::c_int) != 0 { + sys::SSL_set_verify( + ssl.as_ptr(), + mode | SSL_VERIFY_POST_HANDSHAKE, + None, + ); + } + } else { + // Client socket: call SSL_set_post_handshake_auth + SSL_set_post_handshake_auth(ssl.as_ptr(), 1); + } + } + } + + // Set connect/accept state + if server_side { + ssl.set_accept_state(); + } else { + ssl.set_connect_state(); + } + + Ok((ssl, socket_type, server_hostname)) + } + + #[pymethod] + fn _wrap_socket( + zelf: PyRef, + args: WrapSocketArgs, + vm: &VirtualMachine, + ) -> PyResult { + // Use common helper function + let (ssl, socket_type, server_hostname) = + Self::new_py_ssl_socket(zelf.clone(), args.server_side, args.server_hostname, vm)?; + + // Create SslStream with socket + let stream = ssl::SslStream::new(ssl, SocketStream(args.sock.clone())) + .map_err(|e| convert_openssl_error(vm, e))?; + + let py_ssl_socket = PySslSocket { + ctx: PyRwLock::new(zelf.clone()), + connection: PyRwLock::new(SslConnection::Socket(stream)), + socket_type, + server_hostname, + owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?), + }; + + // Convert to PyRef (heap allocation) to avoid use-after-free + let py_ref = + py_ssl_socket.into_ref_with_type(vm, PySslSocket::class(&vm.ctx).to_owned())?; + + // Set SNI callback data if callback is configured + if zelf.sni_callback.lock().is_some() { + unsafe { + let ssl_ptr = py_ref.connection.read().ssl().as_ptr(); + + // Store callback data in SSL ex_data + let callback_data = Box::new(SniCallbackData { + ssl_context: zelf.clone(), + vm_ptr: vm as *const _, + }); + let idx = get_sni_ex_data_index(); + sys::SSL_set_ex_data(ssl_ptr, idx, Box::into_raw(callback_data) as *mut _); + + // Store PyRef pointer (heap-allocated) in ex_data index 0 + sys::SSL_set_ex_data(ssl_ptr, 0, &*py_ref as *const _ as *mut _); + } + } + + // Set session if provided + if let Some(session) = args.session + && !vm.is_none(&session) + { + py_ref.set_session(session, vm)?; + } + + Ok(py_ref.into()) + } + + #[pymethod] + fn _wrap_bio( + zelf: PyRef, + args: WrapBioArgs, + vm: &VirtualMachine, + ) -> PyResult { + // Use common helper function + let (ssl, socket_type, server_hostname) = + Self::new_py_ssl_socket(zelf.clone(), args.server_side, args.server_hostname, vm)?; + + // Create BioStream wrapper + let bio_stream = BioStream { + inbio: args.incoming, + outbio: args.outgoing, + }; + + // Create SslStream with BioStream + let stream = + ssl::SslStream::new(ssl, bio_stream).map_err(|e| convert_openssl_error(vm, e))?; + + let py_ssl_socket = PySslSocket { + ctx: PyRwLock::new(zelf.clone()), + connection: PyRwLock::new(SslConnection::Bio(stream)), + socket_type, + server_hostname, + owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?), + }; + + // Convert to PyRef (heap allocation) to avoid use-after-free + let py_ref = + py_ssl_socket.into_ref_with_type(vm, PySslSocket::class(&vm.ctx).to_owned())?; + + // Set SNI callback data if callback is configured + if zelf.sni_callback.lock().is_some() { + unsafe { + let ssl_ptr = py_ref.connection.read().ssl().as_ptr(); + + // Store callback data in SSL ex_data + let callback_data = Box::new(SniCallbackData { + ssl_context: zelf.clone(), + vm_ptr: vm as *const _, + }); + let idx = get_sni_ex_data_index(); + sys::SSL_set_ex_data(ssl_ptr, idx, Box::into_raw(callback_data) as *mut _); + + // Store PyRef pointer (heap-allocated) in ex_data index 0 + sys::SSL_set_ex_data(ssl_ptr, 0, &*py_ref as *const _ as *mut _); + } + } + + // Set session if provided + if let Some(session) = args.session + && !vm.is_none(&session) + { + py_ref.set_session(session, vm)?; + } + + Ok(py_ref.into()) + } + } + + #[derive(FromArgs)] + #[allow(dead_code)] // Fields will be used when _wrap_bio is fully implemented + struct WrapBioArgs { + incoming: PyRef, + outgoing: PyRef, + server_side: bool, + #[pyarg(any, default)] + server_hostname: Option, + #[pyarg(named, default)] + owner: Option, + #[pyarg(named, default)] + session: Option, + } + + #[derive(FromArgs)] + struct WrapSocketArgs { + sock: PyRef, + server_side: bool, + #[pyarg(any, default)] + server_hostname: Option, + #[pyarg(named, default)] + owner: Option, + #[pyarg(named, default)] + session: Option, + } + + #[derive(FromArgs)] + struct LoadVerifyLocationsArgs { + #[pyarg(any, default)] + cafile: Option, + #[pyarg(any, default)] + capath: Option, + #[pyarg(any, default)] + cadata: Option>, + } + + #[derive(FromArgs)] + struct LoadCertChainArgs { + certfile: FsPath, + #[pyarg(any, optional)] + keyfile: Option, + #[pyarg(any, optional)] + password: Option>, + } + + // Err is true if the socket is blocking + type SocketDeadline = Result; + + enum SelectRet { + Nonblocking, + TimedOut, + IsBlocking, + Closed, + Ok, + } + + #[derive(Clone, Copy)] + enum SslNeeds { + Read, + Write, + } + + struct SocketStream(PyRef); + + impl SocketStream { + fn timeout_deadline(&self) -> SocketDeadline { + self.0.get_timeout().map(|d| Instant::now() + d) + } + + fn select(&self, needs: SslNeeds, deadline: &SocketDeadline) -> SelectRet { + let sock = match self.0.sock_opt() { + Some(s) => s, + None => return SelectRet::Closed, + }; + let deadline = match &deadline { + Ok(deadline) => match deadline.checked_duration_since(Instant::now()) { + Some(deadline) => deadline, + None => return SelectRet::TimedOut, + }, + Err(true) => return SelectRet::IsBlocking, + Err(false) => return SelectRet::Nonblocking, + }; + let res = socket::sock_select( + &sock, + match needs { + SslNeeds::Read => socket::SelectKind::Read, + SslNeeds::Write => socket::SelectKind::Write, + }, + Some(deadline), + ); + match res { + Ok(true) => SelectRet::TimedOut, + _ => SelectRet::Ok, + } + } + + fn socket_needs( + &self, + err: &ssl::Error, + deadline: &SocketDeadline, + ) -> (Option, SelectRet) { + let needs = match err.code() { + ssl::ErrorCode::WANT_READ => Some(SslNeeds::Read), + ssl::ErrorCode::WANT_WRITE => Some(SslNeeds::Write), + _ => None, + }; + let state = needs.map_or(SelectRet::Ok, |needs| self.select(needs, deadline)); + (needs, state) + } + } + + fn socket_closed_error(vm: &VirtualMachine) -> PyBaseExceptionRef { + vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "Underlying socket has been closed.".to_owned(), + ) + } + + // BIO stream wrapper to implement Read/Write traits for MemoryBIO + struct BioStream { + inbio: PyRef, + outbio: PyRef, + } + + impl Read for BioStream { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + // Read from incoming MemoryBIO + unsafe { + let nbytes = sys::BIO_read( + self.inbio.bio, + buf.as_mut_ptr() as *mut _, + buf.len().min(i32::MAX as usize) as i32, + ); + if nbytes < 0 { + // BIO_read returns -1 on error or when no data is available + // Check if it's a retry condition (WANT_READ) + Err(std::io::Error::new( + std::io::ErrorKind::WouldBlock, + "BIO has no data available", + )) + } else { + Ok(nbytes as usize) + } + } + } + } + + impl Write for BioStream { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + // Write to outgoing MemoryBIO + unsafe { + let nbytes = sys::BIO_write( + self.outbio.bio, + buf.as_ptr() as *const _, + buf.len().min(i32::MAX as usize) as i32, + ); + if nbytes < 0 { + return Err(std::io::Error::other("BIO write failed")); + } + Ok(nbytes as usize) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + // MemoryBIO doesn't need flushing + Ok(()) + } + } + + // Enum to represent different SSL connection modes + enum SslConnection { + Socket(ssl::SslStream), + Bio(ssl::SslStream), + } + + impl SslConnection { + // Get a reference to the SSL object + fn ssl(&self) -> &ssl::SslRef { + match self { + SslConnection::Socket(stream) => stream.ssl(), + SslConnection::Bio(stream) => stream.ssl(), + } + } + + // Get underlying socket stream reference (only for socket mode) + fn get_ref(&self) -> Option<&SocketStream> { + match self { + SslConnection::Socket(stream) => Some(stream.get_ref()), + SslConnection::Bio(_) => None, + } + } + + // Check if this is in BIO mode + fn is_bio(&self) -> bool { + matches!(self, SslConnection::Bio(_)) + } + + // Perform SSL handshake + fn do_handshake(&mut self) -> Result<(), ssl::Error> { + match self { + SslConnection::Socket(stream) => stream.do_handshake(), + SslConnection::Bio(stream) => stream.do_handshake(), + } + } + + // Write data to SSL connection + fn ssl_write(&mut self, buf: &[u8]) -> Result { + match self { + SslConnection::Socket(stream) => stream.ssl_write(buf), + SslConnection::Bio(stream) => stream.ssl_write(buf), + } + } + + // Read data from SSL connection + fn ssl_read(&mut self, buf: &mut [u8]) -> Result { + match self { + SslConnection::Socket(stream) => stream.ssl_read(buf), + SslConnection::Bio(stream) => stream.ssl_read(buf), + } + } + + // Get SSL shutdown state + fn get_shutdown(&mut self) -> ssl::ShutdownState { + match self { + SslConnection::Socket(stream) => stream.get_shutdown(), + SslConnection::Bio(stream) => stream.get_shutdown(), + } + } + } + + #[pyattr] + #[pyclass(module = "ssl", name = "_SSLSocket", traverse)] + #[derive(PyPayload)] + struct PySslSocket { + ctx: PyRwLock>, + #[pytraverse(skip)] + connection: PyRwLock, + #[pytraverse(skip)] + socket_type: SslServerOrClient, + server_hostname: Option, + owner: PyRwLock>>, + } + + impl fmt::Debug for PySslSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("_SSLSocket") + } + } + + #[pyclass(flags(IMMUTABLETYPE))] + impl PySslSocket { + #[pygetset] + fn owner(&self) -> Option { + self.owner.read().as_ref().and_then(|weak| weak.upgrade()) + } + #[pygetset(setter)] + fn set_owner(&self, owner: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let mut lock = self.owner.write(); + lock.take(); + *lock = Some(owner.downgrade(None, vm)?); + Ok(()) + } + #[pygetset] + fn server_side(&self) -> bool { + self.socket_type == SslServerOrClient::Server + } + #[pygetset] + fn context(&self) -> PyRef { + self.ctx.read().clone() + } + #[pygetset(setter)] + fn set_context(&self, value: PyRef, vm: &VirtualMachine) -> PyResult<()> { + // Update the SSL context in the underlying SSL object + let stream = self.connection.read(); + + // Set the new SSL_CTX on the SSL object + unsafe { + let result = SSL_set_SSL_CTX(stream.ssl().as_ptr(), value.ctx().as_ptr()); + if result.is_null() { + return Err(vm.new_runtime_error("Failed to set SSL context".to_owned())); + } + } + + // Update self.ctx to the new context + *self.ctx.write() = value; + Ok(()) + } + #[pygetset] + fn server_hostname(&self) -> Option { + self.server_hostname.clone() + } + + #[pymethod] + fn getpeercert( + &self, + binary: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let binary = binary.unwrap_or(false); + let stream = self.connection.read(); + if !stream.ssl().is_init_finished() { + return Err(vm.new_value_error("handshake not done yet")); + } + + let peer_cert = stream.ssl().peer_certificate(); + let Some(cert) = peer_cert else { + return Ok(None); + }; + + if binary { + // Return DER-encoded certificate + cert_to_py(vm, &cert, true).map(Some) + } else { + // Check verify_mode + unsafe { + let ssl_ctx = sys::SSL_get_SSL_CTX(stream.ssl().as_ptr()); + let verify_mode = sys::SSL_CTX_get_verify_mode(ssl_ctx); + if (verify_mode & sys::SSL_VERIFY_PEER as libc::c_int) == 0 { + // Return empty dict when SSL_VERIFY_PEER is not set + Ok(Some(vm.ctx.new_dict().into())) + } else { + // Return decoded certificate + cert_to_py(vm, &cert, false).map(Some) + } + } + } + } + + #[pymethod] + fn get_unverified_chain(&self, vm: &VirtualMachine) -> PyResult> { + let stream = self.connection.read(); + let Some(chain) = stream.ssl().peer_cert_chain() else { + return Ok(None); + }; + + // Return Certificate objects + let certs: Vec = chain + .iter() + .map(|cert| unsafe { + sys::X509_up_ref(cert.as_ptr()); + let owned = X509::from_ptr(cert.as_ptr()); + cert_to_certificate(vm, owned) + }) + .collect::>()?; + Ok(Some(vm.ctx.new_list(certs))) + } + + #[pymethod] + fn get_verified_chain(&self, vm: &VirtualMachine) -> PyResult> { + let stream = self.connection.read(); + unsafe { + let chain = sys::SSL_get0_verified_chain(stream.ssl().as_ptr()); + if chain.is_null() { + return Ok(None); + } + + let num_certs = sys::OPENSSL_sk_num(chain as *const _); + + let mut certs = Vec::with_capacity(num_certs as usize); + // Return Certificate objects + for i in 0..num_certs { + let cert_ptr = sys::OPENSSL_sk_value(chain as *const _, i) as *mut sys::X509; + if cert_ptr.is_null() { + continue; + } + // Clone the X509 certificate to create an owned copy + sys::X509_up_ref(cert_ptr); + let owned_cert = X509::from_ptr(cert_ptr); + let cert_obj = cert_to_certificate(vm, owned_cert)?; + certs.push(cert_obj); + } + + Ok(if certs.is_empty() { + None + } else { + Some(vm.ctx.new_list(certs)) + }) + } + } + + #[pymethod] + fn version(&self) -> Option<&'static str> { + let v = self.connection.read().ssl().version_str(); + if v == "unknown" { None } else { Some(v) } + } + + #[pymethod] + fn cipher(&self) -> Option { + self.connection + .read() + .ssl() + .current_cipher() + .map(cipher_to_tuple) + } + + #[pymethod] + fn shared_ciphers(&self, vm: &VirtualMachine) -> Option { + #[cfg(ossl110)] + { + let stream = self.connection.read(); + unsafe { + let server_ciphers = SSL_get_ciphers(stream.ssl().as_ptr()); + if server_ciphers.is_null() { + return None; + } + + let client_ciphers = SSL_get_client_ciphers(stream.ssl().as_ptr()); + if client_ciphers.is_null() { + return None; + } + + let mut result = Vec::new(); + let num_server = sys::OPENSSL_sk_num(server_ciphers as *const _); + let num_client = sys::OPENSSL_sk_num(client_ciphers as *const _); + + for i in 0..num_server { + let server_cipher_ptr = sys::OPENSSL_sk_value(server_ciphers as *const _, i) + as *const sys::SSL_CIPHER; + + // Check if client supports this cipher by comparing pointers + let mut found = false; + for j in 0..num_client { + let client_cipher_ptr = + sys::OPENSSL_sk_value(client_ciphers as *const _, j) + as *const sys::SSL_CIPHER; + + if server_cipher_ptr == client_cipher_ptr { + found = true; + break; + } + } + + if found { + let cipher = ssl::SslCipherRef::from_ptr(server_cipher_ptr as *mut _); + let (name, version, bits) = cipher_to_tuple(cipher); + let tuple = vm.new_tuple(( + vm.ctx.new_str(name), + vm.ctx.new_str(version), + vm.ctx.new_int(bits), + )); + result.push(tuple.into()); + } + } + + if result.is_empty() { + None + } else { + Some(vm.ctx.new_list(result)) + } + } + } + #[cfg(not(ossl110))] + { + let _ = vm; + None + } + } + + #[pymethod] + fn selected_alpn_protocol(&self) -> Option { + #[cfg(ossl102)] + { + let stream = self.connection.read(); + unsafe { + let mut out: *const libc::c_uchar = std::ptr::null(); + let mut outlen: libc::c_uint = 0; + + sys::SSL_get0_alpn_selected(stream.ssl().as_ptr(), &mut out, &mut outlen); + + if out.is_null() { + None + } else { + let slice = std::slice::from_raw_parts(out, outlen as usize); + Some(String::from_utf8_lossy(slice).into_owned()) + } + } + } + #[cfg(not(ossl102))] + { + None + } + } + + #[pymethod] + fn get_channel_binding( + &self, + cb_type: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + const CB_MAXLEN: usize = 512; + + let cb_type_str = cb_type.as_ref().map_or("tls-unique", |s| s.as_str()); + + if cb_type_str != "tls-unique" { + return Err(vm.new_value_error(format!( + "Unsupported channel binding type '{}'", + cb_type_str + ))); + } + + let stream = self.connection.read(); + let ssl_ptr = stream.ssl().as_ptr(); + + unsafe { + let session_reused = sys::SSL_session_reused(ssl_ptr) != 0; + let is_client = matches!(self.socket_type, SslServerOrClient::Client); + + // Use XOR logic from CPython + let use_finished = session_reused ^ is_client; + + let mut buf = vec![0u8; CB_MAXLEN]; + let len = if use_finished { + sys::SSL_get_finished(ssl_ptr, buf.as_mut_ptr() as *mut _, CB_MAXLEN) + } else { + sys::SSL_get_peer_finished(ssl_ptr, buf.as_mut_ptr() as *mut _, CB_MAXLEN) + }; + + if len == 0 { + Ok(None) + } else { + buf.truncate(len); + Ok(Some(vm.ctx.new_bytes(buf))) + } + } + } + + #[pymethod] + fn verify_client_post_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + #[cfg(ossl111)] + { + let stream = self.connection.read(); + let result = unsafe { SSL_verify_client_post_handshake(stream.ssl().as_ptr()) }; + if result == 0 { + Err(convert_openssl_error(vm, openssl::error::ErrorStack::get())) + } else { + Ok(()) + } + } + #[cfg(not(ossl111))] + { + Err(vm.new_not_implemented_error( + "Post-handshake auth is not supported by your OpenSSL version.".to_owned(), + )) + } + } + + #[pymethod] + fn shutdown(&self, vm: &VirtualMachine) -> PyResult> { + let stream = self.connection.read(); + + // BIO mode doesn't have an underlying socket + if stream.is_bio() { + return Err(vm.new_not_implemented_error( + "shutdown() is not supported for BIO-based SSL objects".to_owned(), + )); + } + + let ssl_ptr = stream.ssl().as_ptr(); + + // Perform SSL shutdown + let ret = unsafe { sys::SSL_shutdown(ssl_ptr) }; + + if ret < 0 { + // Error occurred + let err = unsafe { sys::SSL_get_error(ssl_ptr, ret) }; + + if err == sys::SSL_ERROR_WANT_READ || err == sys::SSL_ERROR_WANT_WRITE { + // Non-blocking would block - this is okay for shutdown + // Return the underlying socket + } else { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + format!("SSL shutdown failed: error code {}", err), + )); + } + } + + // Return the underlying socket + // Get the socket from the stream (SocketStream wraps PyRef) + let socket = stream + .get_ref() + .expect("unwrap() called on bio mode; should only be called in socket mode"); + Ok(socket.0.clone()) + } + + #[cfg(osslconf = "OPENSSL_NO_COMP")] + #[pymethod] + fn compression(&self) -> Option<&'static str> { + None + } + #[cfg(not(osslconf = "OPENSSL_NO_COMP"))] + #[pymethod] + fn compression(&self) -> Option<&'static str> { + let stream = self.connection.read(); + let comp_method = unsafe { sys::SSL_get_current_compression(stream.ssl().as_ptr()) }; + if comp_method.is_null() { + return None; + } + let typ = unsafe { sys::COMP_get_type(comp_method) }; + let nid = Nid::from_raw(typ); + if nid == Nid::UNDEF { + return None; + } + nid.short_name().ok() + } + + #[pymethod] + fn do_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + let mut stream = self.connection.write(); + let ssl_ptr = stream.ssl().as_ptr(); + + // BIO mode: no timeout/select logic, just do handshake + if stream.is_bio() { + return stream.do_handshake().map_err(|e| { + let exc = convert_ssl_error(vm, e); + // If it's a cert verification error, set verify info + if exc.class().is(PySslCertVerificationError::class(&vm.ctx)) { + set_verify_error_info(&exc, ssl_ptr, vm); + } + exc + }); + } + + // Socket mode: handle timeout and blocking + let timeout = stream + .get_ref() + .expect("handshake called in bio mode; should only be called in socket mode") + .timeout_deadline(); + loop { + let err = match stream.do_handshake() { + Ok(()) => return Ok(()), + Err(e) => e, + }; + let (needs, state) = stream + .get_ref() + .expect("handshake called in bio mode; should only be called in socket mode") + .socket_needs(&err, &timeout); + match state { + SelectRet::TimedOut => { + return Err(socket::timeout_error_msg( + vm, + "The handshake operation timed out".to_owned(), + )); + } + SelectRet::Closed => return Err(socket_closed_error(vm)), + SelectRet::Nonblocking => {} + SelectRet::IsBlocking | SelectRet::Ok => { + // For blocking sockets, select() has completed successfully + // Continue the handshake loop (matches CPython's SOCKET_IS_BLOCKING behavior) + if needs.is_some() { + continue; + } + } + } + let exc = convert_ssl_error(vm, err); + // If it's a cert verification error, set verify info + if exc.class().is(PySslCertVerificationError::class(&vm.ctx)) { + set_verify_error_info(&exc, ssl_ptr, vm); + } + return Err(exc); + } + } + + #[pymethod] + fn write(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { + let mut stream = self.connection.write(); + let data = data.borrow_buf(); + let data = &*data; + + // BIO mode: no timeout/select logic + if stream.is_bio() { + return stream.ssl_write(data).map_err(|e| convert_ssl_error(vm, e)); + } + + // Socket mode: handle timeout and blocking + let socket_ref = stream + .get_ref() + .expect("write called in bio mode; should only be called in socket mode"); + let timeout = socket_ref.timeout_deadline(); + let state = socket_ref.select(SslNeeds::Write, &timeout); + match state { + SelectRet::TimedOut => { + return Err(socket::timeout_error_msg( + vm, + "The write operation timed out".to_owned(), + )); + } + SelectRet::Closed => return Err(socket_closed_error(vm)), + _ => {} + } + loop { + let err = match stream.ssl_write(data) { + Ok(len) => return Ok(len), + Err(e) => e, + }; + let (needs, state) = stream + .get_ref() + .expect("write called in bio mode; should only be called in socket mode") + .socket_needs(&err, &timeout); + match state { + SelectRet::TimedOut => { + return Err(socket::timeout_error_msg( + vm, + "The write operation timed out".to_owned(), + )); + } + SelectRet::Closed => return Err(socket_closed_error(vm)), + SelectRet::Nonblocking => {} + SelectRet::IsBlocking | SelectRet::Ok => { + // For blocking sockets, select() has completed successfully + // Continue the write loop (matches CPython's SOCKET_IS_BLOCKING behavior) + if needs.is_some() { + continue; + } + } + } + return Err(convert_ssl_error(vm, err)); + } + } + + #[pygetset] + fn session(&self, _vm: &VirtualMachine) -> PyResult> { + let stream = self.connection.read(); + unsafe { + // Use SSL_get1_session which returns an owned reference (ref count already incremented) + let session_ptr = SSL_get1_session(stream.ssl().as_ptr()); + if session_ptr.is_null() { + Ok(None) + } else { + Ok(Some(PySslSession { + session: session_ptr, + ctx: self.ctx.read().clone(), + })) + } + } + } + + #[pygetset(setter)] + fn set_session(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + // Check if value is SSLSession type + let session = value + .downcast_ref::() + .ok_or_else(|| vm.new_type_error("Value is not a SSLSession.".to_owned()))?; + + // Check if session refers to the same SSLContext + if !std::ptr::eq( + self.ctx.read().ctx.read().as_ptr(), + session.ctx.ctx.read().as_ptr(), + ) { + return Err( + vm.new_value_error("Session refers to a different SSLContext.".to_owned()) + ); + } + + // Check if this is a client socket + if self.socket_type != SslServerOrClient::Client { + return Err( + vm.new_value_error("Cannot set session for server-side SSLSocket.".to_owned()) + ); + } + + // Check if handshake is not finished + let stream = self.connection.read(); + unsafe { + if sys::SSL_is_init_finished(stream.ssl().as_ptr()) != 0 { + return Err( + vm.new_value_error("Cannot set session after handshake.".to_owned()) + ); + } + + let ret = sys::SSL_set_session(stream.ssl().as_ptr(), session.session); + if ret == 0 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + } + + Ok(()) + } + + #[pygetset] + fn session_reused(&self) -> bool { + let stream = self.connection.read(); + unsafe { sys::SSL_session_reused(stream.ssl().as_ptr()) != 0 } + } + + #[pymethod] + fn read( + &self, + n: usize, + buffer: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + // Special case: reading 0 bytes should return empty bytes immediately + if n == 0 { + return if buffer.is_present() { + Ok(vm.ctx.new_int(0).into()) + } else { + Ok(vm.ctx.new_bytes(vec![]).into()) + }; + } + + let mut stream = self.connection.write(); + let mut inner_buffer = if let OptionalArg::Present(buffer) = &buffer { + Either::A(buffer.borrow_buf_mut()) + } else { + Either::B(vec![0u8; n]) + }; + let buf = match &mut inner_buffer { + Either::A(b) => &mut **b, + Either::B(b) => b.as_mut_slice(), + }; + let buf = match buf.get_mut(..n) { + Some(b) => b, + None => buf, + }; + + // BIO mode: no timeout/select logic + let count = if stream.is_bio() { + match stream.ssl_read(buf) { + Ok(count) => count, + Err(e) => return Err(convert_ssl_error(vm, e)), + } + } else { + // Socket mode: handle timeout and blocking + let timeout = stream + .get_ref() + .expect("read called in bio mode; should only be called in socket mode") + .timeout_deadline(); + loop { + let err = match stream.ssl_read(buf) { + Ok(count) => break count, + Err(e) => e, + }; + if err.code() == ssl::ErrorCode::ZERO_RETURN + && stream.get_shutdown() == ssl::ShutdownState::RECEIVED + { + break 0; + } + let (needs, state) = stream + .get_ref() + .expect("read called in bio mode; should only be called in socket mode") + .socket_needs(&err, &timeout); + match state { + SelectRet::TimedOut => { + return Err(socket::timeout_error_msg( + vm, + "The read operation timed out".to_owned(), + )); + } + SelectRet::Closed => return Err(socket_closed_error(vm)), + SelectRet::Nonblocking => {} + SelectRet::IsBlocking | SelectRet::Ok => { + // For blocking sockets, select() has completed successfully + // Continue the read loop (matches CPython's SOCKET_IS_BLOCKING behavior) + if needs.is_some() { + continue; + } + } + } + return Err(convert_ssl_error(vm, err)); + } + }; + let ret = match inner_buffer { + Either::A(_buf) => vm.ctx.new_int(count).into(), + Either::B(mut buf) => { + buf.truncate(count); + buf.shrink_to_fit(); + vm.ctx.new_bytes(buf).into() + } + }; + Ok(ret) + } + } + + #[pyattr] + #[pyclass(module = "ssl", name = "SSLSession")] + #[derive(PyPayload)] + struct PySslSession { + session: *mut sys::SSL_SESSION, + ctx: PyRef, + } + + impl fmt::Debug for PySslSession { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("SSLSession") + } + } + + impl Drop for PySslSession { + fn drop(&mut self) { + if !self.session.is_null() { + unsafe { + sys::SSL_SESSION_free(self.session); + } + } + } + } + + unsafe impl Send for PySslSession {} + unsafe impl Sync for PySslSession {} + + impl Comparable for PySslSession { + fn cmp( + zelf: &Py, + other: &crate::vm::PyObject, + op: PyComparisonOp, + _vm: &VirtualMachine, + ) -> PyResult { + let other = class_or_notimplemented!(Self, other); + + if !matches!(op, PyComparisonOp::Eq | PyComparisonOp::Ne) { + return Ok(PyComparisonValue::NotImplemented); + } + let mut eq = unsafe { + let mut self_len: libc::c_uint = 0; + let mut other_len: libc::c_uint = 0; + let self_id = sys::SSL_SESSION_get_id(zelf.session, &mut self_len); + let other_id = sys::SSL_SESSION_get_id(other.session, &mut other_len); + + if self_len != other_len { + false + } else { + let self_slice = std::slice::from_raw_parts(self_id, self_len as usize); + let other_slice = std::slice::from_raw_parts(other_id, other_len as usize); + self_slice == other_slice + } + }; + if matches!(op, PyComparisonOp::Ne) { + eq = !eq; + } + Ok(PyComparisonValue::Implemented(eq)) + } + } + + #[pyattr] + #[pyclass(module = "ssl", name = "MemoryBIO")] + #[derive(PyPayload)] + struct PySslMemoryBio { + bio: *mut sys::BIO, + eof_written: AtomicCell, + } + + impl fmt::Debug for PySslMemoryBio { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("MemoryBIO") + } + } + + impl Drop for PySslMemoryBio { + fn drop(&mut self) { + if !self.bio.is_null() { + unsafe { + sys::BIO_free_all(self.bio); + } + } + } + } + + unsafe impl Send for PySslMemoryBio {} + unsafe impl Sync for PySslMemoryBio {} + + // OpenSSL functions not in openssl-sys + + unsafe extern "C" { + // X509_check_ca returns 1 for CA certificates, 0 otherwise + fn X509_check_ca(x: *const sys::X509) -> libc::c_int; + } + + unsafe extern "C" { + fn SSL_get_ciphers(ssl: *const sys::SSL) -> *const sys::stack_st_SSL_CIPHER; + } + + #[cfg(ossl110)] + unsafe extern "C" { + fn SSL_get_client_ciphers(ssl: *const sys::SSL) -> *const sys::stack_st_SSL_CIPHER; + } + + #[cfg(ossl111)] + unsafe extern "C" { + fn SSL_verify_client_post_handshake(ssl: *const sys::SSL) -> libc::c_int; + fn SSL_set_post_handshake_auth(ssl: *mut sys::SSL, val: libc::c_int); + } + + #[cfg(ossl110)] + unsafe extern "C" { + fn SSL_CTX_get_security_level(ctx: *const sys::SSL_CTX) -> libc::c_int; + } + + unsafe extern "C" { + fn SSL_set_SSL_CTX(ssl: *mut sys::SSL, ctx: *mut sys::SSL_CTX) -> *mut sys::SSL_CTX; + } + + // Message callback type + #[allow(non_camel_case_types)] + type SSL_CTX_msg_callback = Option< + unsafe extern "C" fn( + write_p: libc::c_int, + version: libc::c_int, + content_type: libc::c_int, + buf: *const libc::c_void, + len: usize, + ssl: *mut sys::SSL, + arg: *mut libc::c_void, + ), + >; + + unsafe extern "C" { + fn SSL_CTX_set_msg_callback(ctx: *mut sys::SSL_CTX, cb: SSL_CTX_msg_callback); + } + + #[cfg(ossl110)] + unsafe extern "C" { + fn SSL_SESSION_has_ticket(session: *const sys::SSL_SESSION) -> libc::c_int; + fn SSL_SESSION_get_ticket_lifetime_hint(session: *const sys::SSL_SESSION) -> libc::c_ulong; + } + + // X509 object types + const X509_LU_X509: libc::c_int = 1; + const X509_LU_CRL: libc::c_int = 2; + + unsafe extern "C" { + fn X509_OBJECT_get_type(obj: *const sys::X509_OBJECT) -> libc::c_int; + fn SSL_set_session_id_context( + ssl: *mut sys::SSL, + sid_ctx: *const libc::c_uchar, + sid_ctx_len: libc::c_uint, + ) -> libc::c_int; + fn SSL_get1_session(ssl: *const sys::SSL) -> *mut sys::SSL_SESSION; + } + + // SSL session statistics constants (used with SSL_CTX_ctrl) + const SSL_CTRL_SESS_NUMBER: libc::c_int = 20; + const SSL_CTRL_SESS_CONNECT: libc::c_int = 21; + const SSL_CTRL_SESS_CONNECT_GOOD: libc::c_int = 22; + const SSL_CTRL_SESS_CONNECT_RENEGOTIATE: libc::c_int = 23; + const SSL_CTRL_SESS_ACCEPT: libc::c_int = 24; + const SSL_CTRL_SESS_ACCEPT_GOOD: libc::c_int = 25; + const SSL_CTRL_SESS_ACCEPT_RENEGOTIATE: libc::c_int = 26; + const SSL_CTRL_SESS_HIT: libc::c_int = 27; + const SSL_CTRL_SESS_MISSES: libc::c_int = 29; + const SSL_CTRL_SESS_TIMEOUTS: libc::c_int = 30; + const SSL_CTRL_SESS_CACHE_FULL: libc::c_int = 31; + + // SSL session statistics functions (implemented as macros in OpenSSL) + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_number(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_NUMBER, 0, std::ptr::null_mut()) } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_connect(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_CONNECT, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_connect_good(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_CONNECT_GOOD, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_connect_renegotiate(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_CONNECT_RENEGOTIATE, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_accept(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_ACCEPT, 0, std::ptr::null_mut()) } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_accept_good(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_ACCEPT_GOOD, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_accept_renegotiate(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_ACCEPT_RENEGOTIATE, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_hits(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_HIT, 0, std::ptr::null_mut()) } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_misses(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_MISSES, 0, std::ptr::null_mut()) } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_timeouts(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_TIMEOUTS, + 0, + std::ptr::null_mut(), + ) + } + } + + #[allow(non_snake_case)] + unsafe fn SSL_CTX_sess_cache_full(ctx: *const sys::SSL_CTX) -> libc::c_long { + unsafe { + sys::SSL_CTX_ctrl( + ctx as *mut _, + SSL_CTRL_SESS_CACHE_FULL, + 0, + std::ptr::null_mut(), + ) + } + } + + // DH parameters functions + unsafe extern "C" { + fn PEM_read_DHparams( + fp: *mut libc::FILE, + x: *mut *mut sys::DH, + cb: *mut libc::c_void, + u: *mut libc::c_void, + ) -> *mut sys::DH; + } + + // OpenSSL BIO helper functions + // These are typically macros in OpenSSL, implemented via BIO_ctrl + const BIO_CTRL_PENDING: libc::c_int = 10; + const BIO_CTRL_SET_EOF: libc::c_int = 2; + + #[allow(non_snake_case)] + unsafe fn BIO_ctrl_pending(bio: *mut sys::BIO) -> usize { + unsafe { sys::BIO_ctrl(bio, BIO_CTRL_PENDING, 0, std::ptr::null_mut()) as usize } + } + + #[allow(non_snake_case)] + unsafe fn BIO_set_mem_eof_return(bio: *mut sys::BIO, eof: libc::c_int) -> libc::c_int { + unsafe { + sys::BIO_ctrl( + bio, + BIO_CTRL_SET_EOF, + eof as libc::c_long, + std::ptr::null_mut(), + ) as libc::c_int + } + } + + #[allow(non_snake_case)] + unsafe fn BIO_clear_retry_flags(bio: *mut sys::BIO) { + unsafe { + sys::BIO_clear_flags(bio, sys::BIO_FLAGS_RWS | sys::BIO_FLAGS_SHOULD_RETRY); + } + } + + impl Constructor for PySslMemoryBio { + type Args = (); + + fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + unsafe { + let bio = sys::BIO_new(sys::BIO_s_mem()); + if bio.is_null() { + return Err(vm.new_memory_error("failed to allocate BIO".to_owned())); + } + + sys::BIO_set_retry_read(bio); + BIO_set_mem_eof_return(bio, -1); + + PySslMemoryBio { + bio, + eof_written: AtomicCell::new(false), + } + .into_ref_with_type(vm, cls) + .map(Into::into) + } + } + } + + #[pyclass(flags(IMMUTABLETYPE), with(Constructor))] + impl PySslMemoryBio { + #[pygetset] + fn pending(&self) -> usize { + unsafe { BIO_ctrl_pending(self.bio) } + } + + #[pygetset] + fn eof(&self) -> bool { + let pending = unsafe { BIO_ctrl_pending(self.bio) }; + pending == 0 && self.eof_written.load() + } + + #[pymethod] + fn read(&self, size: OptionalArg, vm: &VirtualMachine) -> PyResult> { + unsafe { + let avail = BIO_ctrl_pending(self.bio).min(i32::MAX as usize) as i32; + let len = size.unwrap_or(-1); + let len = if len < 0 || len > avail { avail } else { len }; + + // Check if EOF has been written and no data available + // This matches CPython's behavior where read() returns b'' when EOF is set + if len == 0 && self.eof_written.load() { + return Ok(Vec::new()); + } + + if len == 0 { + // No data available and no EOF - would block + // Call BIO_read() to get the proper error (SSL_ERROR_WANT_READ) + let mut test_buf = [0u8; 1]; + let nbytes = sys::BIO_read(self.bio, test_buf.as_mut_ptr() as *mut _, 1); + if nbytes < 0 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + // Shouldn't reach here, but if we do, return what we got + return Ok(test_buf[..nbytes as usize].to_vec()); + } + + let mut buf = vec![0u8; len as usize]; + let nbytes = sys::BIO_read(self.bio, buf.as_mut_ptr() as *mut _, len); + + if nbytes < 0 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + + buf.truncate(nbytes as usize); + Ok(buf) + } + } + + #[pymethod] + fn write(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { + if self.eof_written.load() { + return Err(vm.new_exception_msg( + PySslError::class(&vm.ctx).to_owned(), + "cannot write() after write_eof()".to_owned(), + )); + } + + data.with_ref(|buf| unsafe { + if buf.len() > i32::MAX as usize { + return Err( + vm.new_overflow_error(format!("string longer than {} bytes", i32::MAX)) + ); + } + + let nbytes = sys::BIO_write(self.bio, buf.as_ptr() as *const _, buf.len() as i32); + if nbytes < 0 { + return Err(convert_openssl_error(vm, ErrorStack::get())); + } + + Ok(nbytes) + }) + } + + #[pymethod] + fn write_eof(&self) { + self.eof_written.store(true); + unsafe { + BIO_clear_retry_flags(self.bio); + BIO_set_mem_eof_return(self.bio, 0); + } + } + } + + #[pyclass(flags(IMMUTABLETYPE), with(Comparable))] + impl PySslSession { + #[pygetset] + fn time(&self) -> i64 { + unsafe { + #[cfg(ossl330)] + { + sys::SSL_SESSION_get_time(self.session) as i64 + } + #[cfg(not(ossl330))] + { + sys::SSL_SESSION_get_time(self.session) as i64 + } + } + } + + #[pygetset] + fn timeout(&self) -> i64 { + unsafe { sys::SSL_SESSION_get_timeout(self.session) as i64 } + } + + #[pygetset] + fn ticket_lifetime_hint(&self) -> u64 { + // SSL_SESSION_get_ticket_lifetime_hint available in OpenSSL 1.1.0+ + #[cfg(ossl110)] + { + unsafe { SSL_SESSION_get_ticket_lifetime_hint(self.session) as u64 } + } + #[cfg(not(ossl110))] + { + // Not available in older OpenSSL versions + 0 + } + } + + #[pygetset] + fn id(&self, vm: &VirtualMachine) -> PyBytesRef { + unsafe { + let mut len: libc::c_uint = 0; + let id_ptr = sys::SSL_SESSION_get_id(self.session, &mut len); + let id_slice = std::slice::from_raw_parts(id_ptr, len as usize); + vm.ctx.new_bytes(id_slice.to_vec()) + } + } + + #[pygetset] + fn has_ticket(&self) -> bool { + // SSL_SESSION_has_ticket available in OpenSSL 1.1.0+ + #[cfg(ossl110)] + { + unsafe { SSL_SESSION_has_ticket(self.session) != 0 } + } + #[cfg(not(ossl110))] + { + // Not available in older OpenSSL versions + false + } + } + } + + #[track_caller] + pub(crate) fn convert_openssl_error( + vm: &VirtualMachine, + err: ErrorStack, + ) -> PyBaseExceptionRef { + match err.errors().last() { + Some(e) => { + // Check if this is a system library error (errno-based) + let lib = sys::ERR_GET_LIB(e.code()); + + if lib == sys::ERR_LIB_SYS { + // A system error is being reported; reason is set to errno + let reason = sys::ERR_GET_REASON(e.code()); + + // errno 2 = ENOENT = FileNotFoundError + let exc_type = if reason == 2 { + vm.ctx.exceptions.file_not_found_error.to_owned() + } else { + vm.ctx.exceptions.os_error.to_owned() + }; + let exc = vm.new_exception(exc_type, vec![vm.ctx.new_int(reason).into()]); + // Set errno attribute explicitly + let _ = exc + .as_object() + .set_attr("errno", vm.ctx.new_int(reason), vm); + return exc; + } + + let caller = std::panic::Location::caller(); + let (file, line) = (caller.file(), caller.line()); + let file = file + .rsplit_once(&['/', '\\'][..]) + .map_or(file, |(_, basename)| basename); + + // Get error codes - same approach as CPython + let lib = sys::ERR_GET_LIB(e.code()); + let reason = sys::ERR_GET_REASON(e.code()); + + // Look up error mnemonic from our static tables + // CPython uses dict lookup: err_codes_to_names[(lib, reason)] + let key = super::ssl_data::encode_error_key(lib, reason); + let errstr = super::ssl_data::ERROR_CODES + .get(&key) + .copied() + .or_else(|| { + // Fallback: use OpenSSL's error string + e.reason() + }) + .unwrap_or("unknown error"); + + // Check if this is a certificate verification error + // ERR_LIB_SSL = 20 (from _ssl_data_300.h) + // SSL_R_CERTIFICATE_VERIFY_FAILED = 134 (from _ssl_data_300.h) + let is_cert_verify_error = lib == 20 && reason == 134; + + // Look up library name from our static table + // CPython uses: lib_codes_to_names[lib] + let lib_name = super::ssl_data::LIBRARY_CODES.get(&(lib as u32)).copied(); + + // Use SSLCertVerificationError for certificate verification failures + let cls = if is_cert_verify_error { + PySslCertVerificationError::class(&vm.ctx).to_owned() + } else { + PySslError::class(&vm.ctx).to_owned() + }; + + // Build message + let msg = if let Some(lib_str) = lib_name { + format!("[{lib_str}] {errstr} ({file}:{line})") + } else { + format!("{errstr} ({file}:{line})") + }; + + // Create exception instance + let reason = sys::ERR_GET_REASON(e.code()); + let exc = vm.new_exception( + cls, + vec![vm.ctx.new_int(reason).into(), vm.ctx.new_str(msg).into()], + ); + + // Set attributes on instance, not class + let exc_obj: PyObjectRef = exc.into(); + + // Set reason attribute (always set, even if just the error string) + let reason_value = vm.ctx.new_str(errstr); + let _ = exc_obj.set_attr("reason", reason_value, vm); + + // Set library attribute (None if not available) + let library_value: PyObjectRef = if let Some(lib_str) = lib_name { + vm.ctx.new_str(lib_str).into() + } else { + vm.ctx.none() + }; + let _ = exc_obj.set_attr("library", library_value, vm); + + // For SSLCertVerificationError, set verify_code and verify_message + // Note: These will be set to None here, and can be updated by the caller + // if they have access to the SSL object + if is_cert_verify_error { + let _ = exc_obj.set_attr("verify_code", vm.ctx.none(), vm); + let _ = exc_obj.set_attr("verify_message", vm.ctx.none(), vm); + } + + // Convert back to PyBaseExceptionRef + exc_obj.downcast().expect( + "exc_obj is created as PyBaseExceptionRef and must downcast successfully", + ) + } + None => { + let cls = PySslError::class(&vm.ctx).to_owned(); + vm.new_exception_empty(cls) + } + } + } + + // Helper function to set verify_code and verify_message on SSLCertVerificationError + fn set_verify_error_info( + exc: &PyBaseExceptionRef, + ssl_ptr: *const sys::SSL, + vm: &VirtualMachine, + ) { + // Get verify result + let verify_code = unsafe { sys::SSL_get_verify_result(ssl_ptr) }; + let verify_code_obj = vm.ctx.new_int(verify_code); + + // Get verify message + let verify_message = unsafe { + let verify_str = sys::X509_verify_cert_error_string(verify_code); + if verify_str.is_null() { + vm.ctx.none() + } else { + let c_str = std::ffi::CStr::from_ptr(verify_str); + vm.ctx.new_str(c_str.to_string_lossy()).into() + } + }; + + let exc_obj = exc.as_object(); + let _ = exc_obj.set_attr("verify_code", verify_code_obj, vm); + let _ = exc_obj.set_attr("verify_message", verify_message, vm); + } + #[track_caller] + fn convert_ssl_error( + vm: &VirtualMachine, + e: impl std::borrow::Borrow, + ) -> PyBaseExceptionRef { + let e = e.borrow(); + let (cls, msg) = match e.code() { + ssl::ErrorCode::WANT_READ => ( + PySslWantReadError::class(&vm.ctx).to_owned(), + "The operation did not complete (read)", + ), + ssl::ErrorCode::WANT_WRITE => ( + PySslWantWriteError::class(&vm.ctx).to_owned(), + "The operation did not complete (write)", + ), + ssl::ErrorCode::SYSCALL => match e.io_error() { + Some(io_err) => return io_err.to_pyexception(vm), + // When no I/O error and OpenSSL error queue is empty, + // this is an EOF in violation of protocol -> SSLEOFError + // Need to set args[0] = SSL_ERROR_EOF for suppress_ragged_eofs check + None => { + return vm.new_exception( + PySslEOFError::class(&vm.ctx).to_owned(), + vec![ + vm.ctx.new_int(SSL_ERROR_EOF).into(), + vm.ctx + .new_str("EOF occurred in violation of protocol") + .into(), + ], + ); + } + }, + ssl::ErrorCode::SSL => { + // Check for OpenSSL 3.0 SSL_R_UNEXPECTED_EOF_WHILE_READING + if let Some(ssl_err) = e.ssl_error() { + // In OpenSSL 3.0+, unexpected EOF is reported as SSL_ERROR_SSL + // with this specific reason code instead of SSL_ERROR_SYSCALL + unsafe { + let err_code = sys::ERR_peek_last_error(); + let reason = sys::ERR_GET_REASON(err_code); + let lib = sys::ERR_GET_LIB(err_code); + if lib == ERR_LIB_SSL && reason == SSL_R_UNEXPECTED_EOF_WHILE_READING { + return vm.new_exception( + PySslEOFError::class(&vm.ctx).to_owned(), + vec![ + vm.ctx.new_int(SSL_ERROR_EOF).into(), + vm.ctx + .new_str("EOF occurred in violation of protocol") + .into(), + ], + ); + } + } + return convert_openssl_error(vm, ssl_err.clone()); + } + ( + PySslError::class(&vm.ctx).to_owned(), + "A failure in the SSL library occurred", + ) + } + _ => ( + PySslError::class(&vm.ctx).to_owned(), + "A failure in the SSL library occurred", + ), + }; + vm.new_exception_msg(cls, msg.to_owned()) + } + + // SSL_FILETYPE_ASN1 part of _add_ca_certs in CPython + fn x509_stack_from_der(der: &[u8]) -> Result, ErrorStack> { + unsafe { + openssl::init(); + let bio = bio::MemBioSlice::new(der)?; + + let mut certs = vec![]; + + loop { + let cert = sys::d2i_X509_bio(bio.as_ptr(), std::ptr::null_mut()); + if cert.is_null() { + break; + } + certs.push(X509::from_ptr(cert)); + } + + if certs.is_empty() { + // No certificates loaded at all + return Err(ErrorStack::get()); + } + + // Successfully loaded at least one certificate from DER data. + // Clear any trailing errors from EOF. + // CPython clears errors when: + // - DER: was_bio_eof is set (EOF reached) + // - PEM: PEM_R_NO_START_LINE error (normal EOF) + // Both cases mean successful completion with loaded certs. + eprintln!( + "[x509_stack_from_der] SUCCESS: Clearing errors and returning {} certs", + certs.len() + ); + sys::ERR_clear_error(); + Ok(certs) + } + } + + type CipherTuple = (&'static str, &'static str, i32); + + fn cipher_to_tuple(cipher: &ssl::SslCipherRef) -> CipherTuple { + (cipher.name(), cipher.version(), cipher.bits().secret) + } + + fn cipher_description(cipher: *const sys::SSL_CIPHER) -> String { + unsafe { + // SSL_CIPHER_description writes up to 128 bytes + let mut buf = vec![0u8; 256]; + let result = sys::SSL_CIPHER_description( + cipher, + buf.as_mut_ptr() as *mut libc::c_char, + buf.len() as i32, + ); + if result.is_null() { + return String::from("No description available"); + } + // Find the null terminator + let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len()); + String::from_utf8_lossy(&buf[..len]).trim().to_string() + } + } + + impl Read for SocketStream { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let mut socket: &PySocket = &self.0; + socket.read(buf) + } + } + + impl Write for SocketStream { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let mut socket: &PySocket = &self.0; + socket.write(buf) + } + fn flush(&mut self) -> std::io::Result<()> { + let mut socket: &PySocket = &self.0; + socket.flush() + } + } + + #[cfg(target_os = "android")] + mod android { + use super::convert_openssl_error; + use crate::vm::{VirtualMachine, builtins::PyBaseExceptionRef}; + use openssl::{ + ssl::SslContextBuilder, + x509::{X509, store::X509StoreBuilder}, + }; + use std::{ + fs::{File, read_dir}, + io::Read, + path::Path, + }; + + static CERT_DIR: &'static str = "/system/etc/security/cacerts"; + + pub(super) fn load_client_ca_list( + vm: &VirtualMachine, + b: &mut SslContextBuilder, + ) -> Result<(), PyBaseExceptionRef> { + let root = Path::new(CERT_DIR); + if !root.is_dir() { + return Err(vm.new_exception_msg( + vm.ctx.exceptions.file_not_found_error.to_owned(), + CERT_DIR.to_string(), + )); + } + + let mut combined_pem = String::new(); + let entries = read_dir(root) + .map_err(|err| vm.new_os_error(format!("read cert root: {}", err)))?; + for entry in entries { + let entry = + entry.map_err(|err| vm.new_os_error(format!("iter cert root: {}", err)))?; + + let path = entry.path(); + if !path.is_file() { + continue; + } + + File::open(&path) + .and_then(|mut file| file.read_to_string(&mut combined_pem)) + .map_err(|err| { + vm.new_os_error(format!("open cert file {}: {}", path.display(), err)) + })?; + + combined_pem.push('\n'); + } + + let mut store_b = + X509StoreBuilder::new().map_err(|err| convert_openssl_error(vm, err))?; + let x509_vec = X509::stack_from_pem(combined_pem.as_bytes()) + .map_err(|err| convert_openssl_error(vm, err))?; + for x509 in x509_vec { + store_b + .add_cert(x509) + .map_err(|err| convert_openssl_error(vm, err))?; + } + b.set_cert_store(store_b.build()); + + Ok(()) + } + } +} + +#[cfg(not(ossl101))] +#[pymodule(sub)] +mod ossl101 {} + +#[cfg(not(ossl111))] +#[pymodule(sub)] +mod ossl111 {} + +#[cfg(not(windows))] +#[pymodule(sub)] +mod windows {} + +#[allow(non_upper_case_globals)] +#[cfg(ossl101)] +#[pymodule(sub)] +mod ossl101 { + #[pyattr] + use openssl_sys::{ + SSL_OP_NO_COMPRESSION as OP_NO_COMPRESSION, SSL_OP_NO_TLSv1_1 as OP_NO_TLSv1_1, + SSL_OP_NO_TLSv1_2 as OP_NO_TLSv1_2, + }; +} + +#[allow(non_upper_case_globals)] +#[cfg(ossl111)] +#[pymodule(sub)] +mod ossl111 { + #[pyattr] + use openssl_sys::SSL_OP_NO_TLSv1_3 as OP_NO_TLSv1_3; +} + +#[cfg(windows)] +#[pymodule(sub)] +mod windows { + use crate::{ + common::ascii, + vm::{ + PyObjectRef, PyPayload, PyResult, VirtualMachine, + builtins::{PyFrozenSet, PyStrRef}, + convert::ToPyException, + }, + }; + + #[pyfunction] + fn enum_certificates(store_name: PyStrRef, vm: &VirtualMachine) -> PyResult> { + use schannel::{RawPointer, cert_context::ValidUses, cert_store::CertStore}; + use windows_sys::Win32::Security::Cryptography; + + // TODO: check every store for it, not just 2 of them: + // https://github.com/python/cpython/blob/3.8/Modules/_ssl.c#L5603-L5610 + let open_fns = [CertStore::open_current_user, CertStore::open_local_machine]; + let stores = open_fns + .iter() + .filter_map(|open| open(store_name.as_str()).ok()) + .collect::>(); + let certs = stores.iter().flat_map(|s| s.certs()).map(|c| { + let cert = vm.ctx.new_bytes(c.to_der().to_owned()); + let enc_type = unsafe { + let ptr = c.as_ptr() as *const Cryptography::CERT_CONTEXT; + (*ptr).dwCertEncodingType + }; + let enc_type = match enc_type { + Cryptography::X509_ASN_ENCODING => vm.new_pyobj(ascii!("x509_asn")), + Cryptography::PKCS_7_ASN_ENCODING => vm.new_pyobj(ascii!("pkcs_7_asn")), + other => vm.new_pyobj(other), + }; + let usage: PyObjectRef = match c.valid_uses().map_err(|e| e.to_pyexception(vm))? { + ValidUses::All => vm.ctx.new_bool(true).into(), + ValidUses::Oids(oids) => PyFrozenSet::from_iter( + vm, + oids.into_iter().map(|oid| vm.ctx.new_str(oid).into()), + )? + .into_ref(&vm.ctx) + .into(), + }; + Ok(vm.new_tuple((cert, enc_type, usage)).into()) + }); + let certs: Vec = certs.collect::>>()?; + Ok(certs) + } +} + +mod bio { + //! based off rust-openssl's private `bio` module + + use libc::c_int; + use openssl::error::ErrorStack; + use openssl_sys as sys; + use std::marker::PhantomData; + + pub struct MemBioSlice<'a>(*mut sys::BIO, PhantomData<&'a [u8]>); + + impl Drop for MemBioSlice<'_> { + fn drop(&mut self) { + unsafe { + sys::BIO_free_all(self.0); + } + } + } + + impl<'a> MemBioSlice<'a> { + pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { + openssl::init(); + + assert!(buf.len() <= c_int::MAX as usize); + let bio = unsafe { sys::BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int) }; + if bio.is_null() { + return Err(ErrorStack::get()); + } + + Ok(MemBioSlice(bio, PhantomData)) + } + + pub fn as_ptr(&self) -> *mut sys::BIO { + self.0 + } + } +} diff --git a/stdlib/src/openssl/cert.rs b/stdlib/src/openssl/cert.rs new file mode 100644 index 00000000000..1139f0e26f0 --- /dev/null +++ b/stdlib/src/openssl/cert.rs @@ -0,0 +1,229 @@ +pub(super) use ssl_cert::{PySSLCertificate, cert_to_certificate, cert_to_py, obj2txt}; + +// Certificate type for SSL module + +#[pymodule(sub)] +pub(crate) mod ssl_cert { + use crate::{ + common::ascii, + vm::{ + PyObjectRef, PyPayload, PyResult, VirtualMachine, + convert::{ToPyException, ToPyObject}, + function::{FsPath, OptionalArg}, + }, + }; + use foreign_types_shared::ForeignTypeRef; + use openssl::{ + asn1::Asn1ObjectRef, + x509::{self, X509, X509Ref}, + }; + use openssl_sys as sys; + use std::fmt; + + // Import constants and error converter from _ssl module + use crate::openssl::_ssl::{ENCODING_DER, ENCODING_PEM, convert_openssl_error}; + + pub(crate) fn obj2txt(obj: &Asn1ObjectRef, no_name: bool) -> Option { + let no_name = i32::from(no_name); + let ptr = obj.as_ptr(); + let b = unsafe { + let buflen = sys::OBJ_obj2txt(std::ptr::null_mut(), 0, ptr, no_name); + assert!(buflen >= 0); + if buflen == 0 { + return None; + } + let buflen = buflen as usize; + let mut buf = Vec::::with_capacity(buflen + 1); + let ret = sys::OBJ_obj2txt( + buf.as_mut_ptr() as *mut libc::c_char, + buf.capacity() as _, + ptr, + no_name, + ); + assert!(ret >= 0); + // SAFETY: OBJ_obj2txt initialized the buffer successfully + buf.set_len(buflen); + buf + }; + let s = String::from_utf8(b) + .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned()); + Some(s) + } + + #[pyattr] + #[pyclass(module = "ssl", name = "Certificate")] + #[derive(PyPayload)] + pub(crate) struct PySSLCertificate { + cert: X509, + } + + impl fmt::Debug for PySSLCertificate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Certificate") + } + } + + #[pyclass] + impl PySSLCertificate { + #[pymethod] + fn public_bytes( + &self, + format: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + let format = format.unwrap_or(ENCODING_PEM); + + match format { + ENCODING_DER => { + // DER encoding + let der = self + .cert + .to_der() + .map_err(|e| convert_openssl_error(vm, e))?; + Ok(vm.ctx.new_bytes(der).into()) + } + ENCODING_PEM => { + // PEM encoding + let pem = self + .cert + .to_pem() + .map_err(|e| convert_openssl_error(vm, e))?; + Ok(vm.ctx.new_bytes(pem).into()) + } + _ => Err(vm.new_value_error("Unsupported format")), + } + } + + #[pymethod] + fn get_info(&self, vm: &VirtualMachine) -> PyResult { + cert_to_dict(vm, &self.cert) + } + } + + fn name_to_py(vm: &VirtualMachine, name: &x509::X509NameRef) -> PyResult { + let list = name + .entries() + .map(|entry| { + let txt = obj2txt(entry.object(), false).to_pyobject(vm); + let asn1_str = entry.data(); + let data_bytes = asn1_str.as_slice(); + let data = match std::str::from_utf8(data_bytes) { + Ok(s) => vm.ctx.new_str(s.to_owned()), + Err(_) => vm + .ctx + .new_str(String::from_utf8_lossy(data_bytes).into_owned()), + }; + Ok(vm.new_tuple(((txt, data),)).into()) + }) + .collect::>()?; + Ok(vm.ctx.new_tuple(list).into()) + } + + // Helper to convert X509 to dict (for getpeercert with binary=False) + fn cert_to_dict(vm: &VirtualMachine, cert: &X509Ref) -> PyResult { + let dict = vm.ctx.new_dict(); + + dict.set_item("subject", name_to_py(vm, cert.subject_name())?, vm)?; + dict.set_item("issuer", name_to_py(vm, cert.issuer_name())?, vm)?; + // X.509 version: OpenSSL uses 0-based (0=v1, 1=v2, 2=v3) but Python uses 1-based (1=v1, 2=v2, 3=v3) + dict.set_item("version", vm.new_pyobj(cert.version() + 1), vm)?; + + let serial_num = cert + .serial_number() + .to_bn() + .and_then(|bn| bn.to_hex_str()) + .map_err(|e| convert_openssl_error(vm, e))?; + dict.set_item( + "serialNumber", + vm.ctx.new_str(serial_num.to_owned()).into(), + vm, + )?; + + dict.set_item( + "notBefore", + vm.ctx.new_str(cert.not_before().to_string()).into(), + vm, + )?; + dict.set_item( + "notAfter", + vm.ctx.new_str(cert.not_after().to_string()).into(), + vm, + )?; + + if let Some(names) = cert.subject_alt_names() { + let san: Vec = names + .iter() + .map(|gen_name| { + if let Some(email) = gen_name.email() { + vm.new_tuple((ascii!("email"), email)).into() + } else if let Some(dnsname) = gen_name.dnsname() { + vm.new_tuple((ascii!("DNS"), dnsname)).into() + } else if let Some(ip) = gen_name.ipaddress() { + // Parse IP address properly (IPv4 or IPv6) + let ip_str = if ip.len() == 4 { + // IPv4 + format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]) + } else if ip.len() == 16 { + // IPv6 - format with all zeros visible (not compressed) + let ip_addr = std::net::Ipv6Addr::from(ip[0..16]); + let s = ip_addr.segments(); + format!( + "{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}", + s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7] + ) + } else { + // Fallback for unexpected length + String::from_utf8_lossy(ip).into_owned() + }; + vm.new_tuple((ascii!("IP Address"), ip_str)).into() + } else if let Some(uri) = gen_name.uri() { + vm.new_tuple((ascii!("URI"), uri)).into() + } else { + // Handle DirName, Registered ID, and othername + // Check if this is a directory name + if let Some(dirname) = gen_name.directory_name() + && let Ok(py_name) = name_to_py(vm, dirname) + { + return vm.new_tuple((ascii!("DirName"), py_name)).into(); + } + + // TODO: Handle Registered ID (GEN_RID) + // CPython implementation uses i2t_ASN1_OBJECT to convert OID + // This requires accessing GENERAL_NAME union which is complex in Rust + // For now, we return for unhandled types + + // For othername and other unsupported types + vm.new_tuple((ascii!("othername"), ascii!(""))) + .into() + } + }) + .collect(); + dict.set_item("subjectAltName", vm.ctx.new_tuple(san).into(), vm)?; + }; + + Ok(dict.into()) + } + + // Helper to create Certificate object from X509 + pub(crate) fn cert_to_certificate(vm: &VirtualMachine, cert: X509) -> PyResult { + Ok(PySSLCertificate { cert }.into_ref(&vm.ctx).into()) + } + + // For getpeercert() - returns bytes or dict depending on binary flag + pub(crate) fn cert_to_py(vm: &VirtualMachine, cert: &X509Ref, binary: bool) -> PyResult { + if binary { + let b = cert.to_der().map_err(|e| convert_openssl_error(vm, e))?; + Ok(vm.ctx.new_bytes(b).into()) + } else { + cert_to_dict(vm, cert) + } + } + + #[pyfunction] + pub(crate) fn _test_decode_cert(path: FsPath, vm: &VirtualMachine) -> PyResult { + let path = path.to_path_buf(vm)?; + let pem = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?; + let x509 = X509::from_pem(&pem).map_err(|e| convert_openssl_error(vm, e))?; + cert_to_py(vm, &x509, false) + } +} diff --git a/stdlib/src/ssl/ssl_data_111.rs b/stdlib/src/openssl/ssl_data_111.rs similarity index 100% rename from stdlib/src/ssl/ssl_data_111.rs rename to stdlib/src/openssl/ssl_data_111.rs diff --git a/stdlib/src/ssl/ssl_data_300.rs b/stdlib/src/openssl/ssl_data_300.rs similarity index 100% rename from stdlib/src/ssl/ssl_data_300.rs rename to stdlib/src/openssl/ssl_data_300.rs diff --git a/stdlib/src/ssl/ssl_data_31.rs b/stdlib/src/openssl/ssl_data_31.rs similarity index 100% rename from stdlib/src/ssl/ssl_data_31.rs rename to stdlib/src/openssl/ssl_data_31.rs diff --git a/stdlib/src/ssl.rs b/stdlib/src/ssl.rs index 9604999d7da..e0019ae4750 100644 --- a/stdlib/src/ssl.rs +++ b/stdlib/src/ssl.rs @@ -1,257 +1,356 @@ -// spell-checker:disable - +// spell-checker: ignore ssleof aesccm aesgcm getblocking setblocking ENDTLS + +//! Pure Rust SSL/TLS implementation using rustls +//! +//! This module provides SSL/TLS support without requiring C dependencies. +//! It implements the Python ssl module API using: +//! - rustls: TLS protocol implementation +//! - x509-parser/x509-cert: Certificate parsing +//! - ring: Cryptographic primitives +//! - rustls-platform-verifier: Platform-native certificate verification +//! +//! DO NOT add openssl dependency here. +//! +//! Warning: This library contains AI-generated code and comments. Do not trust any code or comment without verification. Please have a qualified expert review the code and remove this notice after review. + +// OID (Object Identifier) management module +mod oid; + +// Certificate operations module (parsing, validation, conversion) mod cert; -// Conditional compilation for OpenSSL version-specific error codes -cfg_if::cfg_if! { - if #[cfg(ossl310)] { - // OpenSSL 3.1.0+ - #[path = "ssl/ssl_data_31.rs"] - mod ssl_data; - } else if #[cfg(ossl300)] { - // OpenSSL 3.0.0+ - #[path = "ssl/ssl_data_300.rs"] - mod ssl_data; - } else { - // OpenSSL 1.1.1+ (fallback) - #[path = "ssl/ssl_data_111.rs"] - mod ssl_data; - } -} - -use crate::vm::{PyRef, VirtualMachine, builtins::PyModule}; -use openssl_probe::ProbeResult; +// OpenSSL compatibility layer (abstracts rustls operations) +mod compat; -pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef { - // if openssl is vendored, it doesn't know the locations - // of system certificates - cache the probe result now. - #[cfg(openssl_vendored)] - LazyLock::force(&PROBE); - _ssl::make_module(vm) -} - -// define our own copy of ProbeResult so we can handle the vendor case -// easily, without having to have a bunch of cfgs -cfg_if::cfg_if! { - if #[cfg(openssl_vendored)] { - use std::sync::LazyLock; - static PROBE: LazyLock = LazyLock::new(openssl_probe::probe); - fn probe() -> &'static ProbeResult { &PROBE } - } else { - fn probe() -> &'static ProbeResult { - &ProbeResult { cert_file: None, cert_dir: None } - } - } -} +pub(crate) use _ssl::make_module; +#[allow(non_snake_case)] #[allow(non_upper_case_globals)] -#[pymodule(with(cert::ssl_cert, ossl101, ossl111, windows))] +#[pymodule] mod _ssl { - use super::{bio, probe}; use crate::{ - common::lock::{ - PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard, + common::{ + hash::PyHash, + lock::{PyMutex, PyRwLock}, }, - socket::{self, PySocket}, + socket::{PySocket, SelectKind, sock_select, timeout_error_msg}, vm::{ - AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, - builtins::{ - PyBaseExceptionRef, PyBytesRef, PyListRef, PyOSError, PyStrRef, PyTypeRef, PyWeak, - }, - class_or_notimplemented, - convert::ToPyException, - exceptions, - function::{ - ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath, - OptionalArg, PyComparisonValue, - }, - types::{Comparable, Constructor, PyComparisonOp}, - utils::ToCString, + AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, + VirtualMachine, + builtins::{PyBaseExceptionRef, PyBytesRef, PyListRef, PyStrRef, PyTypeRef}, + convert::IntoPyException, + function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, PyComparisonValue}, + stdlib::warnings, + types::{Comparable, Constructor, Hashable, PyComparisonOp, Representable}, }, }; - use crossbeam_utils::atomic::AtomicCell; - use foreign_types_shared::{ForeignType, ForeignTypeRef}; - use openssl::{ - asn1::{Asn1Object, Asn1ObjectRef}, - error::ErrorStack, - nid::Nid, - ssl::{self, SslContextBuilder, SslOptions, SslVerifyMode}, - x509::X509, - }; - use openssl_sys as sys; - use rustpython_vm::ospath::OsPath; use std::{ - ffi::CStr, - fmt, - io::{Read, Write}, - path::{Path, PathBuf}, - sync::LazyLock, - time::Instant, + collections::HashMap, + io::Write, + sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }, + time::{Duration, SystemTime}, + }; + + // Rustls imports + use parking_lot::{Mutex as ParkingMutex, RwLock as ParkingRwLock}; + use pem_rfc7468::{LineEnding, encode_string}; + use rustls::{ + ClientConfig, ClientConnection, RootCertStore, ServerConfig, ServerConnection, + client::{ClientSessionMemoryCache, ClientSessionStore}, + crypto::SupportedKxGroup, + pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer, ServerName}, + server::{ClientHello, ResolvesServerCert}, + sign::CertifiedKey, + version::{TLS12, TLS13}, }; + use sha2::{Digest, Sha256}; + + // Import certificate operations module + use super::cert; - // Import certificate types from parent module - use super::cert::{self, cert_to_certificate, cert_to_py}; - - // Re-export PySSLCertificate to make it available in the _ssl module - // It will be automatically exposed to Python via #[pyclass] - #[allow(unused_imports)] - use super::cert::PySSLCertificate; - - // Constants - #[pyattr] - use sys::{ - // TODO: so many more of these - SSL_AD_DECODE_ERROR as ALERT_DESCRIPTION_DECODE_ERROR, - SSL_AD_ILLEGAL_PARAMETER as ALERT_DESCRIPTION_ILLEGAL_PARAMETER, - SSL_AD_UNRECOGNIZED_NAME as ALERT_DESCRIPTION_UNRECOGNIZED_NAME, - // SSL_ERROR_INVALID_ERROR_CODE, - SSL_ERROR_SSL, - // SSL_ERROR_WANT_X509_LOOKUP, - SSL_ERROR_SYSCALL, - SSL_ERROR_WANT_CONNECT, - SSL_ERROR_WANT_READ, - SSL_ERROR_WANT_WRITE, - SSL_ERROR_ZERO_RETURN, - SSL_OP_CIPHER_SERVER_PREFERENCE as OP_CIPHER_SERVER_PREFERENCE, - SSL_OP_ENABLE_MIDDLEBOX_COMPAT as OP_ENABLE_MIDDLEBOX_COMPAT, - SSL_OP_LEGACY_SERVER_CONNECT as OP_LEGACY_SERVER_CONNECT, - SSL_OP_NO_SSLv2 as OP_NO_SSLv2, - SSL_OP_NO_SSLv3 as OP_NO_SSLv3, - SSL_OP_NO_TICKET as OP_NO_TICKET, - SSL_OP_NO_TLSv1 as OP_NO_TLSv1, - SSL_OP_SINGLE_DH_USE as OP_SINGLE_DH_USE, - SSL_OP_SINGLE_ECDH_USE as OP_SINGLE_ECDH_USE, - X509_V_FLAG_ALLOW_PROXY_CERTS as VERIFY_ALLOW_PROXY_CERTS, - X509_V_FLAG_CRL_CHECK as VERIFY_CRL_CHECK_LEAF, - X509_V_FLAG_PARTIAL_CHAIN as VERIFY_X509_PARTIAL_CHAIN, - X509_V_FLAG_TRUSTED_FIRST as VERIFY_X509_TRUSTED_FIRST, - X509_V_FLAG_X509_STRICT as VERIFY_X509_STRICT, + // Import OID module + use super::oid; + + // Import compat module (OpenSSL compatibility layer) + use super::compat::{ + ClientConfigOptions, MultiCertResolver, ProtocolSettings, ServerConfigOptions, SslError, + TlsConnection, create_client_config, create_server_config, curve_name_to_kx_group, + extract_cipher_info, get_cipher_encryption_desc, is_blocking_io_error, + normalize_cipher_name, ssl_do_handshake, }; - // CRL verification constants + // Type aliases for better readability + // Additional type alias for certificate/key pairs (SessionCache and SniCertName defined below) + + /// Certificate and private key pair used in SSL contexts + type CertKeyPair = (Arc, PrivateKeyDer<'static>); + + // Constants matching Python ssl module + + // SSL/TLS Protocol versions + #[pyattr] + const PROTOCOL_TLS: i32 = 2; // Auto-negotiate best version + #[pyattr] + const PROTOCOL_SSLv23: i32 = PROTOCOL_TLS; // Alias for PROTOCOL_TLS #[pyattr] - const VERIFY_CRL_CHECK_CHAIN: libc::c_ulong = - sys::X509_V_FLAG_CRL_CHECK | sys::X509_V_FLAG_CRL_CHECK_ALL; + const PROTOCOL_TLS_CLIENT: i32 = 16; + #[pyattr] + const PROTOCOL_TLS_SERVER: i32 = 17; - // taken from CPython, should probably be kept up to date with their version if it ever changes + // Note: rustls doesn't support TLS 1.0/1.1 for security reasons + // These are defined for API compatibility but will raise errors if used #[pyattr] - const _DEFAULT_CIPHERS: &str = - "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"; - // #[pyattr] PROTOCOL_SSLv2: u32 = SslVersion::Ssl2 as u32; // unsupported - // #[pyattr] PROTOCOL_SSLv3: u32 = SslVersion::Ssl3 as u32; + const PROTOCOL_TLSv1: i32 = 3; #[pyattr] - const PROTOCOL_SSLv23: u32 = SslVersion::Tls as u32; + const PROTOCOL_TLSv1_1: i32 = 4; #[pyattr] - const PROTOCOL_TLS: u32 = SslVersion::Tls as u32; + const PROTOCOL_TLSv1_2: i32 = 5; #[pyattr] - const PROTOCOL_TLS_CLIENT: u32 = SslVersion::TlsClient as u32; + const PROTOCOL_TLSv1_3: i32 = 6; + + // Protocol version constants for TLSVersion enum #[pyattr] - const PROTOCOL_TLS_SERVER: u32 = SslVersion::TlsServer as u32; + const PROTO_SSLv3: i32 = 0x0300; #[pyattr] - const PROTOCOL_TLSv1: u32 = SslVersion::Tls1 as u32; + const PROTO_TLSv1: i32 = 0x0301; #[pyattr] - const PROTOCOL_TLSv1_1: u32 = SslVersion::Tls1_1 as u32; + const PROTO_TLSv1_1: i32 = 0x0302; #[pyattr] - const PROTOCOL_TLSv1_2: u32 = SslVersion::Tls1_2 as u32; + const PROTO_TLSv1_2: i32 = 0x0303; #[pyattr] - const PROTO_MINIMUM_SUPPORTED: i32 = ProtoVersion::MinSupported as i32; + const PROTO_TLSv1_3: i32 = 0x0304; + + // Minimum and maximum supported protocol versions for rustls + // Use special values -2 and -1 to avoid enum name conflicts #[pyattr] - const PROTO_SSLv3: i32 = ProtoVersion::Ssl3 as i32; + const PROTO_MINIMUM_SUPPORTED: i32 = -2; // special value #[pyattr] - const PROTO_TLSv1: i32 = ProtoVersion::Tls1 as i32; + const PROTO_MAXIMUM_SUPPORTED: i32 = -1; // special value + + // Internal constants for rustls actual supported versions + // rustls only supports TLS 1.2 and TLS 1.3 + const MINIMUM_VERSION: i32 = PROTO_TLSv1_2; // 0x0303 + const MAXIMUM_VERSION: i32 = PROTO_TLSv1_3; // 0x0304 + + // Buffer sizes and limits (OpenSSL/CPython compatibility) + const PEM_BUFSIZE: usize = 1024; + // OpenSSL: ssl/ssl_local.h + const SSL3_RT_MAX_PLAIN_LENGTH: usize = 16384; + // SSL session cache size (common practice, similar to OpenSSL defaults) + const SSL_SESSION_CACHE_SIZE: usize = 256; + + // Certificate verification modes #[pyattr] - const PROTO_TLSv1_1: i32 = ProtoVersion::Tls1_1 as i32; + const CERT_NONE: i32 = 0; #[pyattr] - const PROTO_TLSv1_2: i32 = ProtoVersion::Tls1_2 as i32; + const CERT_OPTIONAL: i32 = 1; #[pyattr] - const PROTO_TLSv1_3: i32 = ProtoVersion::Tls1_3 as i32; + const CERT_REQUIRED: i32 = 2; + + // Certificate requirements #[pyattr] - const PROTO_MAXIMUM_SUPPORTED: i32 = ProtoVersion::MaxSupported as i32; + const VERIFY_DEFAULT: i32 = 0; #[pyattr] - const OP_ALL: libc::c_ulong = (sys::SSL_OP_ALL & !sys::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) as _; + const VERIFY_CRL_CHECK_LEAF: i32 = 4; #[pyattr] - const HAS_TLS_UNIQUE: bool = true; + const VERIFY_CRL_CHECK_CHAIN: i32 = 12; #[pyattr] - const CERT_NONE: u32 = CertRequirements::None as u32; + const VERIFY_X509_STRICT: i32 = 32; #[pyattr] - const CERT_OPTIONAL: u32 = CertRequirements::Optional as u32; + const VERIFY_ALLOW_PROXY_CERTS: i32 = 64; #[pyattr] - const CERT_REQUIRED: u32 = CertRequirements::Required as u32; + const VERIFY_X509_TRUSTED_FIRST: i32 = 32768; #[pyattr] - const VERIFY_DEFAULT: u32 = 0; + const VERIFY_X509_PARTIAL_CHAIN: i32 = 0x80000; + + // Options (OpenSSL-compatible flags, mostly no-op in rustls) #[pyattr] - const SSL_ERROR_EOF: u32 = 8; // custom for python + const OP_NO_SSLv2: i32 = 0x00000000; // Not supported anyway #[pyattr] - const HAS_SNI: bool = true; + const OP_NO_SSLv3: i32 = 0x02000000; #[pyattr] - const HAS_ECDH: bool = true; + const OP_NO_TLSv1: i32 = 0x04000000; #[pyattr] - const HAS_NPN: bool = false; + const OP_NO_TLSv1_1: i32 = 0x10000000; #[pyattr] - const HAS_ALPN: bool = true; + const OP_NO_TLSv1_2: i32 = 0x08000000; #[pyattr] - const HAS_SSLv2: bool = false; + const OP_NO_TLSv1_3: i32 = 0x20000000; #[pyattr] - const HAS_SSLv3: bool = false; + const OP_NO_COMPRESSION: i32 = 0x00020000; + #[pyattr] + const OP_CIPHER_SERVER_PREFERENCE: i32 = 0x00400000; + #[pyattr] + const OP_SINGLE_DH_USE: i32 = 0x00000000; // No-op in rustls #[pyattr] - const HAS_TLSv1: bool = true; + const OP_SINGLE_ECDH_USE: i32 = 0x00000000; // No-op in rustls #[pyattr] - const HAS_TLSv1_1: bool = true; + const OP_NO_TICKET: i32 = 0x00004000; #[pyattr] - const HAS_TLSv1_2: bool = true; + const OP_LEGACY_SERVER_CONNECT: i32 = 0x00000004; #[pyattr] - const HAS_TLSv1_3: bool = cfg!(ossl111); + const OP_NO_RENEGOTIATION: i32 = 0x40000000; #[pyattr] - const HAS_PSK: bool = true; + const OP_IGNORE_UNEXPECTED_EOF: i32 = 0x00000080; + #[pyattr] + const OP_ENABLE_MIDDLEBOX_COMPAT: i32 = 0x00100000; + #[pyattr] + const OP_ALL: i32 = 0x00000BFB; // Combined "safe" options (reduced for i32, excluding OP_LEGACY_SERVER_CONNECT for OpenSSL 3.0.0+ compatibility) - // Encoding constants for Certificate.public_bytes() + // Error types + #[pyattr] + const SSL_ERROR_NONE: i32 = 0; + #[pyattr] + const SSL_ERROR_SSL: i32 = 1; #[pyattr] - pub(crate) const ENCODING_PEM: i32 = sys::X509_FILETYPE_PEM; + const SSL_ERROR_WANT_READ: i32 = 2; #[pyattr] - pub(crate) const ENCODING_DER: i32 = sys::X509_FILETYPE_ASN1; + const SSL_ERROR_WANT_WRITE: i32 = 3; #[pyattr] - const ENCODING_PEM_AUX: i32 = sys::X509_FILETYPE_PEM + 0x100; + const SSL_ERROR_WANT_X509_LOOKUP: i32 = 4; + #[pyattr] + const SSL_ERROR_SYSCALL: i32 = 5; + #[pyattr] + const SSL_ERROR_ZERO_RETURN: i32 = 6; + #[pyattr] + const SSL_ERROR_WANT_CONNECT: i32 = 7; + #[pyattr] + const SSL_ERROR_EOF: i32 = 8; + #[pyattr] + const SSL_ERROR_INVALID_ERROR_CODE: i32 = 10; - // OpenSSL error codes for unexpected EOF detection - const ERR_LIB_SSL: i32 = 20; - const SSL_R_UNEXPECTED_EOF_WHILE_READING: i32 = 294; + // Alert types (matching _TLSAlertType enum) + #[pyattr] + const ALERT_DESCRIPTION_CLOSE_NOTIFY: i32 = 0; + #[pyattr] + const ALERT_DESCRIPTION_UNEXPECTED_MESSAGE: i32 = 10; + #[pyattr] + const ALERT_DESCRIPTION_BAD_RECORD_MAC: i32 = 20; + #[pyattr] + const ALERT_DESCRIPTION_DECRYPTION_FAILED: i32 = 21; + #[pyattr] + const ALERT_DESCRIPTION_RECORD_OVERFLOW: i32 = 22; + #[pyattr] + const ALERT_DESCRIPTION_DECOMPRESSION_FAILURE: i32 = 30; + #[pyattr] + const ALERT_DESCRIPTION_HANDSHAKE_FAILURE: i32 = 40; + #[pyattr] + const ALERT_DESCRIPTION_NO_CERTIFICATE: i32 = 41; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE: i32 = 42; + #[pyattr] + const ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE: i32 = 43; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_REVOKED: i32 = 44; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_EXPIRED: i32 = 45; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN: i32 = 46; + #[pyattr] + const ALERT_DESCRIPTION_ILLEGAL_PARAMETER: i32 = 47; + #[pyattr] + const ALERT_DESCRIPTION_UNKNOWN_CA: i32 = 48; + #[pyattr] + const ALERT_DESCRIPTION_ACCESS_DENIED: i32 = 49; + #[pyattr] + const ALERT_DESCRIPTION_DECODE_ERROR: i32 = 50; + #[pyattr] + const ALERT_DESCRIPTION_DECRYPT_ERROR: i32 = 51; + #[pyattr] + const ALERT_DESCRIPTION_EXPORT_RESTRICTION: i32 = 60; + #[pyattr] + const ALERT_DESCRIPTION_PROTOCOL_VERSION: i32 = 70; + #[pyattr] + const ALERT_DESCRIPTION_INSUFFICIENT_SECURITY: i32 = 71; + #[pyattr] + const ALERT_DESCRIPTION_INTERNAL_ERROR: i32 = 80; + #[pyattr] + const ALERT_DESCRIPTION_INAPPROPRIATE_FALLBACK: i32 = 86; + #[pyattr] + const ALERT_DESCRIPTION_USER_CANCELLED: i32 = 90; + #[pyattr] + const ALERT_DESCRIPTION_NO_RENEGOTIATION: i32 = 100; + #[pyattr] + const ALERT_DESCRIPTION_MISSING_EXTENSION: i32 = 109; + #[pyattr] + const ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION: i32 = 110; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE: i32 = 111; + #[pyattr] + const ALERT_DESCRIPTION_UNRECOGNIZED_NAME: i32 = 112; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE: i32 = 113; + #[pyattr] + const ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE: i32 = 114; + #[pyattr] + const ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY: i32 = 115; + #[pyattr] + const ALERT_DESCRIPTION_CERTIFICATE_REQUIRED: i32 = 116; + #[pyattr] + const ALERT_DESCRIPTION_NO_APPLICATION_PROTOCOL: i32 = 120; - // SSL_VERIFY constants for post-handshake authentication - #[cfg(ossl111)] - const SSL_VERIFY_POST_HANDSHAKE: libc::c_int = 0x20; + // Version info - reporting as OpenSSL 3.3.0 for compatibility + #[pyattr] + const OPENSSL_VERSION_NUMBER: i32 = 0x30300000; // OpenSSL 3.3.0 (808452096) + #[pyattr] + const OPENSSL_VERSION: &str = "OpenSSL 3.3.0 (rustls/0.23)"; + #[pyattr] + const OPENSSL_VERSION_INFO: (i32, i32, i32, i32, i32) = (3, 3, 0, 0, 15); // 3.3.0 release + #[pyattr] + const _OPENSSL_API_VERSION: (i32, i32, i32, i32, i32) = (3, 3, 0, 0, 15); // 3.3.0 release - // the openssl version from the API headers + // Default cipher list for rustls - using modern secure ciphers + #[pyattr] + const _DEFAULT_CIPHERS: &str = + "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"; - #[pyattr(name = "OPENSSL_VERSION")] - fn openssl_version(_vm: &VirtualMachine) -> &str { - openssl::version::version() - } - #[pyattr(name = "OPENSSL_VERSION_NUMBER")] - fn openssl_version_number(_vm: &VirtualMachine) -> i64 { - openssl::version::number() - } - #[pyattr(name = "OPENSSL_VERSION_INFO")] - fn openssl_version_info(_vm: &VirtualMachine) -> OpensslVersionInfo { - parse_version_info(openssl::version::number()) - } + // Has features + #[pyattr] + const HAS_SNI: bool = true; + #[pyattr] + const HAS_TLS_UNIQUE: bool = false; // Not supported + #[pyattr] + const HAS_ECDH: bool = true; + #[pyattr] + const HAS_NPN: bool = false; // Deprecated, use ALPN + #[pyattr] + const HAS_ALPN: bool = true; + #[pyattr] + const HAS_PSK: bool = false; // PSK not supported in rustls + #[pyattr] + const HAS_SSLv2: bool = false; + #[pyattr] + const HAS_SSLv3: bool = false; + #[pyattr] + const HAS_TLSv1: bool = false; // Not supported for security + #[pyattr] + const HAS_TLSv1_1: bool = false; // Not supported for security + #[pyattr] + const HAS_TLSv1_2: bool = true; // rustls supports TLS 1.2 + #[pyattr] + const HAS_TLSv1_3: bool = true; - #[pyattr(name = "_OPENSSL_API_VERSION")] - fn _openssl_api_version(_vm: &VirtualMachine) -> OpensslVersionInfo { - let openssl_api_version = i64::from_str_radix(env!("OPENSSL_API_VERSION"), 16) - .expect("OPENSSL_API_VERSION is malformed"); - parse_version_info(openssl_api_version) - } + // Encoding constants (matching OpenSSL) + #[pyattr] + const ENCODING_PEM: i32 = 1; + #[pyattr] + const ENCODING_DER: i32 = 2; + #[pyattr] + const ENCODING_PEM_AUX: i32 = 0x101; // PEM + 0x100 - // SSL Exception Types + // Exception types + use rustpython_vm::builtins::PyOSError; - /// An error occurred in the SSL implementation. #[pyattr] #[pyexception(name = "SSLError", base = PyOSError)] #[derive(Debug)] - pub struct PySslError {} + pub struct PySSLError {} #[pyexception] - impl PySslError { + impl PySSLError { // Returns strerror attribute if available, otherwise str(args) #[pymethod] fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult { @@ -263,890 +362,973 @@ mod _ssl { } // Otherwise return str(args) - exc.args().as_object().str(vm) + let args = exc.args(); + if args.len() == 1 { + args.as_slice()[0].str(vm) + } else { + args.as_object().str(vm) + } } } - /// A certificate could not be verified. #[pyattr] - #[pyexception(name = "SSLCertVerificationError", base = PySslError)] + #[pyexception(name = "SSLZeroReturnError", base = PySSLError)] #[derive(Debug)] - pub struct PySslCertVerificationError {} + pub struct PySSLZeroReturnError {} #[pyexception] - impl PySslCertVerificationError {} + impl PySSLZeroReturnError {} - /// SSL/TLS session closed cleanly. #[pyattr] - #[pyexception(name = "SSLZeroReturnError", base = PySslError)] + #[pyexception(name = "SSLWantReadError", base = PySSLError)] #[derive(Debug)] - pub struct PySslZeroReturnError {} + pub struct PySSLWantReadError {} #[pyexception] - impl PySslZeroReturnError {} + impl PySSLWantReadError {} - /// Non-blocking SSL socket needs to read more data. #[pyattr] - #[pyexception(name = "SSLWantReadError", base = PySslError)] + #[pyexception(name = "SSLWantWriteError", base = PySSLError)] #[derive(Debug)] - pub struct PySslWantReadError {} + pub struct PySSLWantWriteError {} #[pyexception] - impl PySslWantReadError {} + impl PySSLWantWriteError {} - /// Non-blocking SSL socket needs to write more data. #[pyattr] - #[pyexception(name = "SSLWantWriteError", base = PySslError)] + #[pyexception(name = "SSLSyscallError", base = PySSLError)] #[derive(Debug)] - pub struct PySslWantWriteError {} + pub struct PySSLSyscallError {} #[pyexception] - impl PySslWantWriteError {} + impl PySSLSyscallError {} - /// System error when attempting SSL operation. #[pyattr] - #[pyexception(name = "SSLSyscallError", base = PySslError)] + #[pyexception(name = "SSLEOFError", base = PySSLError)] #[derive(Debug)] - pub struct PySslSyscallError {} + pub struct PySSLEOFError {} #[pyexception] - impl PySslSyscallError {} + impl PySSLEOFError {} - /// SSL/TLS connection terminated abruptly. #[pyattr] - #[pyexception(name = "SSLEOFError", base = PySslError)] + #[pyexception(name = "SSLCertVerificationError", base = PySSLError)] #[derive(Debug)] - pub struct PySslEOFError {} + pub struct PySSLCertVerificationError {} #[pyexception] - impl PySslEOFError {} - - type OpensslVersionInfo = (u8, u8, u8, u8, u8); - const fn parse_version_info(mut n: i64) -> OpensslVersionInfo { - let status = (n & 0xF) as u8; - n >>= 4; - let patch = (n & 0xFF) as u8; - n >>= 8; - let fix = (n & 0xFF) as u8; - n >>= 8; - let minor = (n & 0xFF) as u8; - n >>= 8; - let major = (n & 0xFF) as u8; - (major, minor, fix, patch, status) - } - - #[derive(Copy, Clone, num_enum::IntoPrimitive, num_enum::TryFromPrimitive, PartialEq)] - #[repr(i32)] - enum SslVersion { - Ssl2, - Ssl3 = 1, - Tls, - Tls1, - Tls1_1, - Tls1_2, - TlsClient = 0x10, - TlsServer, + impl PySSLCertVerificationError {} + + // Helper functions to create SSL exceptions with proper errno attribute + pub(super) fn create_ssl_want_read_error(vm: &VirtualMachine) -> PyBaseExceptionRef { + // args = (errno, message) + vm.new_exception( + PySSLWantReadError::class(&vm.ctx).to_owned(), + vec![ + vm.ctx.new_int(SSL_ERROR_WANT_READ).into(), + vm.ctx + .new_str("The operation did not complete (read)") + .into(), + ], + ) } - #[derive(Copy, Clone, num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] - #[repr(i32)] - enum ProtoVersion { - MinSupported = -2, - Ssl3 = sys::SSL3_VERSION, - Tls1 = sys::TLS1_VERSION, - Tls1_1 = sys::TLS1_1_VERSION, - Tls1_2 = sys::TLS1_2_VERSION, - #[cfg(ossl111)] - Tls1_3 = sys::TLS1_3_VERSION, - #[cfg(not(ossl111))] - Tls1_3 = 0x304, - MaxSupported = -1, + pub(super) fn create_ssl_want_write_error(vm: &VirtualMachine) -> PyBaseExceptionRef { + // args = (errno, message) + vm.new_exception( + PySSLWantWriteError::class(&vm.ctx).to_owned(), + vec![ + vm.ctx.new_int(SSL_ERROR_WANT_WRITE).into(), + vm.ctx + .new_str("The operation did not complete (write)") + .into(), + ], + ) } - #[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)] - #[repr(i32)] - enum CertRequirements { - None, - Optional, - Required, + pub(crate) fn create_ssl_eof_error(vm: &VirtualMachine) -> PyBaseExceptionRef { + vm.new_exception_msg( + PySSLEOFError::class(&vm.ctx).to_owned(), + "EOF occurred in violation of protocol".to_owned(), + ) } - #[derive(Debug, PartialEq)] - enum SslServerOrClient { - Client, - Server, + pub(crate) fn create_ssl_zero_return_error(vm: &VirtualMachine) -> PyBaseExceptionRef { + vm.new_exception_msg( + PySSLZeroReturnError::class(&vm.ctx).to_owned(), + "TLS/SSL connection has been closed (EOF)".to_owned(), + ) } - unsafe fn ptr2obj(ptr: *mut sys::ASN1_OBJECT) -> Option { - if ptr.is_null() { - None - } else { - Some(unsafe { Asn1Object::from_ptr(ptr) }) + /// Validate server hostname for TLS SNI + /// + /// Checks that the hostname: + /// - Is not empty + /// - Does not start with a dot + /// - Is not an IP address (SNI requires DNS names) + /// - Does not contain null bytes + /// - Does not exceed 253 characters (DNS limit) + /// + /// Returns Ok(()) if validation passes, or an appropriate error. + fn validate_hostname(hostname: &str, vm: &VirtualMachine) -> PyResult<()> { + if hostname.is_empty() { + return Err(vm.new_value_error("server_hostname cannot be an empty string")); } - } - - fn _txt2obj(s: &CStr, no_name: bool) -> Option { - unsafe { ptr2obj(sys::OBJ_txt2obj(s.as_ptr(), i32::from(no_name))) } - } - fn _nid2obj(nid: Nid) -> Option { - unsafe { ptr2obj(sys::OBJ_nid2obj(nid.as_raw())) } - } - - type PyNid = (libc::c_int, String, String, Option); - fn obj2py(obj: &Asn1ObjectRef, vm: &VirtualMachine) -> PyResult { - let nid = obj.nid(); - let short_name = nid - .short_name() - .map_err(|_| vm.new_value_error("NID has no short name".to_owned()))? - .to_owned(); - let long_name = nid - .long_name() - .map_err(|_| vm.new_value_error("NID has no long name".to_owned()))? - .to_owned(); - Ok(( - nid.as_raw(), - short_name, - long_name, - cert::obj2txt(obj, true), - )) - } - - #[derive(FromArgs)] - struct Txt2ObjArgs { - txt: PyStrRef, - #[pyarg(any, default = false)] - name: bool, - } - #[pyfunction] - fn txt2obj(args: Txt2ObjArgs, vm: &VirtualMachine) -> PyResult { - _txt2obj(&args.txt.to_cstring(vm)?, !args.name) - .as_deref() - .ok_or_else(|| vm.new_value_error(format!("unknown object '{}'", args.txt))) - .and_then(|obj| obj2py(obj, vm)) - } + if hostname.starts_with('.') { + return Err(vm.new_value_error("server_hostname cannot start with a dot")); + } - #[pyfunction] - fn nid2obj(nid: libc::c_int, vm: &VirtualMachine) -> PyResult { - _nid2obj(Nid::from_raw(nid)) - .as_deref() - .ok_or_else(|| vm.new_value_error(format!("unknown NID {nid}"))) - .and_then(|obj| obj2py(obj, vm)) - } + if hostname.parse::().is_ok() { + return Err(vm.new_value_error("server_hostname cannot be an IP address")); + } - // Lazily compute and cache cert file/dir paths - static CERT_PATHS: LazyLock<(PathBuf, PathBuf)> = LazyLock::new(|| { - fn path_from_cstr(c: &CStr) -> PathBuf { - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - std::ffi::OsStr::from_bytes(c.to_bytes()).into() - } - #[cfg(windows)] - { - // Use lossy conversion for potential non-UTF8 - PathBuf::from(c.to_string_lossy().as_ref()) - } + if hostname.contains('\0') { + return Err(vm.new_type_error("embedded null character")); } - let probe = probe(); - let cert_file = probe - .cert_file - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| { - path_from_cstr(unsafe { CStr::from_ptr(sys::X509_get_default_cert_file()) }) - }); - let cert_dir = probe - .cert_dir - .as_ref() - .map(PathBuf::from) - .unwrap_or_else(|| { - path_from_cstr(unsafe { CStr::from_ptr(sys::X509_get_default_cert_dir()) }) - }); - (cert_file, cert_dir) - }); + if hostname.len() > 253 { + return Err(vm.new_value_error("server_hostname is too long (maximum 253 characters)")); + } - fn get_cert_file_dir() -> (&'static Path, &'static Path) { - let (cert_file, cert_dir) = &*CERT_PATHS; - (cert_file.as_path(), cert_dir.as_path()) + Ok(()) } - // Lazily compute and cache cert environment variable names - static CERT_ENV_NAMES: LazyLock<(String, String)> = LazyLock::new(|| { - let cert_file_env = unsafe { CStr::from_ptr(sys::X509_get_default_cert_file_env()) } - .to_string_lossy() - .into_owned(); - let cert_dir_env = unsafe { CStr::from_ptr(sys::X509_get_default_cert_dir_env()) } - .to_string_lossy() - .into_owned(); - (cert_file_env, cert_dir_env) - }); - - #[pyfunction] - fn get_default_verify_paths( - vm: &VirtualMachine, - ) -> PyResult<(&'static str, PyObjectRef, &'static str, PyObjectRef)> { - let (cert_file_env, cert_dir_env) = &*CERT_ENV_NAMES; - let (cert_file, cert_dir) = get_cert_file_dir(); - let cert_file = OsPath::new_str(cert_file).filename(vm); - let cert_dir = OsPath::new_str(cert_dir).filename(vm); - Ok(( - cert_file_env.as_str(), - cert_file, - cert_dir_env.as_str(), - cert_dir, - )) + // SNI certificate resolver that uses shared mutable state + // The Python SNI callback updates this state, and resolve() reads from it + #[derive(Debug)] + struct SniCertResolver { + // SNI state: (certificate, server_name) + sni_state: Arc>, } - #[pyfunction(name = "RAND_status")] - fn rand_status() -> i32 { - unsafe { sys::RAND_status() } - } + impl ResolvesServerCert for SniCertResolver { + fn resolve(&self, client_hello: ClientHello<'_>) -> Option> { + let mut state = self.sni_state.lock(); - #[pyfunction(name = "RAND_add")] - fn rand_add(string: ArgStrOrBytesLike, entropy: f64) { - let f = |b: &[u8]| { - for buf in b.chunks(libc::c_int::MAX as usize) { - unsafe { sys::RAND_add(buf.as_ptr() as *const _, buf.len() as _, entropy) } + // Extract and store SNI from client hello for later use + if let Some(sni) = client_hello.server_name() { + state.1 = Some(sni.to_string()); + } else { + state.1 = None; } - }; - f(&string.borrow_bytes()) - } - #[pyfunction(name = "RAND_bytes")] - fn rand_bytes(n: i32, vm: &VirtualMachine) -> PyResult> { - if n < 0 { - return Err(vm.new_value_error("num must be positive")); + // Return the current certificate (may have been updated by Python callback) + Some(state.0.clone()) } - let mut buf = vec![0; n as usize]; - openssl::rand::rand_bytes(&mut buf).map_err(|e| convert_openssl_error(vm, e))?; - Ok(buf) } - // Callback data stored in SSL context for SNI - struct SniCallbackData { - ssl_context: PyRef, - vm_ptr: *const VirtualMachine, + // Session data structure for tracking TLS sessions + #[derive(Debug, Clone)] + struct SessionData { + #[allow(dead_code)] + server_name: String, + session_id: Vec, + creation_time: SystemTime, + lifetime: u64, } - impl Drop for SniCallbackData { - fn drop(&mut self) { - // PyRef will handle reference counting - } + // Type alias to simplify complex session cache type + type SessionCache = Arc, Arc>>>>; + + // Type alias for SNI state + type SniCertName = (Arc, Option); + + // SESSION EMULATION IMPLEMENTATION + // + // IMPORTANT: This is an EMULATION of CPython's SSL session management. + // Rustls 0.23 does NOT expose session data (ticket bytes, session IDs, etc.) + // through public APIs. All session value fields are private. + // + // LIMITATIONS: + // - Session IDs are generated from metadata (server name + timestamp hash) + // NOT actual TLS session IDs + // - Ticket data is not stored (Rustls keeps it internally) + // - Session resumption works (via Rustls's automatic mechanism) + // but we can't access the actual session state + // + // This implementation provides: + // ✓ session.id - synthetic ID based on metadata + // ✓ session.time - creation timestamp + // ✓ session.timeout - default lifetime value + // ✓ session.has_ticket - always True when session exists + // ✓ session_reused - tracked via handshake_kind() + // ✗ Actual TLS session ID/ticket data - NOT ACCESSIBLE + + // Generate a synthetic session ID from server name and timestamp + // NOTE: This is NOT the actual TLS session ID, just a unique identifier + fn generate_session_id_from_metadata(server_name: &str, time: &SystemTime) -> Vec { + let mut hasher = Sha256::new(); + hasher.update(server_name.as_bytes()); + hasher.update( + time.duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() + .to_le_bytes(), + ); + hasher.finalize()[..16].to_vec() } - // Get or create an ex_data index for SNI callback data - fn get_sni_ex_data_index() -> libc::c_int { - use std::sync::LazyLock; - static SNI_EX_DATA_IDX: LazyLock = LazyLock::new(|| unsafe { - sys::SSL_get_ex_new_index( - 0, - std::ptr::null_mut(), - None, - None, - Some(sni_callback_data_free), - ) - }); - *SNI_EX_DATA_IDX + // Custom ClientSessionStore that tracks session metadata for Python access + // NOTE: This wraps ClientSessionMemoryCache and records metadata when sessions are stored + #[derive(Debug)] + struct PythonClientSessionStore { + inner: Arc, + session_cache: SessionCache, } - // Free function for callback data - unsafe extern "C" fn sni_callback_data_free( - _parent: *mut libc::c_void, - ptr: *mut libc::c_void, - _ad: *mut sys::CRYPTO_EX_DATA, - _idx: libc::c_int, - _argl: libc::c_long, - _argp: *mut libc::c_void, - ) { - if !ptr.is_null() { - unsafe { - let _ = Box::from_raw(ptr as *mut SniCallbackData); - } + impl ClientSessionStore for PythonClientSessionStore { + fn set_kx_hint(&self, server_name: ServerName<'static>, group: rustls::NamedGroup) { + self.inner.set_kx_hint(server_name, group); } - } - // SNI callback function called by OpenSSL - unsafe extern "C" fn _servername_callback( - ssl_ptr: *mut sys::SSL, - _al: *mut libc::c_int, - arg: *mut libc::c_void, - ) -> libc::c_int { - const SSL_TLSEXT_ERR_OK: libc::c_int = 0; - const SSL_TLSEXT_ERR_ALERT_FATAL: libc::c_int = 2; - const TLSEXT_NAMETYPE_host_name: libc::c_int = 0; - - if arg.is_null() { - return SSL_TLSEXT_ERR_OK; + fn kx_hint(&self, server_name: &ServerName<'_>) -> Option { + self.inner.kx_hint(server_name) } - unsafe { - let ctx = &*(arg as *const PySslContext); - - // Get the callback - let callback_opt = ctx.sni_callback.lock().clone(); - let Some(callback) = callback_opt else { - return SSL_TLSEXT_ERR_OK; - }; - - // Get callback data from SSL ex_data - let idx = get_sni_ex_data_index(); - let data_ptr = sys::SSL_get_ex_data(ssl_ptr, idx); - if data_ptr.is_null() { - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - - let callback_data = &*(data_ptr as *const SniCallbackData); - - // SAFETY: vm_ptr is stored during wrap_socket and is valid for the lifetime - // of the SSL connection. The handshake happens synchronously in the same thread. - let vm = &*callback_data.vm_ptr; - - // Get server name - let servername = sys::SSL_get_servername(ssl_ptr, TLSEXT_NAMETYPE_host_name); - let server_name_arg = if servername.is_null() { - vm.ctx.none() - } else { - let name_cstr = std::ffi::CStr::from_ptr(servername); - match name_cstr.to_str() { - Ok(name_str) => vm.ctx.new_str(name_str).into(), - Err(_) => vm.ctx.none(), - } - }; - - // Get SSL socket from SSL ex_data (stored as PySslSocket pointer) - let ssl_socket_ptr = sys::SSL_get_ex_data(ssl_ptr, 0); // Index 0 for SSL socket - let ssl_socket_obj = if !ssl_socket_ptr.is_null() { - let ssl_socket = &*(ssl_socket_ptr as *const PySslSocket); - // Try to get owner first - ssl_socket - .owner - .read() - .as_ref() - .and_then(|weak| weak.upgrade()) - .unwrap_or_else(|| vm.ctx.none()) - } else { - vm.ctx.none() + fn set_tls12_session( + &self, + server_name: ServerName<'static>, + value: rustls::client::Tls12ClientSessionValue, + ) { + // Store in inner cache for actual resumption (Rustls handles this) + self.inner.set_tls12_session(server_name.clone(), value); + + // Record metadata in Python-accessible cache + // NOTE: We can't access value.session_id or value.ticket (private fields) + // So we generate a synthetic ID from metadata + let creation_time = SystemTime::now(); + let server_name_str = server_name.to_str(); + let session_data = SessionData { + server_name: server_name_str.as_ref().to_string(), + session_id: generate_session_id_from_metadata( + server_name_str.as_ref(), + &creation_time, + ), + creation_time, + lifetime: 7200, // TLS 1.2 default session lifetime }; - // Call the Python callback - match callback.call( - ( - ssl_socket_obj, - server_name_arg, - callback_data.ssl_context.to_owned(), - ), - vm, - ) { - Ok(_) => SSL_TLSEXT_ERR_OK, - Err(exc) => { - // Log the exception but don't propagate it - vm.run_unraisable(exc, None, vm.ctx.none()); - SSL_TLSEXT_ERR_ALERT_FATAL - } - } + let key = server_name_str.as_bytes().to_vec(); + self.session_cache + .write() + .insert(key, Arc::new(ParkingMutex::new(session_data))); } - } - #[pyfunction(name = "RAND_pseudo_bytes")] - fn rand_pseudo_bytes(n: i32, vm: &VirtualMachine) -> PyResult<(Vec, bool)> { - if n < 0 { - return Err(vm.new_value_error("num must be positive")); - } - let mut buf = vec![0; n as usize]; - let ret = unsafe { sys::RAND_bytes(buf.as_mut_ptr(), n) }; - match ret { - 0 | 1 => Ok((buf, ret == 1)), - _ => Err(convert_openssl_error(vm, ErrorStack::get())), + fn tls12_session( + &self, + server_name: &ServerName<'_>, + ) -> Option { + self.inner.tls12_session(server_name) } - } - #[pyattr] - #[pyclass(module = "ssl", name = "_SSLContext")] - #[derive(PyPayload)] - struct PySslContext { - ctx: PyRwLock, - check_hostname: AtomicCell, - protocol: SslVersion, - post_handshake_auth: PyMutex, - sni_callback: PyMutex>, - } + fn remove_tls12_session(&self, server_name: &ServerName<'static>) { + self.inner.remove_tls12_session(server_name); - impl fmt::Debug for PySslContext { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("_SSLContext") + // Also remove from Python cache + let key = server_name.to_str().as_bytes().to_vec(); + self.session_cache.write().remove(&key); } - } - - fn builder_as_ctx(x: &SslContextBuilder) -> &ssl::SslContextRef { - unsafe { ssl::SslContextRef::from_ptr(x.as_ptr()) } - } - impl Constructor for PySslContext { - type Args = i32; - - fn py_new(cls: PyTypeRef, proto_version: Self::Args, vm: &VirtualMachine) -> PyResult { - let proto = SslVersion::try_from(proto_version) - .map_err(|_| vm.new_value_error("invalid protocol version"))?; - let method = match proto { - // SslVersion::Ssl3 => unsafe { ssl::SslMethod::from_ptr(sys::SSLv3_method()) }, - SslVersion::Tls => ssl::SslMethod::tls(), - SslVersion::Tls1 => ssl::SslMethod::tls(), - SslVersion::Tls1_1 => ssl::SslMethod::tls(), - SslVersion::Tls1_2 => ssl::SslMethod::tls(), - SslVersion::TlsClient => ssl::SslMethod::tls_client(), - SslVersion::TlsServer => ssl::SslMethod::tls_server(), - _ => return Err(vm.new_value_error("invalid protocol version")), + fn insert_tls13_ticket( + &self, + server_name: ServerName<'static>, + value: rustls::client::Tls13ClientSessionValue, + ) { + // Store in inner cache for actual resumption (Rustls handles this) + self.inner.insert_tls13_ticket(server_name.clone(), value); + + // Record metadata in Python-accessible cache + // NOTE: We can't access value.ticket or value.lifetime_secs (private fields) + // So we use default values + let creation_time = SystemTime::now(); + let server_name_str = server_name.to_str(); + let session_data = SessionData { + server_name: server_name_str.to_string(), + session_id: generate_session_id_from_metadata( + server_name_str.as_ref(), + &creation_time, + ), + creation_time, + lifetime: 7200, // Default TLS 1.3 ticket lifetime (Rustls uses this) }; - let mut builder = - SslContextBuilder::new(method).map_err(|e| convert_openssl_error(vm, e))?; - - #[cfg(target_os = "android")] - android::load_client_ca_list(vm, &mut builder)?; - - let check_hostname = proto == SslVersion::TlsClient; - builder.set_verify(if check_hostname { - SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT - } else { - SslVerifyMode::NONE - }); - let mut options = SslOptions::ALL & !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; - if proto != SslVersion::Ssl2 { - options |= SslOptions::NO_SSLV2; - } - if proto != SslVersion::Ssl3 { - options |= SslOptions::NO_SSLV3; - } - options |= SslOptions::NO_COMPRESSION; - options |= SslOptions::CIPHER_SERVER_PREFERENCE; - options |= SslOptions::SINGLE_DH_USE; - options |= SslOptions::SINGLE_ECDH_USE; - options |= SslOptions::ENABLE_MIDDLEBOX_COMPAT; - builder.set_options(options); + let key = server_name_str.as_bytes().to_vec(); + self.session_cache + .write() + .insert(key, Arc::new(ParkingMutex::new(session_data))); + } - let mode = ssl::SslMode::ACCEPT_MOVING_WRITE_BUFFER | ssl::SslMode::AUTO_RETRY; - builder.set_mode(mode); + fn take_tls13_ticket( + &self, + server_name: &ServerName<'static>, + ) -> Option { + self.inner.take_tls13_ticket(server_name) + } + } - #[cfg(ossl111)] - unsafe { - sys::SSL_CTX_set_post_handshake_auth(builder.as_ptr(), 0); + /// Parse length-prefixed ALPN protocol list + /// + /// Format: [len1, proto1..., len2, proto2..., ...] + /// + /// This is the wire format used by Python's ssl.py when calling _set_alpn_protocols(). + /// Each protocol is prefixed with a single byte indicating its length. + /// + /// # Arguments + /// * `bytes` - The length-prefixed protocol data + /// * `vm` - VirtualMachine for error creation + /// + /// # Returns + /// * `Ok(Vec>)` - List of protocol names as byte vectors + /// * `Err(PyBaseExceptionRef)` - ValueError with detailed error message + fn parse_length_prefixed_alpn(bytes: &[u8], vm: &VirtualMachine) -> PyResult>> { + let mut alpn_list = Vec::new(); + let mut offset = 0; + + while offset < bytes.len() { + // Check if we can read the length byte + if offset + 1 > bytes.len() { + return Err(vm.new_value_error(format!( + "Invalid ALPN protocol data: unexpected end at offset {offset}", + ))); } - builder - .set_session_id_context(b"Python") - .map_err(|e| convert_openssl_error(vm, e))?; + let proto_len = bytes[offset] as usize; + offset += 1; - // Set protocol version limits based on the protocol version - unsafe { - let ctx_ptr = builder.as_ptr(); - match proto { - SslVersion::Tls1 => { - sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_VERSION); - sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_VERSION); - } - SslVersion::Tls1_1 => { - sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_1_VERSION); - sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_1_VERSION); - } - SslVersion::Tls1_2 => { - sys::SSL_CTX_set_min_proto_version(ctx_ptr, sys::TLS1_2_VERSION); - sys::SSL_CTX_set_max_proto_version(ctx_ptr, sys::TLS1_2_VERSION); - } - _ => { - // For Tls, TlsClient, TlsServer, use default (no restrictions) - } - } + // Validate protocol length + if proto_len == 0 { + return Err(vm.new_value_error(format!( + "Invalid ALPN protocol data: protocol length cannot be 0 at offset {}", + offset - 1 + ))); } - // Set default verify flags: VERIFY_X509_TRUSTED_FIRST - unsafe { - let ctx_ptr = builder.as_ptr(); - let param = sys::SSL_CTX_get0_param(ctx_ptr); - sys::X509_VERIFY_PARAM_set_flags(param, sys::X509_V_FLAG_TRUSTED_FIRST); + // Check if we have enough bytes for the protocol data + if offset + proto_len > bytes.len() { + return Err(vm.new_value_error(format!( + "Invalid ALPN protocol data: expected {} bytes at offset {}, but only {} bytes remain", + proto_len, offset, bytes.len() - offset + ))); } - PySslContext { - ctx: PyRwLock::new(builder), - check_hostname: AtomicCell::new(check_hostname), - protocol: proto, - post_handshake_auth: PyMutex::new(false), - sni_callback: PyMutex::new(None), - } - .into_ref_with_type(vm, cls) - .map(Into::into) + // Extract protocol bytes + let proto = bytes[offset..offset + proto_len].to_vec(); + alpn_list.push(proto); + offset += proto_len; } + + Ok(alpn_list) } - #[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor))] - impl PySslContext { - fn builder(&self) -> PyRwLockWriteGuard<'_, SslContextBuilder> { - self.ctx.write() - } - fn ctx(&self) -> PyMappedRwLockReadGuard<'_, ssl::SslContextRef> { - PyRwLockReadGuard::map(self.ctx.read(), builder_as_ctx) + /// Parse OpenSSL cipher string to rustls SupportedCipherSuite list + /// + /// Supports patterns like: + /// - "AES128" → filters for AES_128 + /// - "AES256" → filters for AES_256 + /// - "AES128:AES256" → both + /// - "ECDHE+AESGCM" → ECDHE AND AESGCM (both conditions must match) + /// - "ALL" or "DEFAULT" → all available + /// - "!MD5" → exclusion (ignored, rustls doesn't support weak ciphers anyway) + fn parse_cipher_string(cipher_str: &str) -> Result, String> { + use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES; + + if cipher_str.is_empty() { + return Err("No cipher can be selected".to_string()); } - #[pygetset] - fn post_handshake_auth(&self) -> bool { - *self.post_handshake_auth.lock() - } - #[pygetset(setter)] - fn set_post_handshake_auth( - &self, - value: Option, - vm: &VirtualMachine, - ) -> PyResult<()> { - let value = value.ok_or_else(|| vm.new_attribute_error("cannot delete attribute"))?; - *self.post_handshake_auth.lock() = value.is_true(vm)?; - Ok(()) - } + let all_suites = ALL_CIPHER_SUITES; + let mut selected = Vec::new(); - #[cfg(ossl110)] - #[pygetset] - fn security_level(&self) -> i32 { - unsafe { SSL_CTX_get_security_level(self.ctx().as_ptr()) } - } + for part in cipher_str.split(':') { + let part = part.trim(); - #[pymethod] - fn set_ciphers(&self, cipherlist: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { - let ciphers = cipherlist.as_str(); - if ciphers.contains('\0') { - return Err(exceptions::cstring_error(vm)); + // Skip exclusions (rustls doesn't support these) + if part.starts_with('!') { + continue; } - self.builder().set_cipher_list(ciphers).map_err(|_| { - vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "No cipher can be selected.".to_owned(), - ) - }) - } - - #[pymethod] - fn get_ciphers(&self, vm: &VirtualMachine) -> PyResult { - let ctx = self.ctx(); - let ssl = ssl::Ssl::new(&ctx).map_err(|e| convert_openssl_error(vm, e))?; - - unsafe { - let ciphers_ptr = SSL_get_ciphers(ssl.as_ptr()); - if ciphers_ptr.is_null() { - return Ok(vm.ctx.new_list(vec![])); - } - - let num_ciphers = sys::OPENSSL_sk_num(ciphers_ptr as *const _); - let mut result = Vec::new(); - - for i in 0..num_ciphers { - let cipher_ptr = - sys::OPENSSL_sk_value(ciphers_ptr as *const _, i) as *const sys::SSL_CIPHER; - let cipher = ssl::SslCipherRef::from_ptr(cipher_ptr as *mut _); - - let (name, version, bits) = cipher_to_tuple(cipher); - let dict = vm.ctx.new_dict(); - dict.set_item("name", vm.ctx.new_str(name).into(), vm)?; - dict.set_item("protocol", vm.ctx.new_str(version).into(), vm)?; - dict.set_item("secret_bits", vm.ctx.new_int(bits).into(), vm)?; - // Add description field - let description = cipher_description(cipher_ptr); - dict.set_item("description", vm.ctx.new_str(description).into(), vm)?; + // Skip priority markers starting with + + if part.starts_with('+') { + continue; + } - result.push(dict.into()); + // Match pattern + match part { + "ALL" | "DEFAULT" | "HIGH" => { + // Add all available cipher suites + selected.extend_from_slice(all_suites); } + _ => { + // Check if this is a compound pattern with + (AND condition) + // e.g., "ECDHE+AESGCM" means ECDHE AND AESGCM + let patterns: Vec<&str> = part.split('+').collect(); + + let mut found_any = false; + for suite in all_suites { + let name = format!("{:?}", suite.suite()); + + // Check if all patterns match (AND condition) + let matches = patterns.iter().all(|&pattern| { + // Handle common OpenSSL pattern variations + if pattern.contains("AES128") { + name.contains("AES_128") + } else if pattern.contains("AES256") { + name.contains("AES_256") + } else if pattern == "AESGCM" { + // AESGCM: AES with GCM mode + name.contains("AES") && name.contains("GCM") + } else if pattern == "AESCCM" { + // AESCCM: AES with CCM mode + name.contains("AES") && name.contains("CCM") + } else if pattern == "CHACHA20" { + name.contains("CHACHA20") + } else if pattern == "ECDHE" { + name.contains("ECDHE") + } else if pattern == "DHE" { + // DHE but not ECDHE + name.contains("DHE") && !name.contains("ECDHE") + } else if pattern == "ECDH" { + // ECDH but not ECDHE + name.contains("ECDH") && !name.contains("ECDHE") + } else if pattern == "DH" { + // DH but not DHE or ECDH + name.contains("DH") + && !name.contains("DHE") + && !name.contains("ECDH") + } else if pattern == "RSA" { + name.contains("RSA") + } else if pattern == "AES" { + name.contains("AES") + } else if pattern == "ECDSA" { + name.contains("ECDSA") + } else { + // Direct substring match for other patterns + name.contains(pattern) + } + }); - Ok(vm.ctx.new_list(result)) - } - } - - #[pymethod] - fn set_ecdh_curve( - &self, - name: Either, - vm: &VirtualMachine, - ) -> PyResult<()> { - use openssl::ec::{EcGroup, EcKey}; + if matches { + selected.push(*suite); + found_any = true; + } + } - // Convert name to CString, supporting both str and bytes - let name_cstr = match name { - Either::A(s) => { - if s.as_str().contains('\0') { - return Err(exceptions::cstring_error(vm)); + if !found_any { + // No matching cipher suite found - warn but continue } - s.to_cstring(vm)? } - Either::B(b) => std::ffi::CString::new(b.borrow_buf().to_vec()) - .map_err(|_| exceptions::cstring_error(vm))?, - }; - - // Find the NID for the curve name using OBJ_sn2nid - let nid_raw = unsafe { sys::OBJ_sn2nid(name_cstr.as_ptr()) }; - if nid_raw == 0 { - return Err(vm.new_value_error("unknown curve name")); } - let nid = Nid::from_raw(nid_raw); + } - // Create EC key from the curve - let group = EcGroup::from_curve_name(nid).map_err(|e| convert_openssl_error(vm, e))?; - let key = EcKey::from_group(&group).map_err(|e| convert_openssl_error(vm, e))?; + // Remove duplicates + selected.dedup_by_key(|s| s.suite()); - // Set the temporary ECDH key - self.builder() - .set_tmp_ecdh(&key) - .map_err(|e| convert_openssl_error(vm, e)) + if selected.is_empty() { + Err("No cipher can be selected".to_string()) + } else { + Ok(selected) + } + } + + // SSLContext - manages TLS configuration + #[pyattr] + #[pyclass(name = "_SSLContext", module = "ssl", traverse)] + #[derive(Debug, PyPayload)] + struct PySSLContext { + #[pytraverse(skip)] + protocol: i32, + #[pytraverse(skip)] + check_hostname: PyRwLock, + #[pytraverse(skip)] + verify_mode: PyRwLock, + #[pytraverse(skip)] + verify_flags: PyRwLock, + // Rustls configuration (built lazily) + #[allow(dead_code)] + #[pytraverse(skip)] + client_config: PyRwLock>>, + #[allow(dead_code)] + #[pytraverse(skip)] + server_config: PyRwLock>>, + // Certificate store + #[pytraverse(skip)] + root_certs: PyRwLock, + // Store full CA certificates for get_ca_certs() + // RootCertStore only keeps TrustAnchors, not full certificates + #[pytraverse(skip)] + ca_certs_der: PyRwLock>>, + // Store CA certificates from capath for lazy loading simulation + // (CPython only returns these in get_ca_certs() after they're used in handshake) + #[pytraverse(skip)] + capath_certs_der: PyRwLock>>, + // Certificate Revocation Lists for CRL checking + #[pytraverse(skip)] + crls: PyRwLock>>, + // Server certificate/key pairs (supports multiple for RSA+ECC dual mode) + // OpenSSL allows multiple cert/key pairs to be loaded, and selects the appropriate + // one based on client capabilities during handshake + // Stored as (CertifiedKey, PrivateKeyDer) to support both server and client usage + #[pytraverse(skip)] + cert_keys: PyRwLock>, + // Options + #[allow(dead_code)] + #[pytraverse(skip)] + options: PyRwLock, + // ALPN protocols + #[allow(dead_code)] + #[pytraverse(skip)] + alpn_protocols: PyRwLock>>, + // ALPN strict matching flag + // When false (default), mimics OpenSSL behavior: no ALPN negotiation failure + // When true, requires ALPN match (Rustls default behavior) + #[allow(dead_code)] + #[pytraverse(skip)] + require_alpn_match: PyRwLock, + // TLS 1.3 features + #[pytraverse(skip)] + post_handshake_auth: PyRwLock, + #[pytraverse(skip)] + num_tickets: PyRwLock, + // Protocol version limits + #[pytraverse(skip)] + minimum_version: PyRwLock, + #[pytraverse(skip)] + maximum_version: PyRwLock, + // SNI callback for server-side (contains PyObjectRef - needs GC tracking) + sni_callback: PyRwLock>, + // Message callback for debugging (contains PyObjectRef - needs GC tracking) + msg_callback: PyRwLock>, + // ECDH curve name for key exchange + #[pytraverse(skip)] + ecdh_curve: PyRwLock>, + // Certificate statistics for cert_store_stats() + #[pytraverse(skip)] + ca_cert_count: PyRwLock, // Number of CA certificates + #[pytraverse(skip)] + x509_cert_count: PyRwLock, // Total number of certificates + // Session management + #[pytraverse(skip)] + client_session_cache: SessionCache, + // Rustls session store for actual TLS session resumption + #[pytraverse(skip)] + rustls_session_store: Arc, + // Rustls server session store for server-side session resumption + #[pytraverse(skip)] + rustls_server_session_store: Arc, + // Shared ticketer for TLS 1.2 session tickets + #[pytraverse(skip)] + server_ticketer: Arc, + // Server-side session statistics + #[pytraverse(skip)] + accept_count: AtomicUsize, // Total number of accepts + #[pytraverse(skip)] + session_hits: AtomicUsize, // Number of session reuses + // Cipher suite selection + /// Selected cipher suites (None = use all rustls defaults) + #[pytraverse(skip)] + selected_ciphers: PyRwLock>>, + } + + #[derive(FromArgs)] + struct WrapSocketArgs { + sock: PyObjectRef, + server_side: bool, + #[pyarg(positional, optional)] + server_hostname: OptionalArg>, + #[pyarg(named, optional)] + owner: OptionalArg, + #[pyarg(named, optional)] + session: OptionalArg, + } + + #[derive(FromArgs)] + struct WrapBioArgs { + incoming: PyRef, + outgoing: PyRef, + #[pyarg(named, optional)] + server_side: OptionalArg, + #[pyarg(named, optional)] + server_hostname: OptionalArg>, + #[pyarg(named, optional)] + owner: OptionalArg, + #[pyarg(named, optional)] + session: OptionalArg, + } + + #[derive(FromArgs)] + struct LoadVerifyLocationsArgs { + #[pyarg(any, optional)] + cafile: OptionalArg>, + #[pyarg(any, optional)] + capath: OptionalArg>, + #[pyarg(any, optional)] + cadata: OptionalArg, + } + + #[derive(FromArgs)] + struct LoadCertChainArgs { + #[pyarg(any)] + certfile: PyObjectRef, + #[pyarg(any, optional)] + keyfile: OptionalArg>, + #[pyarg(any, optional)] + password: OptionalArg, + } + + #[pyclass(with(Constructor), flags(BASETYPE))] + impl PySSLContext { + // Helper method to convert DER certificate bytes to Python dict + fn cert_der_to_dict(&self, vm: &VirtualMachine, cert_der: &[u8]) -> PyResult { + cert::cert_der_to_dict_helper(vm, cert_der) + } + + #[pymethod] + fn __repr__(&self) -> String { + format!("", self.protocol) + } + + #[pygetset] + fn check_hostname(&self) -> bool { + *self.check_hostname.read() + } + + #[pygetset(setter)] + fn set_check_hostname(&self, value: bool) { + *self.check_hostname.write() = value; + // When check_hostname is enabled, ensure verify_mode is at least CERT_REQUIRED + if value { + let current_verify_mode = *self.verify_mode.read(); + if current_verify_mode == CERT_NONE { + *self.verify_mode.write() = CERT_REQUIRED; + } + } } #[pygetset] - fn options(&self) -> libc::c_ulong { - self.ctx.read().options().bits() as _ + fn verify_mode(&self) -> i32 { + *self.verify_mode.read() } + #[pygetset(setter)] - fn set_options(&self, opts: libc::c_ulong) { - self.builder() - .set_options(SslOptions::from_bits_truncate(opts as _)); + fn set_verify_mode(&self, mode: i32, vm: &VirtualMachine) -> PyResult<()> { + if !(CERT_NONE..=CERT_REQUIRED).contains(&mode) { + return Err(vm.new_value_error("invalid verify mode")); + } + // Cannot set CERT_NONE when check_hostname is enabled + if mode == CERT_NONE && *self.check_hostname.read() { + return Err(vm.new_value_error( + "Cannot set verify_mode to CERT_NONE when check_hostname is enabled", + )); + } + *self.verify_mode.write() = mode; + Ok(()) } + #[pygetset] fn protocol(&self) -> i32 { - self.protocol as i32 + self.protocol } + #[pygetset] - fn verify_mode(&self) -> i32 { - let mode = self.ctx().verify_mode(); - if mode == SslVerifyMode::NONE { - CertRequirements::None.into() - } else if mode == SslVerifyMode::PEER { - CertRequirements::Optional.into() - } else if mode == SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT { - CertRequirements::Required.into() - } else { - unreachable!() - } + fn verify_flags(&self) -> i32 { + *self.verify_flags.read() } + #[pygetset(setter)] - fn set_verify_mode(&self, cert: i32, vm: &VirtualMachine) -> PyResult<()> { - let mut ctx = self.builder(); - let cert_req = CertRequirements::try_from(cert) - .map_err(|_| vm.new_value_error("invalid value for verify_mode"))?; - let mode = match cert_req { - CertRequirements::None if self.check_hostname.load() => { - return Err(vm.new_value_error( - "Cannot set verify_mode to CERT_NONE when check_hostname is enabled.", - )); - } - CertRequirements::None => SslVerifyMode::NONE, - CertRequirements::Optional => SslVerifyMode::PEER, - CertRequirements::Required => { - SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT - } - }; - ctx.set_verify(mode); - Ok(()) + fn set_verify_flags(&self, value: i32) { + *self.verify_flags.write() = value; } + #[pygetset] - fn verify_flags(&self) -> libc::c_ulong { - unsafe { - let ctx_ptr = self.ctx().as_ptr(); - let param = sys::SSL_CTX_get0_param(ctx_ptr); - sys::X509_VERIFY_PARAM_get_flags(param) - } + fn post_handshake_auth(&self) -> bool { + *self.post_handshake_auth.read() } + #[pygetset(setter)] - fn set_verify_flags(&self, new_flags: libc::c_ulong, vm: &VirtualMachine) -> PyResult<()> { - unsafe { - let ctx_ptr = self.ctx().as_ptr(); - let param = sys::SSL_CTX_get0_param(ctx_ptr); - let flags = sys::X509_VERIFY_PARAM_get_flags(param); - let clear = flags & !new_flags; - let set = !flags & new_flags; - - if clear != 0 && sys::X509_VERIFY_PARAM_clear_flags(param, clear) == 0 { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Failed to clear verify flags".to_owned(), - )); - } - if set != 0 && sys::X509_VERIFY_PARAM_set_flags(param, set) == 0 { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Failed to set verify flags".to_owned(), - )); - } - Ok(()) + fn set_post_handshake_auth(&self, value: bool) { + *self.post_handshake_auth.write() = value; + } + + #[pygetset] + fn num_tickets(&self) -> i32 { + *self.num_tickets.read() + } + + #[pygetset(setter)] + fn set_num_tickets(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { + if value < 0 { + return Err(vm.new_value_error("num_tickets must be a non-negative integer")); } + if self.protocol != PROTOCOL_TLS_SERVER { + return Err( + vm.new_value_error("num_tickets can only be set on server-side contexts") + ); + } + *self.num_tickets.write() = value; + Ok(()) } + #[pygetset] - fn check_hostname(&self) -> bool { - self.check_hostname.load() + fn options(&self) -> i32 { + *self.options.read() } + #[pygetset(setter)] - fn set_check_hostname(&self, ch: bool) { - let mut ctx = self.builder(); - if ch && builder_as_ctx(&ctx).verify_mode() == SslVerifyMode::NONE { - ctx.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT); + fn set_options(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { + // Validate that the value is non-negative + if value < 0 { + return Err(vm.new_overflow_error("options must be non-negative".to_owned())); } - self.check_hostname.store(ch); + + // Deprecated SSL/TLS protocol version options + let opt_no = OP_NO_SSLv2 + | OP_NO_SSLv3 + | OP_NO_TLSv1 + | OP_NO_TLSv1_1 + | OP_NO_TLSv1_2 + | OP_NO_TLSv1_3; + + // Get current options and calculate newly set bits + let old_opts = *self.options.read(); + let set = !old_opts & value; // Bits being newly set + + // Warn if any deprecated options are being newly set + if (set & opt_no) != 0 { + warnings::warn( + vm.ctx.exceptions.deprecation_warning, + "ssl.OP_NO_SSL*/ssl.OP_NO_TLS* options are deprecated".to_owned(), + 2, // stack_level = 2 + vm, + )?; + } + + *self.options.write() = value; + Ok(()) } - // PY_PROTO_MINIMUM_SUPPORTED = -2, PY_PROTO_MAXIMUM_SUPPORTED = -1 #[pygetset] fn minimum_version(&self) -> i32 { - let ctx = self.ctx(); - let version = unsafe { sys::SSL_CTX_get_min_proto_version(ctx.as_ptr()) }; - if version == 0 { - -2 // PY_PROTO_MINIMUM_SUPPORTED - } else { - version - } + let v = *self.minimum_version.read(); + // return MINIMUM_SUPPORTED if value is 0 + if v == 0 { PROTO_MINIMUM_SUPPORTED } else { v } } + #[pygetset(setter)] fn set_minimum_version(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { - // Handle special values - let proto_version = match value { - -2 => { - // PY_PROTO_MINIMUM_SUPPORTED -> use minimum available (TLS 1.2) - sys::TLS1_2_VERSION - } - -1 => { - // PY_PROTO_MAXIMUM_SUPPORTED -> use maximum available - // For max on min_proto_version, we use the newest available - sys::TLS1_3_VERSION - } + // Validate that the value is a valid TLS version constant + // Valid values: 0 (default), -2 (MINIMUM_SUPPORTED), -1 (MAXIMUM_SUPPORTED), + // or 0x0300-0x0304 (SSLv3-TLSv1.3) + if value != 0 + && value != -2 + && value != -1 + && !(PROTO_SSLv3..=PROTO_TLSv1_3).contains(&value) + { + return Err(vm.new_value_error(format!("invalid protocol version: {value}"))); + } + // Convert special values to rustls actual supported versions + // MINIMUM_SUPPORTED (-2) -> 0 (auto-negotiate) + // MAXIMUM_SUPPORTED (-1) -> MAXIMUM_VERSION (TLSv1.3) + let normalized_value = match value { + PROTO_MINIMUM_SUPPORTED => 0, // Auto-negotiate + PROTO_MAXIMUM_SUPPORTED => MAXIMUM_VERSION, // TLSv1.3 _ => value, }; - - let ctx = self.builder(); - let result = unsafe { sys::SSL_CTX_set_min_proto_version(ctx.as_ptr(), proto_version) }; - if result == 0 { - return Err(vm.new_value_error("invalid protocol version")); - } + *self.minimum_version.write() = normalized_value; Ok(()) } #[pygetset] fn maximum_version(&self) -> i32 { - let ctx = self.ctx(); - let version = unsafe { sys::SSL_CTX_get_max_proto_version(ctx.as_ptr()) }; - if version == 0 { - -1 // PY_PROTO_MAXIMUM_SUPPORTED - } else { - version - } + let v = *self.maximum_version.read(); + // return MAXIMUM_SUPPORTED if value is 0 + if v == 0 { PROTO_MAXIMUM_SUPPORTED } else { v } } + #[pygetset(setter)] fn set_maximum_version(&self, value: i32, vm: &VirtualMachine) -> PyResult<()> { - // Handle special values - let proto_version = match value { - -1 => { - // PY_PROTO_MAXIMUM_SUPPORTED -> use 0 for OpenSSL (means no limit) - 0 - } - -2 => { - // PY_PROTO_MINIMUM_SUPPORTED -> use minimum available (TLS 1.2) - sys::TLS1_2_VERSION - } + // Validate that the value is a valid TLS version constant + // Valid values: 0 (default), -2 (MINIMUM_SUPPORTED), -1 (MAXIMUM_SUPPORTED), + // or 0x0300-0x0304 (SSLv3-TLSv1.3) + if value != 0 + && value != -2 + && value != -1 + && !(PROTO_SSLv3..=PROTO_TLSv1_3).contains(&value) + { + return Err(vm.new_value_error(format!("invalid protocol version: {value}"))); + } + // Convert special values to rustls actual supported versions + // MAXIMUM_SUPPORTED (-1) -> 0 (auto-negotiate) + // MINIMUM_SUPPORTED (-2) -> MINIMUM_VERSION (TLSv1.2) + let normalized_value = match value { + PROTO_MAXIMUM_SUPPORTED => 0, // Auto-negotiate + PROTO_MINIMUM_SUPPORTED => MINIMUM_VERSION, // TLSv1.2 _ => value, }; - - let ctx = self.builder(); - let result = unsafe { sys::SSL_CTX_set_max_proto_version(ctx.as_ptr(), proto_version) }; - if result == 0 { - return Err(vm.new_value_error("invalid protocol version")); - } + *self.maximum_version.write() = normalized_value; Ok(()) } - #[pygetset] - fn num_tickets(&self, _vm: &VirtualMachine) -> PyResult { - // Only supported for TLS 1.3 - #[cfg(ossl110)] - { - let ctx = self.ctx(); - let num = unsafe { sys::SSL_CTX_get_num_tickets(ctx.as_ptr()) }; - Ok(num) - } - #[cfg(not(ossl110))] + #[pymethod] + fn load_cert_chain(&self, args: LoadCertChainArgs, vm: &VirtualMachine) -> PyResult<()> { + // Parse certfile argument (str or bytes) to path + let cert_path = Self::parse_path_arg(&args.certfile, vm)?; + + // Parse keyfile argument (default to certfile if not provided) + let key_path = match args.keyfile { + OptionalArg::Present(Some(ref k)) => Self::parse_path_arg(k, vm)?, + _ => cert_path.clone(), + }; + + // Parse password argument (str, bytes-like, or callable) + // Callable passwords are NOT invoked immediately (lazy evaluation) + let (password_str, password_callable) = + Self::parse_password_argument(&args.password, vm)?; + + // Validate immediate password length (limit: PEM_BUFSIZE = 1024 bytes) + if let Some(ref pwd) = password_str + && pwd.len() > PEM_BUFSIZE { - let _ = vm; - Ok(0) - } - } - #[pygetset(setter)] - fn set_num_tickets(&self, value: isize, vm: &VirtualMachine) -> PyResult<()> { - // Check for negative values - if value < 0 { - return Err( - vm.new_value_error("num_tickets must be a non-negative integer".to_owned()) - ); + return Err(vm.new_value_error(format!( + "password cannot be longer than {PEM_BUFSIZE} bytes", + ))); } - // Check that this is a server context - if self.protocol != SslVersion::TlsServer { - return Err(vm.new_value_error("SSLContext is not a server context.".to_owned())); - } + // First attempt: Load with immediate password (or None if callable) + let mut result = + cert::load_cert_chain_from_file(&cert_path, &key_path, password_str.as_deref()); - #[cfg(ossl110)] + // If failed and callable exists, invoke it and retry + // This implements lazy evaluation: callable only invoked if password is actually needed + if result.is_err() + && let Some(callable) = password_callable { - let ctx = self.builder(); - let result = unsafe { sys::SSL_CTX_set_num_tickets(ctx.as_ptr(), value as usize) }; - if result != 1 { - return Err(vm.new_value_error("failed to set num tickets.")); + // Invoke callable - exceptions propagate naturally + let pwd_result = callable.call((), vm)?; + + // Convert callable result to string + let password_from_callable = if let Ok(pwd_str) = + PyStrRef::try_from_object(vm, pwd_result.clone()) + { + pwd_str.as_str().to_owned() + } else if let Ok(pwd_bytes_like) = ArgBytesLike::try_from_object(vm, pwd_result) { + String::from_utf8(pwd_bytes_like.borrow_buf().to_vec()).map_err(|_| { + vm.new_type_error( + "password callback returned invalid UTF-8 bytes".to_owned(), + ) + })? + } else { + return Err(vm.new_type_error( + "password callback must return a string or bytes".to_owned(), + )); + }; + + // Validate callable password length + if password_from_callable.len() > PEM_BUFSIZE { + return Err(vm.new_value_error(format!( + "password cannot be longer than {PEM_BUFSIZE} bytes", + ))); } - Ok(()) - } - #[cfg(not(ossl110))] - { - let _ = (value, vm); - Ok(()) + + // Retry with callable password + result = cert::load_cert_chain_from_file( + &cert_path, + &key_path, + Some(&password_from_callable), + ); } - } - #[pymethod] - fn set_default_verify_paths(&self, vm: &VirtualMachine) -> PyResult<()> { - cfg_if::cfg_if! { - if #[cfg(openssl_vendored)] { - let (cert_file, cert_dir) = get_cert_file_dir(); - self.builder() - .load_verify_locations(Some(cert_file), Some(cert_dir)) - .map_err(|e| convert_openssl_error(vm, e)) + // Process result + let (certs, key) = result.map_err(|e| { + // Try to downcast to io::Error to preserve errno information + if let Ok(io_err) = e.downcast::() { + match io_err.kind() { + // File access errors (NotFound, PermissionDenied) - preserve errno + std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => { + io_err.into_pyexception(vm) + } + // Other io::Error types + std::io::ErrorKind::Other => { + let msg = io_err.to_string(); + if msg.contains("Failed to decrypt") || msg.contains("wrong password") { + // Wrong password error + vm.new_exception_msg(PySSLError::class(&vm.ctx).to_owned(), msg) + } else { + // [SSL] PEM lib + super::compat::SslError::create_ssl_error_with_reason( + vm, "SSL", "", "PEM lib", + ) + } + } + // PEM parsing errors - [SSL] PEM lib + _ => super::compat::SslError::create_ssl_error_with_reason( + vm, "SSL", "", "PEM lib", + ), + } } else { - self.builder() - .set_default_verify_paths() - .map_err(|e| convert_openssl_error(vm, e)) + // Unknown error type - [SSL] PEM lib + super::compat::SslError::create_ssl_error_with_reason(vm, "SSL", "", "PEM lib") } - } - } + })?; - #[pymethod] - fn _set_alpn_protocols(&self, protos: ArgBytesLike, vm: &VirtualMachine) -> PyResult<()> { - #[cfg(ossl102)] - { - let mut ctx = self.builder(); - let server = protos.with_ref(|pbuf| { - if pbuf.len() > libc::c_uint::MAX as usize { - return Err(vm.new_overflow_error(format!( - "protocols longer than {} bytes", - libc::c_uint::MAX - ))); + // Validate certificate and key match + cert::validate_cert_key_match(&certs, &key).map_err(|e| { + vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + if e.contains("key values mismatch") { + "[SSL: KEY_VALUES_MISMATCH] key values mismatch".to_owned() + } else { + e + }, + ) + })?; + + // Auto-build certificate chain: if only leaf cert is in file, try to add CA certs + // This matches OpenSSL behavior where it automatically includes intermediate/CA certs + let mut full_chain = certs.clone(); + if full_chain.len() == 1 { + // Only have leaf cert, try to build chain from CA certs + let ca_certs_der = self.ca_certs_der.read(); + if !ca_certs_der.is_empty() { + // Use build_verified_chain to construct full chain + let chain_result = cert::build_verified_chain(&full_chain, &ca_certs_der); + if chain_result.len() > 1 { + // Successfully built a longer chain + full_chain = chain_result.into_iter().map(CertificateDer::from).collect(); } - ctx.set_alpn_protos(pbuf) - .map_err(|e| convert_openssl_error(vm, e))?; - Ok(pbuf.to_vec()) + } + } + + // Additional validation: Create CertifiedKey to ensure rustls accepts it + let signing_key = + rustls::crypto::aws_lc_rs::sign::any_supported_type(&key).map_err(|_| { + vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "[SSL: KEY_VALUES_MISMATCH] key values mismatch".to_owned(), + ) })?; - ctx.set_alpn_select_callback(move |_, client| { - let proto = - ssl::select_next_proto(&server, client).ok_or(ssl::AlpnError::NOACK)?; - let pos = memchr::memmem::find(client, proto) - .expect("selected alpn proto should be present in client protos"); - Ok(&client[pos..proto.len()]) - }); - Ok(()) - } - #[cfg(not(ossl102))] - { - Err(vm.new_not_implemented_error( - "The NPN extension requires OpenSSL 1.0.1 or later.", - )) + + let certified_key = CertifiedKey::new(full_chain.clone(), signing_key); + if certified_key.keys_match().is_err() { + return Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "[SSL: KEY_VALUES_MISMATCH] key values mismatch".to_owned(), + )); } + + // Add cert/key pair to collection (OpenSSL allows multiple cert/key pairs) + // Store both CertifiedKey (for server) and PrivateKeyDer (for client mTLS) + let cert_der = &full_chain[0]; + let mut cert_keys = self.cert_keys.write(); + + // Remove any existing cert/key pair with the same certificate + // (This allows updating cert/key pair without duplicating) + cert_keys.retain(|(existing, _)| &existing.cert[0] != cert_der); + + // Add new cert/key pair as tuple + cert_keys.push((Arc::new(certified_key), key)); + + Ok(()) } #[pymethod] @@ -1155,264 +1337,357 @@ mod _ssl { args: LoadVerifyLocationsArgs, vm: &VirtualMachine, ) -> PyResult<()> { - if let (None, None, None) = (&args.cafile, &args.capath, &args.cadata) { - return Err(vm.new_type_error("cafile, capath and cadata cannot be all omitted")); - } + // Check that at least one argument is provided + let has_cafile = matches!(&args.cafile, OptionalArg::Present(Some(_))); + let has_capath = matches!(&args.capath, OptionalArg::Present(Some(_))); + let has_cadata = matches!(&args.cadata, OptionalArg::Present(obj) if !vm.is_none(obj)); - #[cold] - fn invalid_cadata(vm: &VirtualMachine) -> PyBaseExceptionRef { - vm.new_type_error("cadata should be an ASCII string or a bytes-like object") + if !has_cafile && !has_capath && !has_cadata { + return Err( + vm.new_type_error("cafile, capath and cadata cannot be all omitted".to_owned()) + ); } - let mut ctx = self.builder(); + // Get mutable references to store and ca_certs_der + let mut root_store = self.root_certs.write(); + let mut ca_certs_der = self.ca_certs_der.write(); - // validate cadata type and load cadata - if let Some(cadata) = args.cadata { - let certs = match cadata { - Either::A(s) => { - if !s.is_ascii() { - return Err(invalid_cadata(vm)); - } - X509::stack_from_pem(s.as_bytes()) - } - Either::B(b) => b.with_ref(x509_stack_from_der), - }; - let certs = certs.map_err(|e| convert_openssl_error(vm, e))?; - let store = ctx.cert_store_mut(); - for cert in certs { - store - .add_cert(cert) - .map_err(|e| convert_openssl_error(vm, e))?; + // Load from file + if let OptionalArg::Present(Some(ref cafile_obj)) = args.cafile { + let path = Self::parse_path_arg(cafile_obj, vm)?; + + // Try to load as CRL first + if let Some(crl) = self.load_crl_from_file(&path, vm)? { + self.crls.write().push(crl); + } else { + // Not a CRL, load as certificate + let stats = self.load_certs_from_file_helper( + &mut root_store, + &mut ca_certs_der, + &path, + vm, + )?; + self.update_cert_stats(stats); } } - if args.cafile.is_some() || args.capath.is_some() { - let cafile_path = args.cafile.map(|p| p.to_path_buf(vm)).transpose()?; - let capath_path = args.capath.map(|p| p.to_path_buf(vm)).transpose()?; - ctx.load_verify_locations(cafile_path.as_deref(), capath_path.as_deref()) - .map_err(|e| convert_openssl_error(vm, e))?; + // Load from directory (don't add to ca_certs_der) + if let OptionalArg::Present(Some(ref capath_obj)) = args.capath { + let dir_path = Self::parse_path_arg(capath_obj, vm)?; + let stats = self.load_certs_from_dir_helper(&mut root_store, &dir_path, vm)?; + self.update_cert_stats(stats); + } + + // Load from bytes or str + if let OptionalArg::Present(cadata_obj) = args.cadata + && !vm.is_none(&cadata_obj) + { + // Check if input is string or bytes + let is_string = PyStrRef::try_from_object(vm, cadata_obj.clone()).is_ok(); + let data_vec = self.parse_cadata_arg(&cadata_obj, vm)?; + let stats = self.load_certs_from_bytes_helper( + &mut root_store, + &mut ca_certs_der, + &data_vec, + is_string, // PEM only for strings + vm, + )?; + self.update_cert_stats(stats); } Ok(()) } - #[pymethod] - fn get_ca_certs( - &self, - binary_form: OptionalArg, + /// Helper: Get path from Python's os.environ + fn get_env_path( + environ: &PyObjectRef, + var_name: &str, vm: &VirtualMachine, - ) -> PyResult> { - let binary_form = binary_form.unwrap_or(false); - let ctx = self.ctx(); - #[cfg(ossl300)] - let certs = ctx.cert_store().all_certificates(); - #[cfg(not(ossl300))] - let certs = ctx.cert_store().objects().iter().filter_map(|x| x.x509()); - - // Filter to only include CA certificates (Basic Constraints: CA=TRUE) - let certs = certs - .into_iter() - .filter(|cert| { - unsafe { - // X509_check_ca() returns 1 for CA certificates - X509_check_ca(cert.as_ptr()) == 1 - } - }) - .map(|ref cert| cert_to_py(vm, cert, binary_form)) - .collect::, _>>()?; - Ok(certs) + ) -> PyResult { + let path_obj = environ.get_item(var_name, vm)?; + path_obj.try_into_value(vm) } - #[pymethod] - fn cert_store_stats(&self, vm: &VirtualMachine) -> PyResult { - let ctx = self.ctx(); - let store_ptr = unsafe { sys::SSL_CTX_get_cert_store(ctx.as_ptr()) }; + /// Helper: Try to load certificates from Python's os.environ variables + /// + /// Returns true if certificates were successfully loaded. + /// + /// We use Python's os.environ instead of Rust's std::env + /// because Python code can modify os.environ at runtime (e.g., + /// `os.environ['SSL_CERT_FILE'] = '/path'`), but rustls-native-certs uses + /// std::env which only sees the process environment at startup. + fn try_load_from_python_environ( + &self, + loader: &mut cert::CertLoader<'_>, + vm: &VirtualMachine, + ) -> PyResult { + use std::path::Path; - if store_ptr.is_null() { - return Err(vm.new_memory_error("failed to get cert store".to_owned())); - } + let os_module = vm.import("os", 0)?; + let environ = os_module.get_attr("environ", vm)?; - let objs_ptr = unsafe { sys::X509_STORE_get0_objects(store_ptr) }; - if objs_ptr.is_null() { - return Err(vm.new_memory_error("failed to query cert store".to_owned())); + // Try SSL_CERT_FILE first + if let Ok(cert_file) = Self::get_env_path(&environ, "SSL_CERT_FILE", vm) + && Path::new(&cert_file).exists() + && let Ok(stats) = loader.load_from_file(&cert_file) + { + self.update_cert_stats(stats); + return Ok(true); } - let mut x509_count = 0; - let mut crl_count = 0; - let mut ca_count = 0; + // Try SSL_CERT_DIR (only if SSL_CERT_FILE didn't work) + if let Ok(cert_dir) = Self::get_env_path(&environ, "SSL_CERT_DIR", vm) + && Path::new(&cert_dir).is_dir() + && let Ok(stats) = loader.load_from_dir(&cert_dir) + { + self.update_cert_stats(stats); + return Ok(true); + } - unsafe { - let num_objs = sys::OPENSSL_sk_num(objs_ptr as *const _); - for i in 0..num_objs { - let obj_ptr = - sys::OPENSSL_sk_value(objs_ptr as *const _, i) as *const sys::X509_OBJECT; - let obj_type = X509_OBJECT_get_type(obj_ptr); + Ok(false) + } - match obj_type { - X509_LU_X509 => { - x509_count += 1; - let x509_ptr = sys::X509_OBJECT_get0_X509(obj_ptr); - if !x509_ptr.is_null() && X509_check_ca(x509_ptr) == 1 { - ca_count += 1; - } - } - X509_LU_CRL => { - crl_count += 1; - } - _ => { - // Ignore unrecognized types - } + /// Helper: Load system certificates using rustls-native-certs + /// + /// This uses platform-specific methods: + /// - Linux: openssl-probe to find certificate files + /// - macOS: Keychain API + /// - Windows: System certificate store + fn load_system_certificates( + &self, + store: &mut rustls::RootCertStore, + vm: &VirtualMachine, + ) -> PyResult<()> { + let result = rustls_native_certs::load_native_certs(); + + // Load successfully found certificates + for cert in result.certs { + let is_ca = cert::is_ca_certificate(cert.as_ref()); + if store.add(cert).is_ok() { + *self.x509_cert_count.write() += 1; + if is_ca { + *self.ca_cert_count.write() += 1; } } - // Note: No need to free objs_ptr as X509_STORE_get0_objects returns - // a pointer to internal data that should not be freed by the caller } - let dict = vm.ctx.new_dict(); - dict.set_item("x509", vm.ctx.new_int(x509_count).into(), vm)?; - dict.set_item("crl", vm.ctx.new_int(crl_count).into(), vm)?; - dict.set_item("x509_ca", vm.ctx.new_int(ca_count).into(), vm)?; - Ok(dict.into()) - } - - #[pymethod] - fn session_stats(&self, vm: &VirtualMachine) -> PyResult { - let ctx = self.ctx(); - let ctx_ptr = ctx.as_ptr(); - - let dict = vm.ctx.new_dict(); - - macro_rules! add_stat { - ($key:expr, $func:ident) => { - let value = unsafe { $func(ctx_ptr) }; - dict.set_item($key, vm.ctx.new_int(value).into(), vm)?; - }; + // If there were errors but some certs loaded, just continue + // If NO certs loaded and there were errors, report the first error + if *self.x509_cert_count.read() == 0 && !result.errors.is_empty() { + return Err(vm.new_os_error(format!( + "Failed to load native certificates: {}", + result.errors[0] + ))); } - add_stat!("number", SSL_CTX_sess_number); - add_stat!("connect", SSL_CTX_sess_connect); - add_stat!("connect_good", SSL_CTX_sess_connect_good); - add_stat!("connect_renegotiate", SSL_CTX_sess_connect_renegotiate); - add_stat!("accept", SSL_CTX_sess_accept); - add_stat!("accept_good", SSL_CTX_sess_accept_good); - add_stat!("accept_renegotiate", SSL_CTX_sess_accept_renegotiate); - add_stat!("hits", SSL_CTX_sess_hits); - add_stat!("misses", SSL_CTX_sess_misses); - add_stat!("timeouts", SSL_CTX_sess_timeouts); - add_stat!("cache_full", SSL_CTX_sess_cache_full); - - Ok(dict.into()) + Ok(()) } #[pymethod] - fn load_dh_params(&self, filepath: FsPath, vm: &VirtualMachine) -> PyResult<()> { - let path = filepath.to_path_buf(vm)?; - - // Open the file using fopen (cross-platform) - let fp = - rustpython_common::fileutils::fopen(path.as_path(), "rb").map_err(|e| { - match e.kind() { - std::io::ErrorKind::NotFound => vm.new_exception_msg( - vm.ctx.exceptions.file_not_found_error.to_owned(), - e.to_string(), - ), - _ => vm.new_os_error(e.to_string()), - } - })?; + fn load_default_certs( + &self, + _purpose: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult<()> { + let mut store = self.root_certs.write(); - // Read DH parameters - let dh = unsafe { - PEM_read_DHparams( - fp, - std::ptr::null_mut(), - std::ptr::null_mut(), - std::ptr::null_mut(), - ) - }; - unsafe { - libc::fclose(fp); - } + // Create loader (without ca_certs_der - default certs don't go to get_ca_certs()) + let mut lazy_ca_certs = Vec::new(); + let mut loader = cert::CertLoader::new(&mut store, &mut lazy_ca_certs); - if dh.is_null() { - return Err(convert_openssl_error(vm, ErrorStack::get())); - } + // Try Python os.environ first (allows runtime env changes) + // This checks SSL_CERT_FILE and SSL_CERT_DIR from Python's os.environ + let loaded = self.try_load_from_python_environ(&mut loader, vm)?; - // Set temporary DH parameters - let ctx = self.builder(); - let result = unsafe { sys::SSL_CTX_set_tmp_dh(ctx.as_ptr(), dh) }; - unsafe { - sys::DH_free(dh); + // Fallback to system certificates if environment variables didn't provide any + if !loaded { + let _ = self.load_system_certificates(&mut store, vm); } - if result != 1 { - return Err(convert_openssl_error(vm, ErrorStack::get())); + // If no certificates were loaded from system, fallback to webpki-roots (Mozilla CA bundle) + // This ensures we always have some trusted root certificates even if system cert loading fails + if *self.x509_cert_count.read() == 0 { + use webpki_roots; + + // webpki_roots provides TLS_SERVER_ROOTS as &[TrustAnchor] + // We can use extend() to add them to the RootCertStore + let webpki_count = webpki_roots::TLS_SERVER_ROOTS.len(); + store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + + *self.x509_cert_count.write() += webpki_count; + *self.ca_cert_count.write() += webpki_count; } Ok(()) } - #[pygetset] + #[pymethod] + fn set_alpn_protocols(&self, protocols: PyListRef, vm: &VirtualMachine) -> PyResult<()> { + let mut alpn_list = Vec::new(); + for item in protocols.borrow_vec().iter() { + let bytes = ArgBytesLike::try_from_object(vm, item.clone())?; + alpn_list.push(bytes.borrow_buf().to_vec()); + } + *self.alpn_protocols.write() = alpn_list; + Ok(()) + } + + #[pymethod] + fn _set_alpn_protocols(&self, protos: ArgBytesLike, vm: &VirtualMachine) -> PyResult<()> { + let bytes = protos.borrow_buf(); + let alpn_list = parse_length_prefixed_alpn(&bytes, vm)?; + *self.alpn_protocols.write() = alpn_list; + Ok(()) + } + + #[pymethod] + fn set_ciphers(&self, ciphers: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { + let cipher_str = ciphers.as_str(); + + // Parse cipher string and store selected ciphers + let selected_ciphers = parse_cipher_string(cipher_str) + .map_err(|e| vm.new_exception_msg(PySSLError::class(&vm.ctx).to_owned(), e))?; + + // Store in context + *self.selected_ciphers.write() = Some(selected_ciphers); + + Ok(()) + } + + #[pymethod] + fn get_ciphers(&self, vm: &VirtualMachine) -> PyResult { + // Dynamically generate cipher list from rustls ALL_CIPHER_SUITES + // This automatically includes all cipher suites supported by the current rustls version + use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES; + + let cipher_list = ALL_CIPHER_SUITES + .iter() + .map(|suite| { + // Extract cipher information using unified helper + let cipher_info = extract_cipher_info(suite); + + // Convert to OpenSSL-style name + // e.g., "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" -> "ECDHE-RSA-AES128-GCM-SHA256" + let openssl_name = normalize_cipher_name(&cipher_info.name); + + // Determine key exchange and auth methods + let (kx, auth) = if cipher_info.protocol == "TLSv1.3" { + // TLS 1.3 doesn't distinguish - all use modern algos + ("any", "any") + } else if cipher_info.name.contains("ECDHE") { + // TLS 1.2 with ECDHE + let auth = if cipher_info.name.contains("ECDSA") { + "ECDSA" + } else if cipher_info.name.contains("RSA") { + "RSA" + } else { + "any" + }; + ("ECDH", auth) + } else { + ("any", "any") + }; + + // Build description string + // Format: "{name} {protocol} Kx={kx} Au={auth} Enc={enc} Mac={mac}" + let enc = get_cipher_encryption_desc(&openssl_name); + + let description = format!( + "{} {} Kx={} Au={} Enc={} Mac=AEAD", + openssl_name, cipher_info.protocol, kx, auth, enc + ); + + // Create cipher dict + let dict = vm.ctx.new_dict(); + dict.set_item("name", vm.ctx.new_str(openssl_name).into(), vm) + .unwrap(); + dict.set_item("protocol", vm.ctx.new_str(cipher_info.protocol).into(), vm) + .unwrap(); + dict.set_item("id", vm.ctx.new_int(0).into(), vm).unwrap(); // Placeholder ID + dict.set_item("strength_bits", vm.ctx.new_int(cipher_info.bits).into(), vm) + .unwrap(); + dict.set_item("alg_bits", vm.ctx.new_int(cipher_info.bits).into(), vm) + .unwrap(); + dict.set_item("description", vm.ctx.new_str(description).into(), vm) + .unwrap(); + dict.into() + }) + .collect::>(); + + Ok(PyListRef::from(vm.ctx.new_list(cipher_list))) + } + + #[pymethod] + fn set_default_verify_paths(&self, vm: &VirtualMachine) -> PyResult<()> { + // Just call load_default_certs + self.load_default_certs(OptionalArg::Missing, vm) + } + + #[pymethod] + fn cert_store_stats(&self, vm: &VirtualMachine) -> PyResult { + // Use the certificate counters that are updated in load_verify_locations + let x509_count = *self.x509_cert_count.read() as i32; + let ca_count = *self.ca_cert_count.read() as i32; + + let dict = vm.ctx.new_dict(); + dict.set_item("x509", vm.ctx.new_int(x509_count).into(), vm)?; + dict.set_item("crl", vm.ctx.new_int(0).into(), vm)?; // CRL not supported + dict.set_item("x509_ca", vm.ctx.new_int(ca_count).into(), vm)?; + Ok(dict.into()) + } + + #[pymethod] + fn session_stats(&self, vm: &VirtualMachine) -> PyResult { + // Return session statistics + // NOTE: This is a partial implementation - rustls doesn't expose all OpenSSL stats + let dict = vm.ctx.new_dict(); + + // Number of sessions currently in the cache + let session_count = self.client_session_cache.read().len() as i32; + dict.set_item("number", vm.ctx.new_int(session_count).into(), vm)?; + + // Client-side statistics (not tracked separately in this implementation) + dict.set_item("connect", vm.ctx.new_int(0).into(), vm)?; + dict.set_item("connect_good", vm.ctx.new_int(0).into(), vm)?; + dict.set_item("connect_renegotiate", vm.ctx.new_int(0).into(), vm)?; // rustls doesn't support renegotiation + + // Server-side statistics + let accept_count = self.accept_count.load(Ordering::SeqCst) as i32; + dict.set_item("accept", vm.ctx.new_int(accept_count).into(), vm)?; + dict.set_item("accept_good", vm.ctx.new_int(accept_count).into(), vm)?; // Assume all accepts are good + dict.set_item("accept_renegotiate", vm.ctx.new_int(0).into(), vm)?; // rustls doesn't support renegotiation + + // Session reuse statistics + let hits = self.session_hits.load(Ordering::SeqCst) as i32; + dict.set_item("hits", vm.ctx.new_int(hits).into(), vm)?; + + // Misses, timeouts, and cache_full are not tracked in this implementation + dict.set_item("misses", vm.ctx.new_int(0).into(), vm)?; + dict.set_item("timeouts", vm.ctx.new_int(0).into(), vm)?; + dict.set_item("cache_full", vm.ctx.new_int(0).into(), vm)?; + + Ok(dict.into()) + } + + #[pygetset] fn sni_callback(&self) -> Option { - self.sni_callback.lock().clone() + self.sni_callback.read().clone() } #[pygetset(setter)] fn set_sni_callback( &self, - value: Option, + callback: Option, vm: &VirtualMachine, ) -> PyResult<()> { - // Check if this is a server context - if self.protocol == SslVersion::TlsClient { - return Err(vm.new_value_error( - "sni_callback cannot be set on TLS_CLIENT context".to_owned(), - )); - } - - let mut callback_guard = self.sni_callback.lock(); - - if let Some(callback_obj) = value { - if !vm.is_none(&callback_obj) { - // Check if callable - if !callback_obj.is_callable() { - return Err(vm.new_type_error("not a callable object".to_owned())); - } - - // Set the callback - *callback_guard = Some(callback_obj); - - // Set OpenSSL callback - unsafe { - sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( - self.ctx().as_ptr(), - Some(_servername_callback), - ); - sys::SSL_CTX_set_tlsext_servername_arg( - self.ctx().as_ptr(), - self as *const _ as *mut _, - ); - } - } else { - // Clear callback - *callback_guard = None; - unsafe { - sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( - self.ctx().as_ptr(), - None, - ); - } - } - } else { - // Clear callback - *callback_guard = None; - unsafe { - sys::SSL_CTX_set_tlsext_servername_callback__fixed_rust( - self.ctx().as_ptr(), - None, - ); - } + // Validate callback is callable or None + if let Some(ref cb) = callback + && !cb.is(vm.ctx.types.none_type) + && !cb.is_callable() + { + return Err(vm.new_type_error("sni_callback must be callable or None")); } - + *self.sni_callback.write() = callback; Ok(()) } @@ -1422,156 +1697,258 @@ mod _ssl { callback: Option, vm: &VirtualMachine, ) -> PyResult<()> { + // Alias for set_sni_callback self.set_sni_callback(callback, vm) } - #[pymethod] - fn load_cert_chain(&self, args: LoadCertChainArgs, vm: &VirtualMachine) -> PyResult<()> { - let LoadCertChainArgs { - certfile, - keyfile, - password, - } = args; - // TODO: requires passing a callback to C - if password.is_some() { - return Err(vm.new_not_implemented_error("password arg not yet supported")); - } - let mut ctx = self.builder(); - let key_path = keyfile.map(|path| path.to_path_buf(vm)).transpose()?; - let cert_path = certfile.to_path_buf(vm)?; - ctx.set_certificate_chain_file(&cert_path) - .and_then(|()| { - ctx.set_private_key_file( - key_path.as_ref().unwrap_or(&cert_path), - ssl::SslFiletype::PEM, - ) - }) - .and_then(|()| ctx.check_private_key()) - .map_err(|e| convert_openssl_error(vm, e)) + #[pygetset] + fn security_level(&self) -> i32 { + // rustls uses a fixed security level + // Return 2 which is a reasonable default (equivalent to OpenSSL 1.1.0+ level 2) + 2 + } + + #[pygetset] + fn _msg_callback(&self) -> Option { + self.msg_callback.read().clone() + } + + #[pygetset(setter)] + fn set__msg_callback( + &self, + callback: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + // Validate callback is callable or None + if let Some(ref cb) = callback + && !cb.is(vm.ctx.types.none_type) + && !cb.is_callable() + { + return Err(vm.new_type_error("msg_callback must be callable or None")); + } + *self.msg_callback.write() = callback; + Ok(()) } #[pymethod] - fn _wrap_socket( - zelf: PyRef, - args: WrapSocketArgs, + fn get_ca_certs( + &self, + binary_form: OptionalArg, vm: &VirtualMachine, - ) -> PyResult { - // validate socket type and context protocol - if !args.server_side && zelf.protocol == SslVersion::TlsServer { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Cannot create a client socket with a PROTOCOL_TLS_SERVER context".to_owned(), - )); + ) -> PyResult { + let binary_form = binary_form.unwrap_or(false); + let ca_certs_der = self.ca_certs_der.read(); + + let mut certs = Vec::new(); + for cert_der in ca_certs_der.iter() { + // Parse certificate to check if it's a CA and get info + match x509_parser::parse_x509_certificate(cert_der) { + Ok((_, cert)) => { + // Check if this is a CA certificate (BasicConstraints: CA=TRUE) + let is_ca = if let Ok(Some(bc_ext)) = cert.basic_constraints() { + bc_ext.value.ca + } else { + false + }; + + // Only include CA certificates + if !is_ca { + continue; + } + + if binary_form { + // Return DER-encoded certificate as bytes + certs.push(vm.ctx.new_bytes(cert_der.clone()).into()); + } else { + // Return certificate as dict (use helper from _test_decode_cert) + let dict = self.cert_der_to_dict(vm, cert_der)?; + certs.push(dict); + } + } + Err(_) => { + // Skip invalid certificates + continue; + } + } } - if args.server_side && zelf.protocol == SslVersion::TlsClient { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Cannot create a server socket with a PROTOCOL_TLS_CLIENT context".to_owned(), + + Ok(PyListRef::from(vm.ctx.new_list(certs))) + } + + #[pymethod] + fn load_dh_params(&self, filepath: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + // Validate filepath is not None + if vm.is_none(&filepath) { + return Err(vm.new_type_error("DH params filepath cannot be None".to_owned())); + } + + // Validate filepath is str or bytes + let path_str = if let Ok(s) = PyStrRef::try_from_object(vm, filepath.clone()) { + s.as_str().to_owned() + } else if let Ok(b) = ArgBytesLike::try_from_object(vm, filepath) { + String::from_utf8(b.borrow_buf().to_vec()) + .map_err(|_| vm.new_value_error("Invalid path encoding".to_owned()))? + } else { + return Err(vm.new_type_error("DH params filepath must be str or bytes".to_owned())); + }; + + // Check if file exists + if !std::path::Path::new(&path_str).exists() { + // Create FileNotFoundError with errno=ENOENT (2) using args + let exc = vm.new_exception( + vm.ctx.exceptions.file_not_found_error.to_owned(), + vec![ + vm.ctx.new_int(2).into(), // errno = ENOENT (2) + vm.ctx.new_str("No such file or directory").into(), + vm.ctx.new_str(path_str.clone()).into(), // filename + ], + ); + return Err(exc); + } + + // Validate that the file contains DH parameters + // Read the file and check for DH PARAMETERS header + let contents = + std::fs::read_to_string(&path_str).map_err(|e| vm.new_os_error(e.to_string()))?; + + if !contents.contains("BEGIN DH PARAMETERS") + && !contents.contains("BEGIN X9.42 DH PARAMETERS") + { + // File exists but doesn't contain DH parameters - raise SSLError + // [PEM: NO_START_LINE] no start line + return Err(super::compat::SslError::create_ssl_error_with_reason( + vm, + "PEM", + "NO_START_LINE", + "[PEM: NO_START_LINE] no start line", )); } - let mut ssl = ssl::Ssl::new(&zelf.ctx()).map_err(|e| convert_openssl_error(vm, e))?; + // rustls doesn't use DH parameters (it uses ECDHE for key exchange) + // This is a no-op for compatibility with OpenSSL-based code + Ok(()) + } + + #[pymethod] + fn set_ecdh_curve(&self, name: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + // Validate name is not None + if vm.is_none(&name) { + return Err(vm.new_type_error("ECDH curve name cannot be None".to_owned())); + } - let socket_type = if args.server_side { - ssl.set_accept_state(); - SslServerOrClient::Server + // Validate name is str or bytes + let curve_name = if let Ok(s) = PyStrRef::try_from_object(vm, name.clone()) { + s.as_str().to_owned() + } else if let Ok(b) = ArgBytesLike::try_from_object(vm, name) { + String::from_utf8(b.borrow_buf().to_vec()) + .map_err(|_| vm.new_value_error("Invalid curve name encoding".to_owned()))? } else { - ssl.set_connect_state(); - SslServerOrClient::Client + return Err(vm.new_type_error("ECDH curve name must be str or bytes".to_owned())); }; - if let Some(hostname) = &args.server_hostname { - let hostname = hostname.as_str(); - if hostname.is_empty() || hostname.starts_with('.') { - return Err(vm.new_value_error( - "server_hostname cannot be an empty string or start with a leading dot.", - )); - } - if hostname.contains('\0') { - return Err(vm.new_value_error("embedded null byte in server_hostname")); - } - let ip = hostname.parse::(); - if ip.is_err() { - ssl.set_hostname(hostname) - .map_err(|e| convert_openssl_error(vm, e))?; - } - if zelf.check_hostname.load() { - if let Ok(ip) = ip { - ssl.param_mut() - .set_ip(ip) - .map_err(|e| convert_openssl_error(vm, e))?; - } else { - ssl.param_mut() - .set_host(hostname) - .map_err(|e| convert_openssl_error(vm, e))?; - } - } + // Validate curve name (common curves for compatibility) + // rustls supports: X25519, secp256r1 (prime256v1), secp384r1 + let valid_curves = [ + "prime256v1", + "secp256r1", + "prime384v1", + "secp384r1", + "prime521v1", + "secp521r1", + "X25519", + "x25519", + "x448", // For future compatibility + ]; + + if !valid_curves.contains(&curve_name.as_str()) { + return Err(vm.new_value_error(format!("unknown curve name '{curve_name}'"))); } - // Configure post-handshake authentication (PHA) - #[cfg(ossl111)] - if *zelf.post_handshake_auth.lock() { - unsafe { - if args.server_side { - // Server socket: add SSL_VERIFY_POST_HANDSHAKE flag - // Only in combination with SSL_VERIFY_PEER - let mode = sys::SSL_get_verify_mode(ssl.as_ptr()); - if (mode & sys::SSL_VERIFY_PEER as libc::c_int) != 0 { - // Add POST_HANDSHAKE flag (keep existing flags including FAIL_IF_NO_PEER_CERT) - sys::SSL_set_verify( - ssl.as_ptr(), - mode | SSL_VERIFY_POST_HANDSHAKE, - None, - ); - } - } else { - // Client socket: call SSL_set_post_handshake_auth - SSL_set_post_handshake_auth(ssl.as_ptr(), 1); + // Store the curve name to be used during handshake + // This will limit the key exchange groups offered/accepted + *self.ecdh_curve.write() = Some(curve_name); + Ok(()) + } + + #[pymethod] + fn _wrap_socket( + zelf: PyRef, + args: WrapSocketArgs, + vm: &VirtualMachine, + ) -> PyResult> { + // Convert server_hostname to Option + // Handle both missing argument and None value + let hostname = match args.server_hostname.into_option().flatten() { + Some(hostname_str) => { + let hostname = hostname_str.as_str(); + + // Validate hostname + if hostname.is_empty() { + return Err(vm.new_value_error("server_hostname cannot be an empty string")); } - } - } - let stream = ssl::SslStream::new(ssl, SocketStream(args.sock.clone())) - .map_err(|e| convert_openssl_error(vm, e))?; + // Check if it starts with a dot + if hostname.starts_with('.') { + return Err(vm.new_value_error("server_hostname cannot start with a dot")); + } - let py_ssl_socket = PySslSocket { - ctx: PyRwLock::new(zelf.clone()), - connection: PyRwLock::new(SslConnection::Socket(stream)), - socket_type, - server_hostname: args.server_hostname, - owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?), - }; + // Check if it's a bare IP address (not allowed for SNI) + if hostname.parse::().is_ok() { + return Err(vm.new_value_error("server_hostname cannot be an IP address")); + } + + // Check for NULL bytes + if hostname.contains('\0') { + return Err(vm.new_type_error("embedded null character")); + } - // Convert to PyRef (heap allocation) to avoid use-after-free - let py_ref = - py_ssl_socket.into_ref_with_type(vm, PySslSocket::class(&vm.ctx).to_owned())?; - - // Set SNI callback data if callback is configured - if zelf.sni_callback.lock().is_some() { - unsafe { - let ssl_ptr = py_ref.connection.read().ssl().as_ptr(); - - // Store callback data in SSL ex_data - let callback_data = Box::new(SniCallbackData { - ssl_context: zelf.clone(), - vm_ptr: vm as *const _, - }); - let idx = get_sni_ex_data_index(); - sys::SSL_set_ex_data(ssl_ptr, idx, Box::into_raw(callback_data) as *mut _); - - // Store PyRef pointer (heap-allocated) in ex_data index 0 - sys::SSL_set_ex_data(ssl_ptr, 0, &*py_ref as *const _ as *mut _); + Some(hostname.to_string()) } - } + None => None, + }; - // Set session if provided - if let Some(session) = args.session - && !vm.is_none(&session) - { - py_ref.set_session(session, vm)?; + // Validate socket type and context protocol + if args.server_side && zelf.protocol == PROTOCOL_TLS_CLIENT { + return Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "Cannot create a server socket with a PROTOCOL_TLS_CLIENT context".to_owned(), + )); + } + if !args.server_side && zelf.protocol == PROTOCOL_TLS_SERVER { + return Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "Cannot create a client socket with a PROTOCOL_TLS_SERVER context".to_owned(), + )); } - Ok(py_ref.into()) + // Create _SSLSocket instance + let ssl_socket = PySSLSocket { + sock: args.sock.clone(), + context: PyRwLock::new(zelf), + server_side: args.server_side, + server_hostname: PyRwLock::new(hostname), + connection: PyMutex::new(None), + handshake_done: PyMutex::new(false), + session_was_reused: PyMutex::new(false), + owner: PyRwLock::new(args.owner.into_option()), + // Filter out Python None objects - only store actual SSLSession objects + session: PyRwLock::new(args.session.into_option().filter(|s| !vm.is_none(s))), + verified_chain: PyRwLock::new(None), + incoming_bio: None, + outgoing_bio: None, + sni_state: PyRwLock::new(None), + pending_context: PyRwLock::new(None), + client_hello_buffer: PyMutex::new(None), + shutdown_state: PyMutex::new(ShutdownState::NotStarted), + deferred_cert_error: Arc::new(ParkingRwLock::new(None)), + }; + + // Create PyRef with correct type + let ssl_socket_ref = ssl_socket + .into_ref_with_type(vm, vm.class("_ssl", "_SSLSocket")) + .map_err(|_| vm.new_type_error("Failed to create SSLSocket"))?; + + Ok(ssl_socket_ref) } #[pymethod] @@ -1579,1911 +1956,2546 @@ mod _ssl { zelf: PyRef, args: WrapBioArgs, vm: &VirtualMachine, - ) -> PyResult { - // validate socket type and context protocol - if !args.server_side && zelf.protocol == SslVersion::TlsServer { + ) -> PyResult> { + // Convert server_hostname to Option + // Handle both missing argument and None value + let hostname = match args.server_hostname.into_option().flatten() { + Some(hostname_str) => { + let hostname = hostname_str.as_str(); + validate_hostname(hostname, vm)?; + Some(hostname.to_string()) + } + None => None, + }; + + // Extract server_side value + let server_side = args.server_side.unwrap_or(false); + + // Validate socket type and context protocol + if server_side && zelf.protocol == PROTOCOL_TLS_CLIENT { return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Cannot create a client socket with a PROTOCOL_TLS_SERVER context".to_owned(), + PySSLError::class(&vm.ctx).to_owned(), + "Cannot create a server socket with a PROTOCOL_TLS_CLIENT context".to_owned(), )); } - if args.server_side && zelf.protocol == SslVersion::TlsClient { + if !server_side && zelf.protocol == PROTOCOL_TLS_SERVER { return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Cannot create a server socket with a PROTOCOL_TLS_CLIENT context".to_owned(), + PySSLError::class(&vm.ctx).to_owned(), + "Cannot create a client socket with a PROTOCOL_TLS_SERVER context".to_owned(), )); } - let mut ssl = ssl::Ssl::new(&zelf.ctx()).map_err(|e| convert_openssl_error(vm, e))?; + // Create _SSLSocket instance with BIO mode + let ssl_socket = PySSLSocket { + sock: vm.ctx.none(), // No socket in BIO mode + context: PyRwLock::new(zelf), + server_side, + server_hostname: PyRwLock::new(hostname), + connection: PyMutex::new(None), + handshake_done: PyMutex::new(false), + session_was_reused: PyMutex::new(false), + owner: PyRwLock::new(args.owner.into_option()), + // Filter out Python None objects - only store actual SSLSession objects + session: PyRwLock::new(args.session.into_option().filter(|s| !vm.is_none(s))), + verified_chain: PyRwLock::new(None), + incoming_bio: Some(args.incoming), + outgoing_bio: Some(args.outgoing), + sni_state: PyRwLock::new(None), + pending_context: PyRwLock::new(None), + client_hello_buffer: PyMutex::new(None), + shutdown_state: PyMutex::new(ShutdownState::NotStarted), + deferred_cert_error: Arc::new(ParkingRwLock::new(None)), + }; + + let ssl_socket_ref = ssl_socket + .into_ref_with_type(vm, vm.class("_ssl", "_SSLSocket")) + .map_err(|_| vm.new_type_error("Failed to create SSLSocket"))?; + + Ok(ssl_socket_ref) + } + + // Helper functions (private): - let socket_type = if args.server_side { - ssl.set_accept_state(); - SslServerOrClient::Server + /// Parse path argument (str or bytes) to string + fn parse_path_arg(arg: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Ok(s) = PyStrRef::try_from_object(vm, arg.clone()) { + Ok(s.as_str().to_owned()) + } else if let Ok(b) = ArgBytesLike::try_from_object(vm, arg.clone()) { + String::from_utf8(b.borrow_buf().to_vec()) + .map_err(|_| vm.new_value_error("path contains invalid UTF-8".to_owned())) } else { - ssl.set_connect_state(); - SslServerOrClient::Client - }; + Err(vm.new_type_error("path should be a str or bytes".to_owned())) + } + } - if let Some(hostname) = &args.server_hostname { - let hostname = hostname.as_str(); - if hostname.is_empty() || hostname.starts_with('.') { - return Err(vm.new_value_error( - "server_hostname cannot be an empty string or start with a leading dot.", - )); - } - if hostname.contains('\0') { - return Err(vm.new_value_error("embedded null byte in server_hostname")); - } - let ip = hostname.parse::(); - if ip.is_err() { - ssl.set_hostname(hostname) - .map_err(|e| convert_openssl_error(vm, e))?; - } - if zelf.check_hostname.load() { - if let Ok(ip) = ip { - ssl.param_mut() - .set_ip(ip) - .map_err(|e| convert_openssl_error(vm, e))?; + /// Parse password argument (str, bytes-like, or callable) + /// + /// Returns (immediate_password, callable) where: + /// - immediate_password: Some(string) if password is str/bytes, None if callable + /// - callable: Some(PyObjectRef) if password is callable, None otherwise + fn parse_password_argument( + password: &OptionalArg, + vm: &VirtualMachine, + ) -> PyResult<(Option, Option)> { + match password { + OptionalArg::Present(p) => { + // Try string first + if let Ok(pwd_str) = PyStrRef::try_from_object(vm, p.clone()) { + Ok((Some(pwd_str.as_str().to_owned()), None)) + } + // Try bytes-like + else if let Ok(pwd_bytes_like) = ArgBytesLike::try_from_object(vm, p.clone()) + { + let pwd = String::from_utf8(pwd_bytes_like.borrow_buf().to_vec()).map_err( + |_| vm.new_type_error("password bytes must be valid UTF-8".to_owned()), + )?; + Ok((Some(pwd), None)) + } + // Try callable + else if p.is_callable() { + Ok((None, Some(p.clone()))) } else { - ssl.param_mut() - .set_host(hostname) - .map_err(|e| convert_openssl_error(vm, e))?; + Err(vm.new_type_error( + "password should be a string, bytes, or callable".to_owned(), + )) } } + _ => Ok((None, None)), } + } - // Don't use SSL_set_bio - let SslStream drive I/O through BioStream Read/Write - - // Configure post-handshake authentication (PHA) - #[cfg(ossl111)] - if *zelf.post_handshake_auth.lock() { - unsafe { - if args.server_side { - // Server socket: add SSL_VERIFY_POST_HANDSHAKE flag - // Only in combination with SSL_VERIFY_PEER - let mode = sys::SSL_get_verify_mode(ssl.as_ptr()); - if (mode & sys::SSL_VERIFY_PEER as libc::c_int) != 0 { - // Add POST_HANDSHAKE flag (keep existing flags including FAIL_IF_NO_PEER_CERT) - sys::SSL_set_verify( - ssl.as_ptr(), - mode | SSL_VERIFY_POST_HANDSHAKE, - None, - ); - } - } else { - // Client socket: call SSL_set_post_handshake_auth - SSL_set_post_handshake_auth(ssl.as_ptr(), 1); + /// Helper: Load certificates from file into existing store + fn load_certs_from_file_helper( + &self, + root_store: &mut RootCertStore, + ca_certs_der: &mut Vec>, + path: &str, + vm: &VirtualMachine, + ) -> PyResult { + let mut loader = cert::CertLoader::new(root_store, ca_certs_der); + loader.load_from_file(path).map_err(|e| { + // Preserve errno for file access errors (NotFound, PermissionDenied) + match e.kind() { + std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => { + e.into_pyexception(vm) } + // PEM parsing errors + _ => super::compat::SslError::create_ssl_error_with_reason( + vm, "X509", "", "PEM lib", + ), } - } + }) + } - // Create a BioStream wrapper (dummy, actual IO goes through BIOs) - let bio_stream = BioStream { - inbio: args.incoming, - outbio: args.outgoing, - }; + /// Helper: Load certificates from directory into existing store + fn load_certs_from_dir_helper( + &self, + root_store: &mut RootCertStore, + path: &str, + vm: &VirtualMachine, + ) -> PyResult { + // Load certs and store them in capath_certs_der for lazy loading simulation + // (CPython only returns these in get_ca_certs() after they're used in handshake) + let mut capath_certs = Vec::new(); + let mut loader = cert::CertLoader::new(root_store, &mut capath_certs); + let stats = loader + .load_from_dir(path) + .map_err(|e| e.into_pyexception(vm))?; + + // Store loaded certs for potential tracking after handshake + *self.capath_certs_der.write() = capath_certs; + + Ok(stats) + } + + /// Helper: Load certificates from bytes into existing store + fn load_certs_from_bytes_helper( + &self, + root_store: &mut RootCertStore, + ca_certs_der: &mut Vec>, + data: &[u8], + pem_only: bool, + vm: &VirtualMachine, + ) -> PyResult { + let mut loader = cert::CertLoader::new(root_store, ca_certs_der); + // treat_all_as_ca=true: CPython counts all certificates loaded via cadata as CA certs + // regardless of their Basic Constraints extension + // pem_only=true for string input + loader + .load_from_bytes_ex(data, true, pem_only) + .map_err(|e| { + // Preserve specific error messages from cert.rs + let err_msg = e.to_string(); + if err_msg.contains("no start line") { + // no start line: cadata does not contain a certificate + vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "no start line: cadata does not contain a certificate".to_string(), + ) + } else if err_msg.contains("not enough data") { + // not enough data: cadata does not contain a certificate + vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "not enough data: cadata does not contain a certificate".to_string(), + ) + } else { + // Generic PEM error + vm.new_exception_msg(PySSLError::class(&vm.ctx).to_owned(), err_msg) + } + }) + } - // Create SslStream with BioStream - let stream = - ssl::SslStream::new(ssl, bio_stream).map_err(|e| convert_openssl_error(vm, e))?; + /// Helper: Try to parse data as CRL (PEM or DER format) + fn try_parse_crl( + &self, + data: &[u8], + ) -> Result, String> { + // Try PEM format first + let mut cursor = std::io::Cursor::new(data); + let mut crl_iter = rustls_pemfile::crls(&mut cursor); + if let Some(Ok(crl)) = crl_iter.next() { + return Ok(crl); + } - let py_ssl_socket = PySslSocket { - ctx: PyRwLock::new(zelf.clone()), - connection: PyRwLock::new(SslConnection::Bio(stream)), - socket_type, - server_hostname: args.server_hostname, - owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?), - }; + // Try DER format + // Basic validation: CRL should start with SEQUENCE tag (0x30) + if !data.is_empty() && data[0] == 0x30 { + return Ok(CertificateRevocationListDer::from(data.to_vec())); + } - // Convert to PyRef (heap allocation) to avoid use-after-free - let py_ref = - py_ssl_socket.into_ref_with_type(vm, PySslSocket::class(&vm.ctx).to_owned())?; - - // Set SNI callback data if callback is configured - if zelf.sni_callback.lock().is_some() { - unsafe { - let ssl_ptr = py_ref.connection.read().ssl().as_ptr(); - - // Store callback data in SSL ex_data - let callback_data = Box::new(SniCallbackData { - ssl_context: zelf.clone(), - vm_ptr: vm as *const _, - }); - let idx = get_sni_ex_data_index(); - sys::SSL_set_ex_data(ssl_ptr, idx, Box::into_raw(callback_data) as *mut _); - - // Store PyRef pointer (heap-allocated) in ex_data index 0 - sys::SSL_set_ex_data(ssl_ptr, 0, &*py_ref as *const _ as *mut _); + Err("Not a valid CRL file".to_string()) + } + + /// Helper: Load CRL from file + fn load_crl_from_file( + &self, + path: &str, + vm: &VirtualMachine, + ) -> PyResult>> { + let data = std::fs::read(path).map_err(|e| match e.kind() { + std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => { + e.into_pyexception(vm) } + _ => vm.new_os_error(e.to_string()), + })?; + + match self.try_parse_crl(&data) { + Ok(crl) => Ok(Some(crl)), + Err(_) => Ok(None), // Not a CRL file, might be a cert file } + } - // Set session if provided - if let Some(session) = args.session - && !vm.is_none(&session) - { - py_ref.set_session(session, vm)?; + /// Helper: Parse cadata argument (str or bytes) + fn parse_cadata_arg(&self, arg: &PyObjectRef, vm: &VirtualMachine) -> PyResult> { + if let Ok(s) = PyStrRef::try_from_object(vm, arg.clone()) { + Ok(s.as_str().as_bytes().to_vec()) + } else if let Ok(b) = ArgBytesLike::try_from_object(vm, arg.clone()) { + Ok(b.borrow_buf().to_vec()) + } else { + Err(vm.new_type_error("cadata should be a str or bytes".to_owned())) } + } - Ok(py_ref.into()) + /// Helper: Update certificate statistics + fn update_cert_stats(&self, stats: cert::CertStats) { + *self.x509_cert_count.write() += stats.total_certs; + *self.ca_cert_count.write() += stats.ca_certs; } } - #[derive(FromArgs)] - #[allow(dead_code)] // Fields will be used when _wrap_bio is fully implemented - struct WrapBioArgs { - incoming: PyRef, - outgoing: PyRef, - server_side: bool, - #[pyarg(any, default)] - server_hostname: Option, - #[pyarg(named, default)] - owner: Option, - #[pyarg(named, default)] - session: Option, - } + impl Constructor for PySSLContext { + type Args = (i32,); - #[derive(FromArgs)] - struct WrapSocketArgs { - sock: PyRef, - server_side: bool, - #[pyarg(any, default)] - server_hostname: Option, - #[pyarg(named, default)] - owner: Option, - #[pyarg(named, default)] - session: Option, - } + fn py_new(cls: PyTypeRef, (protocol,): Self::Args, vm: &VirtualMachine) -> PyResult { + // Validate protocol + match protocol { + PROTOCOL_TLS | PROTOCOL_TLS_CLIENT | PROTOCOL_TLS_SERVER | PROTOCOL_TLSv1_2 + | PROTOCOL_TLSv1_3 => { + // Valid protocols + } + PROTOCOL_TLSv1 | PROTOCOL_TLSv1_1 => { + return Err(vm.new_value_error( + "TLS 1.0 and 1.1 are not supported by rustls for security reasons", + )); + } + _ => { + return Err(vm.new_value_error(format!("invalid protocol version: {protocol}"))); + } + } - #[derive(FromArgs)] - struct LoadVerifyLocationsArgs { - #[pyarg(any, default)] - cafile: Option, - #[pyarg(any, default)] - capath: Option, - #[pyarg(any, default)] - cadata: Option>, - } + // Set default options + // OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 | OP_NO_COMPRESSION | + // OP_CIPHER_SERVER_PREFERENCE | OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE | + // OP_ENABLE_MIDDLEBOX_COMPAT + let default_options = OP_ALL + | OP_NO_SSLv2 + | OP_NO_SSLv3 + | OP_NO_COMPRESSION + | OP_CIPHER_SERVER_PREFERENCE + | OP_SINGLE_DH_USE + | OP_SINGLE_ECDH_USE + | OP_ENABLE_MIDDLEBOX_COMPAT; + + // Set default verify_mode based on protocol + // PROTOCOL_TLS_CLIENT defaults to CERT_REQUIRED + // PROTOCOL_TLS_SERVER defaults to CERT_NONE + let default_verify_mode = if protocol == PROTOCOL_TLS_CLIENT { + CERT_REQUIRED + } else { + CERT_NONE + }; - #[derive(FromArgs)] - struct LoadCertChainArgs { - certfile: FsPath, - #[pyarg(any, optional)] - keyfile: Option, - #[pyarg(any, optional)] - password: Option>, - } + // Set default verify_flags based on protocol + // Both PROTOCOL_TLS_CLIENT and PROTOCOL_TLS_SERVER only set VERIFY_X509_TRUSTED_FIRST + // Note: VERIFY_X509_PARTIAL_CHAIN and VERIFY_X509_STRICT are NOT set here + // - they're only added by create_default_context() in Python's ssl.py + let default_verify_flags = VERIFY_DEFAULT | VERIFY_X509_TRUSTED_FIRST; + + // Set minimum and maximum protocol versions based on protocol constant + // specific protocol versions fix both min and max + let (min_version, max_version) = match protocol { + PROTOCOL_TLSv1_2 => (PROTO_TLSv1_2, PROTO_TLSv1_2), // Only TLS 1.2 + PROTOCOL_TLSv1_3 => (PROTO_TLSv1_3, PROTO_TLSv1_3), // Only TLS 1.3 + _ => (PROTO_MINIMUM_SUPPORTED, PROTO_MAXIMUM_SUPPORTED), // Auto-negotiate + }; - // Err is true if the socket is blocking - type SocketDeadline = Result; + // IMPORTANT: Create shared session cache BEFORE PySSLContext + // Both client_session_cache and PythonClientSessionStore.session_cache + // MUST point to the same HashMap to ensure Python-level and Rustls-level + // sessions are synchronized + let shared_session_cache = Arc::new(ParkingRwLock::new(HashMap::new())); + let rustls_client_store = Arc::new(PythonClientSessionStore { + inner: Arc::new(rustls::client::ClientSessionMemoryCache::new( + SSL_SESSION_CACHE_SIZE, + )), + session_cache: shared_session_cache.clone(), + }); - enum SelectRet { - Nonblocking, - TimedOut, - IsBlocking, - Closed, - Ok, + PySSLContext { + protocol, + check_hostname: PyRwLock::new(protocol == PROTOCOL_TLS_CLIENT), + verify_mode: PyRwLock::new(default_verify_mode), + verify_flags: PyRwLock::new(default_verify_flags), + client_config: PyRwLock::new(None), + server_config: PyRwLock::new(None), + root_certs: PyRwLock::new(RootCertStore::empty()), + ca_certs_der: PyRwLock::new(Vec::new()), + capath_certs_der: PyRwLock::new(Vec::new()), + crls: PyRwLock::new(Vec::new()), + cert_keys: PyRwLock::new(Vec::new()), + options: PyRwLock::new(default_options), + alpn_protocols: PyRwLock::new(Vec::new()), + require_alpn_match: PyRwLock::new(false), + post_handshake_auth: PyRwLock::new(false), + num_tickets: PyRwLock::new(2), // TLS 1.3 default + minimum_version: PyRwLock::new(min_version), + maximum_version: PyRwLock::new(max_version), + sni_callback: PyRwLock::new(None), + msg_callback: PyRwLock::new(None), + ecdh_curve: PyRwLock::new(None), + ca_cert_count: PyRwLock::new(0), + x509_cert_count: PyRwLock::new(0), + // Use the shared cache created above + client_session_cache: shared_session_cache, + rustls_session_store: rustls_client_store, + rustls_server_session_store: rustls::server::ServerSessionMemoryCache::new( + SSL_SESSION_CACHE_SIZE, + ), + server_ticketer: rustls::crypto::aws_lc_rs::Ticketer::new() + .expect("Failed to create shared ticketer for TLS 1.2 session resumption"), + accept_count: AtomicUsize::new(0), + session_hits: AtomicUsize::new(0), + selected_ciphers: PyRwLock::new(None), + } + .into_ref_with_type(vm, cls) + .map(Into::into) + } } - #[derive(Clone, Copy)] - enum SslNeeds { - Read, - Write, + // SSLSocket - represents a TLS-wrapped socket + #[pyattr] + #[pyclass(name = "_SSLSocket", module = "ssl")] + #[derive(Debug, PyPayload)] + pub(crate) struct PySSLSocket { + // Underlying socket + sock: PyObjectRef, + // SSL context + context: PyRwLock>, + // Server-side or client-side + server_side: bool, + // Server hostname for SNI + server_hostname: PyRwLock>, + // TLS connection state + connection: PyMutex>, + // Handshake completed flag + handshake_done: PyMutex, + // Session was reused (for session resumption tracking) + session_was_reused: PyMutex, + // Owner (SSLSocket instance that owns this _SSLSocket) + owner: PyRwLock>, + // Session for resumption + session: PyRwLock>, + // Verified certificate chain (built during verification) + #[allow(dead_code)] + verified_chain: PyRwLock>>>, + // MemoryBIO mode (optional) + incoming_bio: Option>, + outgoing_bio: Option>, + // SNI certificate resolver state (for server-side only) + sni_state: PyRwLock>>>, + // Pending context change (for SNI callback deferred handling) + pending_context: PyRwLock>>, + // Buffer to store ClientHello for connection recreation + client_hello_buffer: PyMutex>>, + // Shutdown state for tracking close-notify exchange + shutdown_state: PyMutex, + // Deferred client certificate verification error (for TLS 1.3) + // Stores error message if client cert verification failed during handshake + // Error is raised on first I/O operation after handshake + // Using Arc to share with the certificate verifier + deferred_cert_error: Arc>>, } - struct SocketStream(PyRef); + // Shutdown state for tracking close-notify exchange + #[derive(Debug, Clone, Copy, PartialEq)] + enum ShutdownState { + NotStarted, // unwrap() not called yet + SentCloseNotify, // close-notify sent, waiting for peer's response + Completed, // unwrap() completed successfully + } - impl SocketStream { - fn timeout_deadline(&self) -> SocketDeadline { - self.0.get_timeout().map(|d| Instant::now() + d) + #[pyclass(with(Constructor), flags(BASETYPE))] + impl PySSLSocket { + // Check if this is BIO mode + pub(crate) fn is_bio_mode(&self) -> bool { + self.incoming_bio.is_some() && self.outgoing_bio.is_some() } - fn select(&self, needs: SslNeeds, deadline: &SocketDeadline) -> SelectRet { - let sock = match self.0.sock_opt() { - Some(s) => s, - None => return SelectRet::Closed, - }; - let deadline = match &deadline { - Ok(deadline) => match deadline.checked_duration_since(Instant::now()) { - Some(deadline) => deadline, - None => return SelectRet::TimedOut, - }, - Err(true) => return SelectRet::IsBlocking, - Err(false) => return SelectRet::Nonblocking, - }; - let res = socket::sock_select( - &sock, - match needs { - SslNeeds::Read => socket::SelectKind::Read, - SslNeeds::Write => socket::SelectKind::Write, - }, - Some(deadline), - ); - match res { - Ok(true) => SelectRet::TimedOut, - _ => SelectRet::Ok, - } + // Get incoming BIO reference (for EOF checking) + pub(crate) fn incoming_bio(&self) -> Option { + self.incoming_bio.as_ref().map(|bio| bio.clone().into()) } - fn socket_needs( - &self, - err: &ssl::Error, - deadline: &SocketDeadline, - ) -> (Option, SelectRet) { - let needs = match err.code() { - ssl::ErrorCode::WANT_READ => Some(SslNeeds::Read), - ssl::ErrorCode::WANT_WRITE => Some(SslNeeds::Write), - _ => None, - }; - let state = needs.map_or(SelectRet::Ok, |needs| self.select(needs, deadline)); - (needs, state) + // Check for deferred certificate verification errors (TLS 1.3) + // If an error exists, raise it and clear it from storage + fn check_deferred_cert_error(&self, vm: &VirtualMachine) -> PyResult<()> { + let error_opt = self.deferred_cert_error.read().clone(); + if let Some(error_msg) = error_opt { + // Clear the error so it's only raised once + *self.deferred_cert_error.write() = None; + // Raise OSError with the stored error message + return Err(vm.new_os_error(error_msg)); + } + Ok(()) } - } - fn socket_closed_error(vm: &VirtualMachine) -> PyBaseExceptionRef { - vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "Underlying socket has been closed.".to_owned(), - ) - } + // Get socket timeout as Duration + pub(crate) fn get_socket_timeout(&self, vm: &VirtualMachine) -> PyResult> { + if self.is_bio_mode() { + return Ok(None); + } - // BIO stream wrapper to implement Read/Write traits for MemoryBIO - struct BioStream { - inbio: PyRef, - outbio: PyRef, - } + // Get timeout from socket + let timeout_obj = self.sock.get_attr("gettimeout", vm)?.call((), vm)?; - impl Read for BioStream { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - // Read from incoming MemoryBIO - unsafe { - let nbytes = sys::BIO_read( - self.inbio.bio, - buf.as_mut_ptr() as *mut _, - buf.len().min(i32::MAX as usize) as i32, - ); - if nbytes < 0 { - // BIO_read returns -1 on error or when no data is available - // Check if it's a retry condition (WANT_READ) - Err(std::io::Error::new( - std::io::ErrorKind::WouldBlock, - "BIO has no data available", - )) + // timeout can be None (blocking), 0.0 (non-blocking), or positive float + if vm.is_none(&timeout_obj) { + // None means blocking forever + Ok(None) + } else { + let timeout_float: f64 = timeout_obj.try_into_value(vm)?; + if timeout_float <= 0.0 { + // 0 means non-blocking + Ok(Some(Duration::from_secs(0))) } else { - Ok(nbytes as usize) + // Positive timeout + Ok(Some(Duration::from_secs_f64(timeout_float))) } } } - } - impl Write for BioStream { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - // Write to outgoing MemoryBIO - unsafe { - let nbytes = sys::BIO_write( - self.outbio.bio, - buf.as_ptr() as *const _, - buf.len().min(i32::MAX as usize) as i32, - ); - if nbytes < 0 { - return Err(std::io::Error::other("BIO write failed")); + // Create and store a session object after successful handshake + fn create_session_after_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + // Only create session for client-side connections + if self.server_side { + return Ok(()); + } + + // Check if session already exists + let session_opt = self.session.read().clone(); + if let Some(ref s) = session_opt { + if vm.is_none(s) { + } else { + return Ok(()); } - Ok(nbytes as usize) } - } - fn flush(&mut self) -> std::io::Result<()> { - // MemoryBIO doesn't need flushing + // Get server hostname + let server_name = self.server_hostname.read().clone(); + + // Try to get session data from context's session cache + // IMPORTANT: Acquire and release locks quickly to avoid deadlock + let context = self.context.read(); + let session_cache_arc = context.client_session_cache.clone(); + drop(context); // Release context lock ASAP + + let (session_id, creation_time, lifetime) = if let Some(ref name) = server_name { + let key = name.as_bytes().to_vec(); + + // Clone the data we need while holding the lock, then immediately release + let session_data_opt = { + let cache_guard = session_cache_arc.read(); + cache_guard.get(&key).cloned() // Clone Arc> + }; // Lock released here + + if let Some(session_data_arc) = session_data_opt { + let data = session_data_arc.lock(); + let result = (data.session_id.clone(), data.creation_time, data.lifetime); + drop(data); // Explicit unlock + result + } else { + // Create new session ID if not in cache + let time = std::time::SystemTime::now(); + (generate_session_id_from_metadata(name, &time), time, 7200) + } + } else { + // No server name, use defaults + let time = std::time::SystemTime::now(); + (vec![0; 16], time, 7200) + }; + + // Create a new SSLSession object with real metadata + let session = PySSLSession { + // Use dummy session data to indicate we have a ticket + // TLS 1.2+ always uses session tickets/resumption + session_data: vec![1], // Non-empty to indicate has_ticket=True + session_id, + creation_time, + lifetime, + }; + + let py_session = session.into_pyobject(vm); + + *self.session.write() = Some(py_session); + Ok(()) } - } - // Enum to represent different SSL connection modes - enum SslConnection { - Socket(ssl::SslStream), - Bio(ssl::SslStream), - } + // Complete handshake and create session + /// Track which CA certificate from capath was used to verify peer + /// + /// This simulates lazy loading behavior: capath certificates + /// are only added to get_ca_certs() after they're actually used in a handshake. + fn track_used_ca_from_capath(&self) -> Result<(), String> { + let context = self.context.read(); + let capath_certs = context.capath_certs_der.read(); + + // No capath certs to track + if capath_certs.is_empty() { + return Ok(()); + } - impl SslConnection { - // Get a reference to the SSL object - fn ssl(&self) -> &ssl::SslRef { - match self { - SslConnection::Socket(stream) => stream.ssl(), - SslConnection::Bio(stream) => stream.ssl(), + // Get peer certificate chain + let conn_guard = self.connection.lock(); + let conn = conn_guard.as_ref().ok_or("No connection")?; + + let peer_certs = conn.peer_certificates().ok_or("No peer certificates")?; + + if peer_certs.is_empty() { + return Ok(()); } - } - // Get underlying socket stream reference (only for socket mode) - fn get_ref(&self) -> Option<&SocketStream> { - match self { - SslConnection::Socket(stream) => Some(stream.get_ref()), - SslConnection::Bio(_) => None, + // Get the top certificate in the chain (closest to root) + // Note: Server usually doesn't send the root CA, so we check the last cert's issuer + let top_cert_der = peer_certs.last().unwrap(); + let (_, top_cert) = x509_parser::parse_x509_certificate(top_cert_der) + .map_err(|e| format!("Failed to parse top cert: {e}"))?; + + let top_issuer = top_cert.issuer(); + + // Find matching CA in capath certs + for ca_der in capath_certs.iter() { + let (_, ca) = x509_parser::parse_x509_certificate(ca_der) + .map_err(|e| format!("Failed to parse CA: {e}"))?; + + // Check if this CA is self-signed and matches the issuer + if ca.subject() == ca.issuer() // Self-signed (root CA) + && ca.subject() == top_issuer + // Matches top cert's issuer + { + // Check if not already in ca_certs_der + let mut ca_certs_der = context.ca_certs_der.write(); + if !ca_certs_der.iter().any(|c| c == ca_der) { + ca_certs_der.push(ca_der.clone()); + } + break; + } } - } - // Check if this is in BIO mode - fn is_bio(&self) -> bool { - matches!(self, SslConnection::Bio(_)) + Ok(()) } - // Perform SSL handshake - fn do_handshake(&mut self) -> Result<(), ssl::Error> { - match self { - SslConnection::Socket(stream) => stream.do_handshake(), - SslConnection::Bio(stream) => stream.do_handshake(), + fn complete_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + *self.handshake_done.lock() = true; + + // Check if session was resumed before creating session object + let conn_guard = self.connection.lock(); + if let Some(ref conn) = *conn_guard { + let was_resumed = conn.is_session_resumed(); + *self.session_was_reused.lock() = was_resumed; + + // Update context session statistics if server-side + if self.server_side { + let context = self.context.read(); + // Increment accept count for every successful server handshake + context.accept_count.fetch_add(1, Ordering::SeqCst); + // Increment hits count if session was resumed + if was_resumed { + context.session_hits.fetch_add(1, Ordering::SeqCst); + } + } } - } + drop(conn_guard); - // Write data to SSL connection - fn ssl_write(&mut self, buf: &[u8]) -> Result { - match self { - SslConnection::Socket(stream) => stream.ssl_write(buf), - SslConnection::Bio(stream) => stream.ssl_write(buf), + // Track CA certificate used during handshake (client-side only) + // This simulates lazy loading behavior for capath certificates + if !self.server_side { + // Don't fail handshake if tracking fails + let _ = self.track_used_ca_from_capath(); } + + self.create_session_after_handshake(vm)?; + Ok(()) } - // Read data from SSL connection - fn ssl_read(&mut self, buf: &mut [u8]) -> Result { - match self { - SslConnection::Socket(stream) => stream.ssl_read(buf), - SslConnection::Bio(stream) => stream.ssl_read(buf), + // Internal implementation with timeout control + pub(crate) fn sock_wait_for_io_impl( + &self, + kind: SelectKind, + vm: &VirtualMachine, + ) -> PyResult { + if self.is_bio_mode() { + // BIO mode doesn't use select + return Ok(false); } - } - // Get SSL shutdown state - fn get_shutdown(&mut self) -> ssl::ShutdownState { - match self { - SslConnection::Socket(stream) => stream.get_shutdown(), - SslConnection::Bio(stream) => stream.get_shutdown(), + // Get timeout + let timeout = self.get_socket_timeout(vm)?; + + // Check for non-blocking mode (timeout = 0) + if let Some(t) = timeout + && t.is_zero() + { + // Non-blocking mode - don't use select + return Ok(false); } - } - } - #[pyattr] - #[pyclass(module = "ssl", name = "_SSLSocket", traverse)] - #[derive(PyPayload)] - struct PySslSocket { - ctx: PyRwLock>, - #[pytraverse(skip)] - connection: PyRwLock, - #[pytraverse(skip)] - socket_type: SslServerOrClient, - server_hostname: Option, - owner: PyRwLock>>, - } + // Use select with the effective timeout + let py_socket: PyRef = self.sock.clone().try_into_value(vm)?; + let socket = py_socket + .sock() + .map_err(|e| vm.new_os_error(format!("Failed to get socket: {e}")))?; - impl fmt::Debug for PySslSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("_SSLSocket") + let timed_out = sock_select(&socket, kind, timeout) + .map_err(|e| vm.new_os_error(format!("select failed: {e}")))?; + + Ok(timed_out) } - } - #[pyclass(flags(IMMUTABLETYPE))] - impl PySslSocket { - #[pygetset] - fn owner(&self) -> Option { - self.owner.read().as_ref().and_then(|weak| weak.upgrade()) + // SNI (Server Name Indication) Helper Methods: + // These methods support the server-side handshake SNI callback mechanism + + /// Check if this is the first read during handshake (for SNI callback) + /// Returns true if we haven't processed ClientHello yet, regardless of SNI presence + pub(crate) fn is_first_sni_read(&self) -> bool { + self.client_hello_buffer.lock().is_none() } - #[pygetset(setter)] - fn set_owner(&self, owner: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let mut lock = self.owner.write(); - lock.take(); - *lock = Some(owner.downgrade(None, vm)?); - Ok(()) + + /// Check if SNI callback is configured + pub(crate) fn has_sni_callback(&self) -> bool { + self.context.read().sni_callback.read().is_some() } - #[pygetset] - fn server_side(&self) -> bool { - self.socket_type == SslServerOrClient::Server + + /// Save ClientHello data from PyObjectRef for potential connection recreation + pub(crate) fn save_client_hello_from_bytes(&self, bytes_data: &[u8]) { + *self.client_hello_buffer.lock() = Some(bytes_data.to_vec()); } - #[pygetset] - fn context(&self) -> PyRef { - self.ctx.read().clone() + + /// Get the extracted SNI name from resolver + pub(crate) fn get_extracted_sni_name(&self) -> Option { + self.sni_state + .read() + .as_ref() + .and_then(|arc| arc.lock().1.clone()) } - #[pygetset(setter)] - fn set_context(&self, value: PyRef, vm: &VirtualMachine) -> PyResult<()> { - // Update the SSL context in the underlying SSL object - let stream = self.connection.read(); - - // Set the new SSL_CTX on the SSL object - unsafe { - let result = SSL_set_SSL_CTX(stream.ssl().as_ptr(), value.ctx().as_ptr()); - if result.is_null() { - return Err(vm.new_runtime_error("Failed to set SSL context".to_owned())); + + /// Invoke the Python SNI callback + pub(crate) fn invoke_sni_callback( + &self, + sni_name: Option<&str>, + vm: &VirtualMachine, + ) -> PyResult<()> { + let callback = self + .context + .read() + .sni_callback + .read() + .clone() + .ok_or_else(|| vm.new_value_error("SNI callback not set"))?; + + let ssl_sock = self.owner.read().clone().unwrap_or(vm.ctx.none()); + let server_name_py: PyObjectRef = match sni_name { + Some(name) => vm.ctx.new_str(name.to_string()).into(), + None => vm.ctx.none(), + }; + let initial_context: PyObjectRef = self.context.read().clone().into(); + + let result = callback.call((ssl_sock, server_name_py, initial_context), vm)?; + + // Check return value type (must be None or integer) + if !vm.is_none(&result) { + // Try to convert to integer + if result.try_to_value::(vm).is_err() { + // Type conversion failed - raise TypeError as unraisable + let type_error = vm.new_type_error(format!( + "servername callback must return None or an integer, not '{}'", + result.class().name() + )); + vm.run_unraisable(type_error, None, result.clone()); + + // Return SSL error with reason set to TLSV1_ALERT_INTERNAL_ERROR + // + // RUSTLS API LIMITATION: + // We cannot send a TLS InternalError alert to the client here because: + // 1. Rustls does not provide a public API like send_fatal_alert() + // 2. This method is called AFTER dropping the connection lock (to prevent deadlock) + // 3. By the time we detect the error, the connection is no longer available + // + // CPython/OpenSSL behavior: + // - SNI callback runs inside SSL_do_handshake with connection active + // - Sets *al = SSL_AD_INTERNAL_ERROR + // - OpenSSL automatically sends alert before returning + // + // RustPython/Rustls behavior: + // - SNI callback runs after dropping connection lock (deadlock prevention) + // - Exception has _reason='TLSV1_ALERT_INTERNAL_ERROR' for error reporting + // - TCP connection closes without sending TLS alert to client + // + // If rustls adds send_fatal_alert() API in the future, we should: + // - Re-acquire connection lock after callback + // - Call: connection.send_fatal_alert(AlertDescription::InternalError) + // - Then close connection + let exc = vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "SNI callback returned invalid type".to_owned(), + ); + let _ = exc.as_object().set_attr( + "reason", + vm.ctx.new_str("TLSV1_ALERT_INTERNAL_ERROR"), + vm, + ); + return Err(exc); } } - // Update self.ctx to the new context - *self.ctx.write() = value; Ok(()) } - #[pygetset] - fn server_hostname(&self) -> Option { - self.server_hostname.clone() + + // Helper to call socket methods, bypassing any SSL wrapper + pub(crate) fn sock_recv(&self, size: usize, vm: &VirtualMachine) -> PyResult { + // In BIO mode, read from incoming BIO + if let Some(ref bio) = self.incoming_bio { + let bio_obj: PyObjectRef = bio.clone().into(); + let read_method = bio_obj.get_attr("read", vm)?; + return read_method.call((vm.ctx.new_int(size),), vm); + } + + // Normal socket mode + let socket_mod = vm.import("socket", 0)?; + let socket_class = socket_mod.get_attr("socket", vm)?; + + // Call socket.socket.recv(self.sock, size) + let recv_method = socket_class.get_attr("recv", vm)?; + recv_method.call((self.sock.clone(), vm.ctx.new_int(size)), vm) + } + + pub(crate) fn sock_send( + &self, + data: Vec, + vm: &VirtualMachine, + ) -> PyResult { + // In BIO mode, write to outgoing BIO + if let Some(ref bio) = self.outgoing_bio { + let bio_obj: PyObjectRef = bio.clone().into(); + let write_method = bio_obj.get_attr("write", vm)?; + return write_method.call((vm.ctx.new_bytes(data),), vm); + } + + // Normal socket mode + let socket_mod = vm.import("socket", 0)?; + let socket_class = socket_mod.get_attr("socket", vm)?; + + // Call socket.socket.send(self.sock, data) + let send_method = socket_class.get_attr("send", vm)?; + send_method.call((self.sock.clone(), vm.ctx.new_bytes(data)), vm) } #[pymethod] - fn getpeercert( + fn __repr__(&self) -> String { + "".to_string() + } + + // Helper function to convert Python PROTO_* constants to rustls versions + fn get_rustls_versions( + minimum: i32, + maximum: i32, + options: i32, + ) -> &'static [&'static rustls::SupportedProtocolVersion] { + // Rustls only supports TLS 1.2 and 1.3 + // PROTO_TLSv1_2 = 0x0303, PROTO_TLSv1_3 = 0x0304 + // PROTO_MINIMUM_SUPPORTED = -2, PROTO_MAXIMUM_SUPPORTED = -1 + // If minimum and maximum are 0, use default (both TLS 1.2 and 1.3) + + // Static arrays for single-version configurations + static TLS12_ONLY: &[&rustls::SupportedProtocolVersion] = &[&TLS12]; + static TLS13_ONLY: &[&rustls::SupportedProtocolVersion] = &[&TLS13]; + + // Normalize special values: -2 (MINIMUM_SUPPORTED) → TLS 1.2, -1 (MAXIMUM_SUPPORTED) → TLS 1.3 + let min = if minimum == -2 { + PROTO_TLSv1_2 + } else { + minimum + }; + let max = if maximum == -1 { + PROTO_TLSv1_3 + } else { + maximum + }; + + // Check if versions are disabled by options + let tls12_disabled = (options & OP_NO_TLSv1_2) != 0; + let tls13_disabled = (options & OP_NO_TLSv1_3) != 0; + + let want_tls12 = (min == 0 || min <= PROTO_TLSv1_2) + && (max == 0 || max >= PROTO_TLSv1_2) + && !tls12_disabled; + let want_tls13 = (min == 0 || min <= PROTO_TLSv1_3) + && (max == 0 || max >= PROTO_TLSv1_3) + && !tls13_disabled; + + match (want_tls12, want_tls13) { + (true, true) => rustls::DEFAULT_VERSIONS, // Both TLS 1.2 and 1.3 + (true, false) => TLS12_ONLY, // Only TLS 1.2 + (false, true) => TLS13_ONLY, // Only TLS 1.3 + (false, false) => rustls::DEFAULT_VERSIONS, // Fallback to default + } + } + + /// Helper: Prepare TLS versions from context settings + fn prepare_tls_versions(&self) -> &'static [&'static rustls::SupportedProtocolVersion] { + let ctx = self.context.read(); + let min_ver = *ctx.minimum_version.read(); + let max_ver = *ctx.maximum_version.read(); + let options = *ctx.options.read(); + Self::get_rustls_versions(min_ver, max_ver, options) + } + + /// Helper: Prepare KX groups (ECDH curve) from context settings + fn prepare_kx_groups( &self, - binary: OptionalArg, vm: &VirtualMachine, - ) -> PyResult> { - let binary = binary.unwrap_or(false); - let stream = self.connection.read(); - if !stream.ssl().is_init_finished() { - return Err(vm.new_value_error("handshake not done yet")); + ) -> PyResult>> { + let ctx = self.context.read(); + let ecdh_curve = ctx.ecdh_curve.read().clone(); + drop(ctx); + + if let Some(ref curve_name) = ecdh_curve { + match curve_name_to_kx_group(curve_name) { + Ok(groups) => Ok(Some(groups)), + Err(e) => Err(vm.new_value_error(format!("Failed to set ECDH curve: {e}"))), + } + } else { + Ok(None) } + } - let peer_cert = stream.ssl().peer_certificate(); - let Some(cert) = peer_cert else { - return Ok(None); + /// Helper: Prepare all common protocol settings (versions, KX groups, ciphers, ALPN) + fn prepare_protocol_settings(&self, vm: &VirtualMachine) -> PyResult { + let ctx = self.context.read(); + let versions = self.prepare_tls_versions(); + let kx_groups = self.prepare_kx_groups(vm)?; + let cipher_suites = ctx.selected_ciphers.read().clone(); + let alpn_protocols = ctx.alpn_protocols.read().clone(); + + Ok(ProtocolSettings { + versions, + kx_groups, + cipher_suites, + alpn_protocols, + }) + } + + /// Initialize server-side TLS connection with configuration + /// + /// This method handles all server-side setup including: + /// - Certificate and key validation + /// - Client authentication configuration + /// - SNI (Server Name Indication) setup + /// - ALPN protocol negotiation + /// - Session resumption configuration + /// + /// Returns the configured ServerConnection. + fn initialize_server_connection( + &self, + conn_guard: &mut Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + let ctx = self.context.read(); + let cert_keys = ctx.cert_keys.read(); + + if cert_keys.is_empty() { + return Err(vm.new_value_error( + "Server-side connection requires certificate and key (use load_cert_chain)", + )); + } + + // Clone cert_keys for use in config + // PrivateKeyDer doesn't implement Clone, use clone_key() + let cert_keys_clone: Vec = cert_keys + .iter() + .map(|(ck, pk)| (ck.clone(), pk.clone_key())) + .collect(); + drop(cert_keys); + + // Prepare common protocol settings (TLS versions, ECDH curve, cipher suites, ALPN) + let protocol_settings = self.prepare_protocol_settings(vm)?; + let min_ver = *ctx.minimum_version.read(); + + // Check if client certificate verification is required + let verify_mode = *ctx.verify_mode.read(); + let root_store = ctx.root_certs.read(); + let pha_enabled = *ctx.post_handshake_auth.read(); + + // Check if TLS 1.3 is being used + let is_tls13 = min_ver >= PROTO_TLSv1_3; + + // For TLS 1.3: always use deferred validation for client certificates + // For TLS 1.2: use immediate validation during handshake + let use_deferred_validation = is_tls13 + && !pha_enabled + && (verify_mode == CERT_REQUIRED || verify_mode == CERT_OPTIONAL); + + // For TLS 1.3 + PHA: if PHA is enabled, don't request cert in initial handshake + // The certificate will be requested later via verify_client_post_handshake() + let request_initial_cert = if pha_enabled { + // PHA enabled: don't request cert initially (will use PHA later) + false + } else if verify_mode == CERT_REQUIRED || verify_mode == CERT_OPTIONAL { + // PHA not enabled or TLS 1.2: request cert in initial handshake + true + } else { + // CERT_NONE + false }; - if binary { - // Return DER-encoded certificate - cert_to_py(vm, &cert, true).map(Some) + // Check if SNI callback is set + let sni_callback = ctx.sni_callback.read().clone(); + let use_sni_resolver = sni_callback.is_some(); + + // Create SNI state if needed (to be stored in PySSLSocket later) + // For SNI, use the first cert_key pair as the initial certificate + let sni_state: Option>> = if use_sni_resolver { + // Use first cert_key as initial certificate for SNI + // Extract CertifiedKey from tuple + let (first_cert_key, _) = &cert_keys_clone[0]; + let first_cert_key = first_cert_key.clone(); + + // Check if we already have existing SNI state (from previous connection) + let existing_sni_state = self.sni_state.read().clone(); + + if let Some(sni_state_arc) = existing_sni_state { + // Reuse existing Arc and update its contents + // This is crucial: rustls SniCertResolver holds references to this Arc + let mut state = sni_state_arc.lock(); + state.0 = first_cert_key; + state.1 = None; // Reset SNI name for new connection + drop(state); + + // Return the existing Arc (not a new one!) + Some(sni_state_arc) + } else { + // First connection: create new SNI state + Some(Arc::new(ParkingMutex::new((first_cert_key, None)))) + } } else { - // Check verify_mode - unsafe { - let ssl_ctx = sys::SSL_get_SSL_CTX(stream.ssl().as_ptr()); - let verify_mode = sys::SSL_CTX_get_verify_mode(ssl_ctx); - if (verify_mode & sys::SSL_VERIFY_PEER as libc::c_int) == 0 { - // Return empty dict when SSL_VERIFY_PEER is not set - Ok(Some(vm.ctx.new_dict().into())) - } else { - // Return decoded certificate - cert_to_py(vm, &cert, false).map(Some) - } + None + }; + + // Determine which cert resolver to use + // Priority: SNI > Multi-cert/Single-cert via MultiCertResolver + let cert_resolver: Option> = if use_sni_resolver { + // SNI takes precedence - use first cert_key for initial setup + sni_state.as_ref().map(|sni_state_arc| { + Arc::new(SniCertResolver { + sni_state: sni_state_arc.clone(), + }) as Arc + }) + } else { + // Use MultiCertResolver for all cases (single or multiple certs) + // Extract CertifiedKey from tuples for MultiCertResolver + let cert_keys_only: Vec> = + cert_keys_clone.iter().map(|(ck, _)| ck.clone()).collect(); + Some(Arc::new(MultiCertResolver::new(cert_keys_only))) + }; + + // Extract cert_chain and private_key from first cert_key + // + // Note: Since we always use cert_resolver now, these values won't actually be used + // by create_server_config. But we still need to provide them for the API signature. + let (first_cert_key, _) = &cert_keys_clone[0]; + let certs_clone = first_cert_key.cert.clone(); + + // Provide a dummy key since cert_resolver will handle cert selection + let key_clone = PrivateKeyDer::Pkcs8(Vec::new().into()); + + // Get shared server session storage and ticketer from context + let server_session_storage = ctx.rustls_server_session_store.clone(); + let server_ticketer = ctx.server_ticketer.clone(); + + // Build server config using compat helper + let config_options = ServerConfigOptions { + protocol_settings, + cert_chain: certs_clone, + private_key: key_clone, + root_store: if request_initial_cert { + Some(root_store.clone()) + } else { + None + }, + request_client_cert: request_initial_cert, + use_deferred_validation, + cert_resolver, + deferred_cert_error: if use_deferred_validation { + Some(self.deferred_cert_error.clone()) + } else { + None + }, + session_storage: Some(server_session_storage), + ticketer: Some(server_ticketer), + }; + + drop(root_store); + + // Check if we have a cached ServerConfig + let cached_config_arc = ctx.server_config.read().clone(); + drop(ctx); + + let config_arc = if let Some(cached) = cached_config_arc { + // Don't use cache when SNI is enabled, because each connection needs + // a fresh SniCertResolver with the correct Arc references + if use_sni_resolver { + let config = + create_server_config(config_options).map_err(|e| vm.new_value_error(e))?; + Arc::new(config) + } else { + cached } + } else { + let config = + create_server_config(config_options).map_err(|e| vm.new_value_error(e))?; + let config_arc = Arc::new(config); + + // Cache the ServerConfig for future connections + let ctx = self.context.read(); + *ctx.server_config.write() = Some(config_arc.clone()); + drop(ctx); + + config_arc + }; + + let conn = ServerConnection::new(config_arc).map_err(|e| { + vm.new_value_error(format!("Failed to create server connection: {e}")) + })?; + + *conn_guard = Some(TlsConnection::Server(conn)); + + // If ClientHello buffer exists (from SNI callback), re-inject it + if let Some(ref hello_data) = *self.client_hello_buffer.lock() + && let Some(TlsConnection::Server(ref mut server)) = *conn_guard + { + let mut cursor = std::io::Cursor::new(hello_data.as_slice()); + let _ = server.read_tls(&mut cursor); + + // Process the re-injected ClientHello + let _ = server.process_new_packets(); + + // DON'T clear buffer - keep it to prevent callback from being invoked again + // The buffer being non-empty signals that SNI callback was already processed + } + + // Store SNI state if we're using SNI resolver + if let Some(sni_state_arc) = sni_state { + *self.sni_state.write() = Some(sni_state_arc); } + + Ok(()) } #[pymethod] - fn get_unverified_chain(&self, vm: &VirtualMachine) -> PyResult> { - let stream = self.connection.read(); - let Some(chain) = stream.ssl().peer_cert_chain() else { - return Ok(None); - }; + fn do_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + // Check if handshake already done + if *self.handshake_done.lock() { + return Ok(()); + } - // Return Certificate objects - let certs: Vec = chain - .iter() - .map(|cert| unsafe { - sys::X509_up_ref(cert.as_ptr()); - let owned = X509::from_ptr(cert.as_ptr()); - cert_to_certificate(vm, owned) - }) - .collect::>()?; - Ok(Some(vm.ctx.new_list(certs))) + let mut conn_guard = self.connection.lock(); + + // Initialize connection if not already done + if conn_guard.is_none() { + // Check for pending context change (from SNI callback) + if let Some(new_ctx) = self.pending_context.write().take() { + *self.context.write() = new_ctx; + } + + if self.server_side { + // Server-side connection - delegate to helper method + self.initialize_server_connection(&mut conn_guard, vm)?; + } else { + // Client-side connection + let ctx = self.context.read(); + + // Prepare common protocol settings (TLS versions, ECDH curve, cipher suites, ALPN) + let protocol_settings = self.prepare_protocol_settings(vm)?; + + // Clone values we need before building config + let verify_mode = *ctx.verify_mode.read(); + let root_store_clone = ctx.root_certs.read().clone(); + let ca_certs_der_clone = ctx.ca_certs_der.read().clone(); + + // For client mTLS: extract cert_chain and private_key from first cert_key (if any) + // Now we store both CertifiedKey and PrivateKeyDer as tuple + let cert_keys_guard = ctx.cert_keys.read(); + let (cert_chain_clone, private_key_opt) = if !cert_keys_guard.is_empty() { + let (first_cert_key, private_key) = &cert_keys_guard[0]; + let certs = first_cert_key.cert.clone(); + (certs, Some(private_key.clone_key())) + } else { + (Vec::new(), None) + }; + drop(cert_keys_guard); + + let check_hostname = *ctx.check_hostname.read(); + let verify_flags = *ctx.verify_flags.read(); + + // Get session store before dropping ctx + let session_store = ctx.rustls_session_store.clone(); + + // Get CRLs for revocation checking + let crls_clone = ctx.crls.read().clone(); + + // Drop ctx early to avoid borrow conflicts + drop(ctx); + + // Build client config using compat helper + let config_options = ClientConfigOptions { + protocol_settings, + root_store: if verify_mode != CERT_NONE { + Some(root_store_clone) + } else { + None + }, + ca_certs_der: ca_certs_der_clone, + cert_chain: if !cert_chain_clone.is_empty() { + Some(cert_chain_clone) + } else { + None + }, + private_key: private_key_opt, + verify_server_cert: verify_mode != CERT_NONE, + check_hostname, + verify_flags, + session_store: Some(session_store), + crls: crls_clone, + }; + + let config = + create_client_config(config_options).map_err(|e| vm.new_value_error(e))?; + + // Parse server name for SNI + // Convert to ServerName + use rustls::pki_types::ServerName; + let hostname_opt = self.server_hostname.read().clone(); + + let server_name = if let Some(ref hostname) = hostname_opt { + // Use the provided hostname for SNI + ServerName::try_from(hostname.clone()).map_err(|e| { + vm.new_value_error(format!("Invalid server hostname: {e:?}")) + })? + } else { + // When server_hostname=None, use an IP address to suppress SNI + // no hostname = no SNI extension + ServerName::IpAddress( + std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)).into(), + ) + }; + + let conn = ClientConnection::new(Arc::new(config), server_name.clone()) + .map_err(|e| { + vm.new_value_error(format!("Failed to create client connection: {e}")) + })?; + + *conn_guard = Some(TlsConnection::Client(conn)); + } + } + + // Perform the actual handshake by exchanging data with the socket/BIO + match conn_guard.as_mut() { + Some(TlsConnection::Client(_conn)) => { + // CLIENT is simple - no SNI callback handling needed + ssl_do_handshake(conn_guard.as_mut().unwrap(), self, vm) + .map_err(|e| e.into_py_err(vm))?; + + drop(conn_guard); + self.complete_handshake(vm)?; + Ok(()) + } + Some(TlsConnection::Server(_conn)) => { + // Use OpenSSL-compatible handshake for server + // Handle SNI callback restart + match ssl_do_handshake(conn_guard.as_mut().unwrap(), self, vm) { + Ok(()) => { + // Handshake completed successfully + drop(conn_guard); + self.complete_handshake(vm)?; + Ok(()) + } + Err(SslError::SniCallbackRestart) => { + // SNI detected - need to call callback and recreate connection + + // CRITICAL: Drop connection lock BEFORE calling Python callback to avoid deadlock + // + // Deadlock scenario if we keep the lock: + // 1. This thread holds self.connection.lock() + // 2. Python callback invokes other SSL methods (e.g., getpeercert(), cipher()) + // 3. Those methods try to acquire self.connection.lock() again + // 4. PyMutex (parking_lot::Mutex) is not reentrant -> DEADLOCK + // + // Trade-off: By dropping the lock, we lose the ability to send TLS alerts + // because Rustls doesn't provide a send_fatal_alert() API. See detailed + // explanation in invoke_sni_callback() where we set _reason attribute. + drop(conn_guard); + + // Get the SNI name that was extracted (may be None if client didn't send SNI) + let sni_name = self.get_extracted_sni_name(); + + // Now safe to call Python callback (no locks held) + self.invoke_sni_callback(sni_name.as_deref(), vm)?; + + // Clear connection to trigger recreation + *self.connection.lock() = None; + + // Recursively call do_handshake to recreate with new context + self.do_handshake(vm) + } + Err(e) => { + // Other errors - convert to Python exception + drop(conn_guard); + Err(e.into_py_err(vm)) + } + } + } + None => unreachable!(), + } } #[pymethod] - fn get_verified_chain(&self, vm: &VirtualMachine) -> PyResult> { - let stream = self.connection.read(); - unsafe { - let chain = sys::SSL_get0_verified_chain(stream.ssl().as_ptr()); - if chain.is_null() { - return Ok(None); + fn read( + &self, + len: OptionalArg, + buffer: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + // Convert len to usize, defaulting to 1024 if not provided + // -1 means read all available data (treat as large buffer size) + let len_val = len.unwrap_or(PEM_BUFSIZE as isize); + let mut len = if len_val == -1 { + // -1 is only valid when a buffer is provided + match &buffer { + OptionalArg::Present(buf_arg) => buf_arg.len(), + OptionalArg::Missing => { + return Err(vm.new_value_error("negative read length")); + } } + } else if len_val < 0 { + return Err(vm.new_value_error("negative read length")); + } else { + len_val as usize + }; - let num_certs = sys::OPENSSL_sk_num(chain as *const _); + // if buffer is provided, limit len to buffer size + if let OptionalArg::Present(buf_arg) = &buffer { + let buf_len = buf_arg.len(); + if len_val <= 0 || len > buf_len { + len = buf_len; + } + } - let mut certs = Vec::with_capacity(num_certs as usize); - // Return Certificate objects - for i in 0..num_certs { - let cert_ptr = sys::OPENSSL_sk_value(chain as *const _, i) as *mut sys::X509; - if cert_ptr.is_null() { - continue; + // return empty bytes immediately for len=0 + if len == 0 { + return match buffer { + OptionalArg::Present(_) => Ok(vm.ctx.new_int(0).into()), + OptionalArg::Missing => Ok(vm.ctx.new_bytes(vec![]).into()), + }; + } + + // Ensure handshake is done + if !*self.handshake_done.lock() { + return Err(vm.new_value_error("Handshake not completed")); + } + + // Check if connection has been shut down + // After unwrap()/shutdown(), read operations should fail with SSLError + let shutdown_state = *self.shutdown_state.lock(); + if shutdown_state != ShutdownState::NotStarted { + return Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "cannot read after shutdown".to_owned(), + )); + } + + // Check for deferred certificate verification errors (TLS 1.3) + self.check_deferred_cert_error(vm)?; + + // Helper function to handle return value based on buffer presence + let return_data = |data: Vec, + buffer_arg: &OptionalArg, + vm: &VirtualMachine| + -> PyResult { + match buffer_arg { + OptionalArg::Present(buf_arg) => { + // Write into buffer and return number of bytes written + let n = data.len(); + if n > 0 { + let mut buf = buf_arg.borrow_buf_mut(); + let buf_slice = &mut *buf; + let copy_len = n.min(buf_slice.len()); + buf_slice[..copy_len].copy_from_slice(&data[..copy_len]); + } + Ok(vm.ctx.new_int(n).into()) } - // Clone the X509 certificate to create an owned copy - sys::X509_up_ref(cert_ptr); - let owned_cert = X509::from_ptr(cert_ptr); - let cert_obj = cert_to_certificate(vm, owned_cert)?; - certs.push(cert_obj); + OptionalArg::Missing => { + // Return bytes object + Ok(vm.ctx.new_bytes(data).into()) + } + } + }; + + let mut conn_guard = self.connection.lock(); + let conn = conn_guard + .as_mut() + .ok_or_else(|| vm.new_value_error("Connection not established"))?; + + // Use compat layer for unified read logic with proper EOF handling + // This matches CPython's SSL_read_ex() approach + let mut buf = vec![0u8; len]; + + match crate::ssl::compat::ssl_read(conn, &mut buf, self, vm) { + Ok(n) => { + buf.truncate(n); + return_data(buf, &buffer, vm) + } + Err(crate::ssl::compat::SslError::Eof) => { + // EOF occurred in violation of protocol (unexpected closure) + Err(vm.new_exception_msg( + PySSLEOFError::class(&vm.ctx).to_owned(), + "EOF occurred in violation of protocol".to_owned(), + )) + } + Err(crate::ssl::compat::SslError::ZeroReturn) => { + // Clean closure with close_notify - return empty data + return_data(vec![], &buffer, vm) + } + Err(crate::ssl::compat::SslError::WantRead) => { + // Non-blocking mode: would block + Err(create_ssl_want_read_error(vm)) + } + Err(crate::ssl::compat::SslError::WantWrite) => { + // Non-blocking mode: would block on write + Err(create_ssl_want_write_error(vm)) + } + Err(crate::ssl::compat::SslError::Timeout(msg)) => Err(timeout_error_msg(vm, msg)), + Err(crate::ssl::compat::SslError::Py(e)) => { + // Python exception - pass through + Err(e) + } + Err(e) => { + // Other SSL errors + Err(e.into_py_err(vm)) } - - Ok(if certs.is_empty() { - None - } else { - Some(vm.ctx.new_list(certs)) - }) } } #[pymethod] - fn version(&self) -> Option<&'static str> { - let v = self.connection.read().ssl().version_str(); - if v == "unknown" { None } else { Some(v) } - } + fn pending(&self) -> PyResult { + // Returns the number of already decrypted bytes available for read + // This is critical for asyncore's readable() method which checks socket.pending() > 0 + let mut conn_guard = self.connection.lock(); + let conn = match conn_guard.as_mut() { + Some(c) => c, + None => return Ok(0), // No connection established yet + }; - #[pymethod] - fn cipher(&self) -> Option { - self.connection - .read() - .ssl() - .current_cipher() - .map(cipher_to_tuple) + // Use rustls Reader's fill_buf() to check buffered plaintext + // fill_buf() returns a reference to buffered data without consuming it + // This matches OpenSSL's SSL_pending() behavior + use std::io::BufRead; + let mut reader = conn.reader(); + match reader.fill_buf() { + Ok(buf) => Ok(buf.len()), + Err(_) => { + // WouldBlock or other errors mean no data available + // Return 0 like OpenSSL does when buffer is empty + Ok(0) + } + } } #[pymethod] - fn shared_ciphers(&self, vm: &VirtualMachine) -> Option { - #[cfg(ossl110)] - { - let stream = self.connection.read(); - unsafe { - let server_ciphers = SSL_get_ciphers(stream.ssl().as_ptr()); - if server_ciphers.is_null() { - return None; - } - - let client_ciphers = SSL_get_client_ciphers(stream.ssl().as_ptr()); - if client_ciphers.is_null() { - return None; - } + fn write(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { + let data_bytes = data.borrow_buf(); + let data_len = data_bytes.len(); - let mut result = Vec::new(); - let num_server = sys::OPENSSL_sk_num(server_ciphers as *const _); - let num_client = sys::OPENSSL_sk_num(client_ciphers as *const _); + // return 0 immediately for empty write + if data_len == 0 { + return Ok(0); + } - for i in 0..num_server { - let server_cipher_ptr = sys::OPENSSL_sk_value(server_ciphers as *const _, i) - as *const sys::SSL_CIPHER; + // Ensure handshake is done + if !*self.handshake_done.lock() { + return Err(vm.new_value_error("Handshake not completed")); + } - // Check if client supports this cipher by comparing pointers - let mut found = false; - for j in 0..num_client { - let client_cipher_ptr = - sys::OPENSSL_sk_value(client_ciphers as *const _, j) - as *const sys::SSL_CIPHER; + // Check if connection has been shut down + // After unwrap()/shutdown(), write operations should fail with SSLError + let shutdown_state = *self.shutdown_state.lock(); + if shutdown_state != ShutdownState::NotStarted { + return Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "cannot write after shutdown".to_owned(), + )); + } - if server_cipher_ptr == client_cipher_ptr { - found = true; - break; - } + // Check for deferred certificate verification errors (TLS 1.3) + self.check_deferred_cert_error(vm)?; + + let mut conn_guard = self.connection.lock(); + let conn = conn_guard + .as_mut() + .ok_or_else(|| vm.new_value_error("Connection not established"))?; + + // Unified write logic - no need to match on Client/Server anymore + let mut writer = conn.writer(); + writer + .write_all(data_bytes.as_ref()) + .map_err(|e| vm.new_os_error(format!("Write failed: {e}")))?; + + // Flush to get TLS-encrypted data (writer automatically flushed on drop) + // Send encrypted data to socket + if conn.wants_write() { + let is_bio = self.is_bio_mode(); + + if is_bio { + // BIO mode: Write ALL pending TLS data to outgoing BIO + // This prevents hangs where Python's ssl_io_loop waits for data + self.write_pending_tls(conn, vm)?; + } else { + // Socket mode: Try once and may return SSLWantWriteError + let mut buf = Vec::new(); + conn.write_tls(&mut buf) + .map_err(|e| vm.new_os_error(format!("TLS write failed: {e}")))?; + + if !buf.is_empty() { + // Wait for socket to be ready for writing + let timed_out = self.sock_wait_for_io_impl(SelectKind::Write, vm)?; + if timed_out { + return Err(vm.new_os_error("Write operation timed out")); } - if found { - let cipher = ssl::SslCipherRef::from_ptr(server_cipher_ptr as *mut _); - let (name, version, bits) = cipher_to_tuple(cipher); - let tuple = vm.new_tuple(( - vm.ctx.new_str(name), - vm.ctx.new_str(version), - vm.ctx.new_int(bits), - )); - result.push(tuple.into()); + // Send encrypted data to socket + // Convert BlockingIOError to SSLWantWriteError + match self.sock_send(buf, vm) { + Ok(_) => {} + Err(e) => { + if is_blocking_io_error(&e, vm) { + // Non-blocking socket would block - return SSLWantWriteError + return Err(create_ssl_want_write_error(vm)); + } + return Err(e); + } } } - - if result.is_empty() { - None - } else { - Some(vm.ctx.new_list(result)) - } } } - #[cfg(not(ossl110))] - { - let _ = vm; - None - } - } - - #[pymethod] - fn selected_alpn_protocol(&self) -> Option { - #[cfg(ossl102)] - { - let stream = self.connection.read(); - unsafe { - let mut out: *const libc::c_uchar = std::ptr::null(); - let mut outlen: libc::c_uint = 0; - - sys::SSL_get0_alpn_selected(stream.ssl().as_ptr(), &mut out, &mut outlen); - if out.is_null() { - None - } else { - let slice = std::slice::from_raw_parts(out, outlen as usize); - Some(String::from_utf8_lossy(slice).into_owned()) - } - } - } - #[cfg(not(ossl102))] - { - None - } + Ok(data_len) } #[pymethod] - fn get_channel_binding( + fn getpeercert( &self, - cb_type: OptionalArg, + binary_form: OptionalArg, vm: &VirtualMachine, - ) -> PyResult> { - const CB_MAXLEN: usize = 512; - - let cb_type_str = cb_type.as_ref().map_or("tls-unique", |s| s.as_str()); + ) -> PyResult> { + let binary = binary_form.unwrap_or(false); - if cb_type_str != "tls-unique" { - return Err(vm.new_value_error(format!( - "Unsupported channel binding type '{}'", - cb_type_str - ))); + // Check if handshake is complete + if !*self.handshake_done.lock() { + return Err(vm.new_value_error("handshake not done yet")); } - let stream = self.connection.read(); - let ssl_ptr = stream.ssl().as_ptr(); + // Get peer certificates from TLS connection + let conn_guard = self.connection.lock(); + let conn = conn_guard + .as_ref() + .ok_or_else(|| vm.new_value_error("No TLS connection established"))?; + + let certs = conn.peer_certificates(); - unsafe { - let session_reused = sys::SSL_session_reused(ssl_ptr) != 0; - let is_client = matches!(self.socket_type, SslServerOrClient::Client); + // Return None if no peer certificate + let Some(certs) = certs else { + return Ok(None); + }; - // Use XOR logic from CPython - let use_finished = session_reused ^ is_client; + // Get first certificate (peer's certificate) + let cert_der = certs + .first() + .ok_or_else(|| vm.new_value_error("No peer certificate available"))?; - let mut buf = vec![0u8; CB_MAXLEN]; - let len = if use_finished { - sys::SSL_get_finished(ssl_ptr, buf.as_mut_ptr() as *mut _, CB_MAXLEN) - } else { - sys::SSL_get_peer_finished(ssl_ptr, buf.as_mut_ptr() as *mut _, CB_MAXLEN) - }; + if binary { + // Return DER-encoded certificate as bytes + let der_bytes = cert_der.as_ref().to_vec(); + return Ok(Some(vm.ctx.new_bytes(der_bytes).into())); + } - if len == 0 { - Ok(None) - } else { - buf.truncate(len); - Ok(Some(vm.ctx.new_bytes(buf))) - } + // Dictionary mode: check verify_mode + let verify_mode = *self.context.read().verify_mode.read(); + + if verify_mode == CERT_NONE { + // Return empty dict when CERT_NONE + return Ok(Some(vm.ctx.new_dict().into())); } + + // Parse DER certificate and convert to dict + let der_bytes = cert_der.as_ref(); + let (_, cert) = x509_parser::parse_x509_certificate(der_bytes) + .map_err(|e| vm.new_value_error(format!("Failed to parse certificate: {e}")))?; + + cert::cert_to_dict(vm, &cert).map(Some) } #[pymethod] - fn verify_client_post_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { - #[cfg(ossl111)] - { - let stream = self.connection.read(); - let result = unsafe { SSL_verify_client_post_handshake(stream.ssl().as_ptr()) }; - if result == 0 { - Err(convert_openssl_error(vm, openssl::error::ErrorStack::get())) - } else { - Ok(()) - } - } - #[cfg(not(ossl111))] - { - Err(vm.new_not_implemented_error( - "Post-handshake auth is not supported by your OpenSSL version.".to_owned(), - )) - } + fn cipher(&self) -> Option<(String, String, i32)> { + let conn_guard = self.connection.lock(); + let conn = conn_guard.as_ref()?; + + let suite = conn.negotiated_cipher_suite()?; + + // Extract cipher information using unified helper + let cipher_info = extract_cipher_info(&suite); + + // Note: returns a 3-tuple (name, protocol_version, bits) + // The 'description' field is part of get_ciphers() output, not cipher() + Some(( + cipher_info.name, + cipher_info.protocol.to_string(), + cipher_info.bits, + )) } #[pymethod] - fn shutdown(&self, vm: &VirtualMachine) -> PyResult> { - let stream = self.connection.read(); + fn version(&self) -> Option { + let conn_guard = self.connection.lock(); + let conn = conn_guard.as_ref()?; - // BIO mode doesn't have an underlying socket - if stream.is_bio() { - return Err(vm.new_not_implemented_error( - "shutdown() is not supported for BIO-based SSL objects".to_owned(), - )); - } + let suite = conn.negotiated_cipher_suite()?; + + let version_str = match suite.version().version { + rustls::ProtocolVersion::TLSv1_2 => "TLSv1.2", + rustls::ProtocolVersion::TLSv1_3 => "TLSv1.3", + _ => "Unknown", + }; - let ssl_ptr = stream.ssl().as_ptr(); + Some(version_str.to_string()) + } - // Perform SSL shutdown - let ret = unsafe { sys::SSL_shutdown(ssl_ptr) }; + #[pymethod] + fn selected_alpn_protocol(&self) -> Option { + let conn_guard = self.connection.lock(); + let conn = conn_guard.as_ref()?; - if ret < 0 { - // Error occurred - let err = unsafe { sys::SSL_get_error(ssl_ptr, ret) }; + let alpn_bytes = conn.alpn_protocol()?; - if err == sys::SSL_ERROR_WANT_READ || err == sys::SSL_ERROR_WANT_WRITE { - // Non-blocking would block - this is okay for shutdown - // Return the underlying socket - } else { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - format!("SSL shutdown failed: error code {}", err), - )); - } + // Null byte protocol (vec![0u8]) means no actual ALPN match (fallback protocol) + if alpn_bytes.is_empty() || alpn_bytes == [0u8] { + return None; } - // Return the underlying socket - // Get the socket from the stream (SocketStream wraps PyRef) - let socket = stream - .get_ref() - .expect("unwrap() called on bio mode; should only be called in socket mode"); - Ok(socket.0.clone()) + // Convert bytes to string + String::from_utf8(alpn_bytes.to_vec()).ok() } - #[cfg(osslconf = "OPENSSL_NO_COMP")] #[pymethod] - fn compression(&self) -> Option<&'static str> { + fn selected_npn_protocol(&self) -> Option { + // NPN (Next Protocol Negotiation) is the predecessor to ALPN + // It was deprecated in favor of ALPN (RFC 7301) + // Rustls doesn't support NPN, only ALPN + // Return None to indicate NPN is not supported None } - #[cfg(not(osslconf = "OPENSSL_NO_COMP"))] - #[pymethod] - fn compression(&self) -> Option<&'static str> { - let stream = self.connection.read(); - let comp_method = unsafe { sys::SSL_get_current_compression(stream.ssl().as_ptr()) }; - if comp_method.is_null() { - return None; - } - let typ = unsafe { sys::COMP_get_type(comp_method) }; - let nid = Nid::from_raw(typ); - if nid == Nid::UNDEF { - return None; - } - nid.short_name().ok() + + #[pygetset] + fn owner(&self) -> Option { + self.owner.read().clone() } - #[pymethod] - fn do_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { - let mut stream = self.connection.write(); - let ssl_ptr = stream.ssl().as_ptr(); - - // BIO mode: no timeout/select logic, just do handshake - if stream.is_bio() { - return stream.do_handshake().map_err(|e| { - let exc = convert_ssl_error(vm, e); - // If it's a cert verification error, set verify info - if exc.class().is(PySslCertVerificationError::class(&vm.ctx)) { - set_verify_error_info(&exc, ssl_ptr, vm); - } - exc - }); - } + #[pygetset(setter)] + fn set_owner(&self, owner: PyObjectRef, _vm: &VirtualMachine) -> PyResult<()> { + *self.owner.write() = Some(owner); + Ok(()) + } - // Socket mode: handle timeout and blocking - let timeout = stream - .get_ref() - .expect("handshake called in bio mode; should only be called in socket mode") - .timeout_deadline(); - loop { - let err = match stream.do_handshake() { - Ok(()) => return Ok(()), - Err(e) => e, - }; - let (needs, state) = stream - .get_ref() - .expect("handshake called in bio mode; should only be called in socket mode") - .socket_needs(&err, &timeout); - match state { - SelectRet::TimedOut => { - return Err(socket::timeout_error_msg( - vm, - "The handshake operation timed out".to_owned(), - )); - } - SelectRet::Closed => return Err(socket_closed_error(vm)), - SelectRet::Nonblocking => {} - _ => { - if needs.is_some() { - continue; - } - } - } - let exc = convert_ssl_error(vm, err); - // If it's a cert verification error, set verify info - if exc.class().is(PySslCertVerificationError::class(&vm.ctx)) { - set_verify_error_info(&exc, ssl_ptr, vm); - } - return Err(exc); - } + #[pygetset] + fn server_side(&self) -> bool { + self.server_side } - #[pymethod] - fn write(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { - let mut stream = self.connection.write(); - let data = data.borrow_buf(); - let data = &*data; - - // BIO mode: no timeout/select logic - if stream.is_bio() { - return stream.ssl_write(data).map_err(|e| convert_ssl_error(vm, e)); - } - - // Socket mode: handle timeout and blocking - let socket_ref = stream - .get_ref() - .expect("write called in bio mode; should only be called in socket mode"); - let timeout = socket_ref.timeout_deadline(); - let state = socket_ref.select(SslNeeds::Write, &timeout); - match state { - SelectRet::TimedOut => { - return Err(socket::timeout_error_msg( - vm, - "The write operation timed out".to_owned(), - )); - } - SelectRet::Closed => return Err(socket_closed_error(vm)), - _ => {} + #[pygetset] + fn context(&self) -> PyRef { + self.context.read().clone() + } + + #[pygetset(setter)] + fn set_context(&self, value: PyRef, _vm: &VirtualMachine) -> PyResult<()> { + // Update context reference immediately + // SSL_set_SSL_CTX allows context changes at any time, + // even after handshake completion + *self.context.write() = value; + + // Clear pending context as we've applied the change + *self.pending_context.write() = None; + + Ok(()) + } + + #[pygetset] + fn server_hostname(&self) -> Option { + self.server_hostname.read().clone() + } + + #[pygetset(setter)] + fn set_server_hostname( + &self, + value: Option, + vm: &VirtualMachine, + ) -> PyResult<()> { + // Check if handshake is already done + if *self.handshake_done.lock() { + return Err( + vm.new_value_error("Cannot set server_hostname on socket after handshake") + ); } - loop { - let err = match stream.ssl_write(data) { - Ok(len) => return Ok(len), - Err(e) => e, - }; - let (needs, state) = stream - .get_ref() - .expect("write called in bio mode; should only be called in socket mode") - .socket_needs(&err, &timeout); - match state { - SelectRet::TimedOut => { - return Err(socket::timeout_error_msg( - vm, - "The write operation timed out".to_owned(), - )); - } - SelectRet::Closed => return Err(socket_closed_error(vm)), - SelectRet::Nonblocking => {} - _ => { - if needs.is_some() { - continue; - } - } - } - return Err(convert_ssl_error(vm, err)); + + // Validate hostname + if let Some(hostname_str) = &value { + validate_hostname(hostname_str.as_str(), vm)?; } + + *self.server_hostname.write() = value.map(|s| s.as_str().to_string()); + Ok(()) } #[pygetset] - fn session(&self, _vm: &VirtualMachine) -> PyResult> { - let stream = self.connection.read(); - unsafe { - let session_ptr = sys::SSL_get_session(stream.ssl().as_ptr()); - if session_ptr.is_null() { - Ok(None) - } else { - // Increment reference count since SSL_get_session returns a borrowed reference - #[cfg(ossl110)] - let _session = sys::SSL_SESSION_up_ref(session_ptr); - - Ok(Some(PySslSession { - session: session_ptr, - ctx: self.ctx.read().clone(), - })) - } + fn session(&self, vm: &VirtualMachine) -> PyResult { + // Return the stored session object if any + let sess = self.session.read().clone(); + if let Some(s) = sess { + Ok(s) + } else { + Ok(vm.ctx.none()) } } #[pygetset(setter)] fn set_session(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - // Check if value is SSLSession type - let session = value - .downcast_ref::() - .ok_or_else(|| vm.new_type_error("Value is not a SSLSession.".to_owned()))?; - - // Check if session refers to the same SSLContext - if !std::ptr::eq( - self.ctx.read().ctx.read().as_ptr(), - session.ctx.ctx.read().as_ptr(), - ) { - return Err( - vm.new_value_error("Session refers to a different SSLContext.".to_owned()) - ); + // Validate that value is an SSLSession + if !value.is(vm.ctx.types.none_type) { + // Try to downcast to SSLSession to validate + let _ = value + .downcast_ref::() + .ok_or_else(|| vm.new_type_error("Value is not a SSLSession."))?; } // Check if this is a client socket - if self.socket_type != SslServerOrClient::Client { - return Err( - vm.new_value_error("Cannot set session for server-side SSLSocket.".to_owned()) - ); + if self.server_side { + return Err(vm.new_value_error("Cannot set session for server-side SSLSocket")); } - // Check if handshake is not finished - let stream = self.connection.read(); - unsafe { - if sys::SSL_is_init_finished(stream.ssl().as_ptr()) != 0 { - return Err( - vm.new_value_error("Cannot set session after handshake.".to_owned()) - ); - } - - if sys::SSL_set_session(stream.ssl().as_ptr(), session.session) == 0 { - return Err(convert_openssl_error(vm, ErrorStack::get())); - } + // Check if handshake is already done + if *self.handshake_done.lock() { + return Err(vm.new_value_error("Cannot set session after handshake.")); } + // Store the session for potential use during handshake + *self.session.write() = if value.is(vm.ctx.types.none_type) { + None + } else { + Some(value) + }; + Ok(()) } #[pygetset] fn session_reused(&self) -> bool { - let stream = self.connection.read(); - unsafe { sys::SSL_session_reused(stream.ssl().as_ptr()) != 0 } + // Return the tracked session reuse status + *self.session_was_reused.lock() } #[pymethod] - fn read( - &self, - n: usize, - buffer: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - // Special case: reading 0 bytes should return empty bytes immediately - if n == 0 { - return if buffer.is_present() { - Ok(vm.ctx.new_int(0).into()) - } else { - Ok(vm.ctx.new_bytes(vec![]).into()) - }; - } + fn compression(&self) -> Option<&'static str> { + // rustls doesn't support compression + None + } - let mut stream = self.connection.write(); - let mut inner_buffer = if let OptionalArg::Present(buffer) = &buffer { - Either::A(buffer.borrow_buf_mut()) - } else { - Either::B(vec![0u8; n]) - }; - let buf = match &mut inner_buffer { - Either::A(b) => &mut **b, - Either::B(b) => b.as_mut_slice(), - }; - let buf = match buf.get_mut(..n) { - Some(b) => b, - None => buf, + #[pymethod] + fn get_unverified_chain(&self, vm: &VirtualMachine) -> PyResult> { + // Get peer certificates from the connection + let conn_guard = self.connection.lock(); + let conn = conn_guard + .as_ref() + .ok_or_else(|| vm.new_value_error("Handshake not completed"))?; + + let certs = conn.peer_certificates(); + + let Some(certs) = certs else { + return Ok(None); }; - // BIO mode: no timeout/select logic - let count = if stream.is_bio() { - match stream.ssl_read(buf) { - Ok(count) => count, - Err(e) => return Err(convert_ssl_error(vm, e)), - } - } else { - // Socket mode: handle timeout and blocking - let timeout = stream - .get_ref() - .expect("read called in bio mode; should only be called in socket mode") - .timeout_deadline(); - loop { - let err = match stream.ssl_read(buf) { - Ok(count) => break count, - Err(e) => e, - }; - if err.code() == ssl::ErrorCode::ZERO_RETURN - && stream.get_shutdown() == ssl::ShutdownState::RECEIVED - { - break 0; - } - let (needs, state) = stream - .get_ref() - .expect("read called in bio mode; should only be called in socket mode") - .socket_needs(&err, &timeout); - match state { - SelectRet::TimedOut => { - return Err(socket::timeout_error_msg( - vm, - "The read operation timed out".to_owned(), - )); - } - SelectRet::Nonblocking => {} - _ => { - if needs.is_some() { - continue; - } - } + // Convert to list of Certificate objects + let cert_list: Vec = certs + .iter() + .map(|cert_der| { + let cert_bytes = cert_der.as_ref().to_vec(); + PySSLCertificate { + der_bytes: cert_bytes, } - return Err(convert_ssl_error(vm, err)); - } + .into_ref(&vm.ctx) + .into() + }) + .collect(); + + Ok(Some(vm.ctx.new_list(cert_list))) + } + + #[pymethod] + fn get_verified_chain(&self, vm: &VirtualMachine) -> PyResult> { + // Get peer certificates (what peer sent during handshake) + let conn_guard = self.connection.lock(); + let Some(ref conn) = *conn_guard else { + return Ok(None); }; - let ret = match inner_buffer { - Either::A(_buf) => vm.ctx.new_int(count).into(), - Either::B(mut buf) => { - buf.truncate(count); - buf.shrink_to_fit(); - vm.ctx.new_bytes(buf).into() - } + + let peer_certs = conn.peer_certificates(); + + let Some(peer_certs_slice) = peer_certs else { + return Ok(None); }; - Ok(ret) - } - } - #[pyattr] - #[pyclass(module = "ssl", name = "SSLSession")] - #[derive(PyPayload)] - struct PySslSession { - session: *mut sys::SSL_SESSION, - ctx: PyRef, - } + // Build the verified chain using cert module + let ctx_guard = self.context.read(); + let ca_certs_der = ctx_guard.ca_certs_der.read(); + + let chain_der = cert::build_verified_chain(peer_certs_slice, &ca_certs_der); + + // Convert DER chain to Python list of Certificate objects + let cert_list: Vec = chain_der + .into_iter() + .map(|der_bytes| PySSLCertificate { der_bytes }.into_ref(&vm.ctx).into()) + .collect(); - impl fmt::Debug for PySslSession { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("SSLSession") + Ok(Some(vm.ctx.new_list(cert_list))) } - } - impl Drop for PySslSession { - fn drop(&mut self) { - if !self.session.is_null() { - unsafe { - sys::SSL_SESSION_free(self.session); + #[pymethod] + fn shutdown(&self, vm: &VirtualMachine) -> PyResult { + // Check current shutdown state + let current_state = *self.shutdown_state.lock(); + + // If already completed, return immediately + if current_state == ShutdownState::Completed { + if self.is_bio_mode() { + return Ok(vm.ctx.none()); } + return Ok(self.sock.clone()); } - } - } - unsafe impl Send for PySslSession {} - unsafe impl Sync for PySslSession {} + // Get connection + let mut conn_guard = self.connection.lock(); + let conn = conn_guard + .as_mut() + .ok_or_else(|| vm.new_value_error("Connection not established"))?; - impl Comparable for PySslSession { - fn cmp( - zelf: &Py, - other: &crate::vm::PyObject, - op: PyComparisonOp, - _vm: &VirtualMachine, - ) -> PyResult { - let other = class_or_notimplemented!(Self, other); + // Step 1: Send our close_notify if not already sent + if current_state == ShutdownState::NotStarted { + conn.send_close_notify(); + + // Write close_notify to outgoing buffer/BIO + self.write_pending_tls(conn, vm)?; - if !matches!(op, PyComparisonOp::Eq | PyComparisonOp::Ne) { - return Ok(PyComparisonValue::NotImplemented); + // Update state + *self.shutdown_state.lock() = ShutdownState::SentCloseNotify; } - let mut eq = unsafe { - let mut self_len: libc::c_uint = 0; - let mut other_len: libc::c_uint = 0; - let self_id = sys::SSL_SESSION_get_id(zelf.session, &mut self_len); - let other_id = sys::SSL_SESSION_get_id(other.session, &mut other_len); - if self_len != other_len { - false + // Step 2: Try to read and process peer's close_notify + let is_bio = self.is_bio_mode(); + + // First check if we already have peer's close_notify + // This can happen if it was received during a previous read() call + let mut peer_closed = self.check_peer_closed(conn, vm)?; + + // If peer hasn't closed yet, try to read from socket + if !peer_closed { + // Check if socket is in blocking mode (timeout is None) + let is_blocking = if !is_bio { + // Get socket timeout + match self.sock.get_attr("gettimeout", vm) { + Ok(method) => match method.call((), vm) { + Ok(timeout) => vm.is_none(&timeout), + Err(_) => false, + }, + Err(_) => false, + } } else { - let self_slice = std::slice::from_raw_parts(self_id, self_len as usize); - let other_slice = std::slice::from_raw_parts(other_id, other_len as usize); - self_slice == other_slice + false + }; + + if is_bio { + // In BIO mode: non-blocking read attempt + let _ = self.try_read_close_notify(conn, vm); + } else if is_blocking { + // Blocking socket mode: Return immediately without waiting for peer + // + // Reasons we don't read from socket here: + // 1. STARTTLS scenario: application data may arrive before/instead of close_notify + // - Example: client sends ENDTLS, immediately sends plain "msg 5" + // - Server's unwrap() would read "msg 5" and try to parse as TLS → FAIL + // 2. CPython's SSL_shutdown() typically returns immediately without waiting + // 3. Bidirectional shutdown is the application's responsibility + // 4. Reading from socket would consume application data incorrectly + // + // Therefore: Just send our close_notify and return success immediately. + // The peer's close_notify (if any) will remain in the socket buffer. + // + // Mark shutdown as complete and return the underlying socket + drop(conn_guard); + *self.shutdown_state.lock() = ShutdownState::Completed; + *self.connection.lock() = None; + return Ok(self.sock.clone()); } - }; - if matches!(op, PyComparisonOp::Ne) { - eq = !eq; + + // Step 3: Check again if peer has sent close_notify (non-blocking/BIO mode only) + peer_closed = self.check_peer_closed(conn, vm)?; } - Ok(PyComparisonValue::Implemented(eq)) - } - } - #[pyattr] - #[pyclass(module = "ssl", name = "MemoryBIO")] - #[derive(PyPayload)] - struct PySslMemoryBio { - bio: *mut sys::BIO, - eof_written: AtomicCell, - } + drop(conn_guard); // Release lock before returning - impl fmt::Debug for PySslMemoryBio { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("MemoryBIO") - } - } + if !peer_closed { + // Still waiting for peer's close-notify + // Raise SSLWantReadError to signal app needs to transfer data + // This is correct for non-blocking sockets and BIO mode + return Err(create_ssl_want_read_error(vm)); + } + // Both close-notify exchanged, shutdown complete + *self.shutdown_state.lock() = ShutdownState::Completed; - impl Drop for PySslMemoryBio { - fn drop(&mut self) { - if !self.bio.is_null() { - unsafe { - sys::BIO_free_all(self.bio); - } + if is_bio { + return Ok(vm.ctx.none()); } + Ok(self.sock.clone()) } - } - unsafe impl Send for PySslMemoryBio {} - unsafe impl Sync for PySslMemoryBio {} + // Helper: Write all pending TLS data (including close_notify) to outgoing buffer/BIO + fn write_pending_tls(&self, conn: &mut TlsConnection, vm: &VirtualMachine) -> PyResult<()> { + loop { + if !conn.wants_write() { + break; + } + + let mut buf = vec![0u8; SSL3_RT_MAX_PLAIN_LENGTH]; + let written = conn + .write_tls(&mut buf.as_mut_slice()) + .map_err(|e| vm.new_os_error(format!("TLS write failed: {e}")))?; - // OpenSSL functions not in openssl-sys + if written == 0 { + break; + } - unsafe extern "C" { - // X509_check_ca returns 1 for CA certificates, 0 otherwise - fn X509_check_ca(x: *const sys::X509) -> libc::c_int; - } + // Send to outgoing BIO or socket + self.sock_send(buf[..written].to_vec(), vm)?; + } - unsafe extern "C" { - fn SSL_get_ciphers(ssl: *const sys::SSL) -> *const sys::stack_st_SSL_CIPHER; - } + Ok(()) + } + + // Helper: Try to read incoming data from BIO (non-blocking) + fn try_read_close_notify( + &self, + conn: &mut TlsConnection, + vm: &VirtualMachine, + ) -> PyResult<()> { + // Try to read incoming data from BIO + // This is non-blocking in BIO mode - if no data, recv returns empty + match self.sock_recv(SSL3_RT_MAX_PLAIN_LENGTH, vm) { + Ok(bytes_obj) => { + let bytes = ArgBytesLike::try_from_object(vm, bytes_obj)?; + let data = bytes.borrow_buf(); + + if !data.is_empty() { + // Feed data to TLS connection + let data_slice: &[u8] = data.as_ref(); + let mut cursor = std::io::Cursor::new(data_slice); + let _ = conn.read_tls(&mut cursor); + + // Process packets + let _ = conn.process_new_packets(); + } + } + Err(_) => { + // No data available or error - that's OK in BIO mode + } + } - #[cfg(ossl110)] - unsafe extern "C" { - fn SSL_get_client_ciphers(ssl: *const sys::SSL) -> *const sys::stack_st_SSL_CIPHER; - } + Ok(()) + } - #[cfg(ossl111)] - unsafe extern "C" { - fn SSL_verify_client_post_handshake(ssl: *const sys::SSL) -> libc::c_int; - fn SSL_set_post_handshake_auth(ssl: *mut sys::SSL, val: libc::c_int); - } + // Helper: Check if peer has sent close_notify + fn check_peer_closed( + &self, + conn: &mut TlsConnection, + vm: &VirtualMachine, + ) -> PyResult { + // Process any remaining packets and check peer_has_closed + let io_state = conn + .process_new_packets() + .map_err(|e| vm.new_os_error(format!("Failed to process packets: {e}")))?; - #[cfg(ossl110)] - unsafe extern "C" { - fn SSL_CTX_get_security_level(ctx: *const sys::SSL_CTX) -> libc::c_int; - } + Ok(io_state.peer_has_closed()) + } - unsafe extern "C" { - fn SSL_set_SSL_CTX(ssl: *mut sys::SSL, ctx: *mut sys::SSL_CTX) -> *mut sys::SSL_CTX; - } + #[pymethod] + fn shared_ciphers(&self, vm: &VirtualMachine) -> Option { + // Return None for client-side sockets + if !self.server_side { + return None; + } - #[cfg(ossl110)] - unsafe extern "C" { - fn SSL_SESSION_has_ticket(session: *const sys::SSL_SESSION) -> libc::c_int; - fn SSL_SESSION_get_ticket_lifetime_hint(session: *const sys::SSL_SESSION) -> libc::c_ulong; - } + // Check if handshake completed + if !*self.handshake_done.lock() { + return None; + } - // X509 object types - const X509_LU_X509: libc::c_int = 1; - const X509_LU_CRL: libc::c_int = 2; + // Get negotiated cipher suite from rustls + let conn_guard = self.connection.lock(); + let conn = conn_guard.as_ref()?; - unsafe extern "C" { - fn X509_OBJECT_get_type(obj: *const sys::X509_OBJECT) -> libc::c_int; - } + let suite = conn.negotiated_cipher_suite()?; - // SSL session statistics constants (used with SSL_CTX_ctrl) - const SSL_CTRL_SESS_NUMBER: libc::c_int = 20; - const SSL_CTRL_SESS_CONNECT: libc::c_int = 21; - const SSL_CTRL_SESS_CONNECT_GOOD: libc::c_int = 22; - const SSL_CTRL_SESS_CONNECT_RENEGOTIATE: libc::c_int = 23; - const SSL_CTRL_SESS_ACCEPT: libc::c_int = 24; - const SSL_CTRL_SESS_ACCEPT_GOOD: libc::c_int = 25; - const SSL_CTRL_SESS_ACCEPT_RENEGOTIATE: libc::c_int = 26; - const SSL_CTRL_SESS_HIT: libc::c_int = 27; - const SSL_CTRL_SESS_MISSES: libc::c_int = 29; - const SSL_CTRL_SESS_TIMEOUTS: libc::c_int = 30; - const SSL_CTRL_SESS_CACHE_FULL: libc::c_int = 31; - - // SSL session statistics functions (implemented as macros in OpenSSL) - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_number(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_NUMBER, 0, std::ptr::null_mut()) } - } + // Extract cipher information using unified helper + let cipher_info = extract_cipher_info(&suite); - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_connect(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_CONNECT, - 0, - std::ptr::null_mut(), - ) + // Return as list with single tuple (name, version, bits) + let tuple = vm.ctx.new_tuple(vec![ + vm.ctx.new_str(cipher_info.name).into(), + vm.ctx.new_str(cipher_info.protocol).into(), + vm.ctx.new_int(cipher_info.bits).into(), + ]); + Some(vm.ctx.new_list(vec![tuple.into()])) } - } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_connect_good(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_CONNECT_GOOD, - 0, - std::ptr::null_mut(), - ) - } - } + #[pymethod] + fn verify_client_post_handshake(&self, vm: &VirtualMachine) -> PyResult<()> { + // TLS 1.3 post-handshake authentication + // This is only valid for server-side TLS 1.3 connections - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_connect_renegotiate(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_CONNECT_RENEGOTIATE, - 0, - std::ptr::null_mut(), - ) - } - } + // Check if this is a server-side socket + if !self.server_side { + return Err(vm.new_value_error( + "Cannot perform post-handshake authentication on client-side socket", + )); + } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_accept(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_ACCEPT, 0, std::ptr::null_mut()) } - } + // Check if handshake has been completed + if !*self.handshake_done.lock() { + return Err(vm.new_value_error( + "Handshake must be completed before post-handshake authentication", + )); + } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_accept_good(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_ACCEPT_GOOD, - 0, - std::ptr::null_mut(), - ) - } - } + // Check connection exists and protocol version + let conn_guard = self.connection.lock(); + if let Some(conn) = conn_guard.as_ref() { + let version = match conn { + TlsConnection::Client(_) => { + return Err(vm.new_value_error( + "Post-handshake authentication requires server socket", + )); + } + TlsConnection::Server(server) => server.protocol_version(), + }; + + // Post-handshake auth is only available in TLS 1.3 + if version != Some(rustls::ProtocolVersion::TLSv1_3) { + // Get SSLError class from ssl module (not _ssl) + // ssl.py imports _ssl.SSLError as ssl.SSLError + let ssl_mod = vm.import("ssl", 0)?; + let ssl_error_class = ssl_mod.get_attr("SSLError", vm)?; + + // Create SSLError instance with message containing WRONG_SSL_VERSION + let msg = "[SSL: WRONG_SSL_VERSION] wrong ssl version"; + let args = vm.ctx.new_tuple(vec![vm.ctx.new_str(msg).into()]); + let exc = ssl_error_class.call((args,), vm)?; + + return Err(exc + .downcast() + .map_err(|_| vm.new_type_error("Failed to create SSLError"))?); + } + } else { + return Err(vm.new_value_error("No SSL connection established")); + } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_accept_renegotiate(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_ACCEPT_RENEGOTIATE, - 0, - std::ptr::null_mut(), - ) + // rustls doesn't provide an API for post-handshake authentication. + // The rustls TLS library does not support requesting client certificates + // after the initial handshake is completed. + // Raise SSLError instead of NotImplementedError for compatibility + Err(vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + "Post-handshake authentication is not supported by the rustls backend. \ + The rustls TLS library does not provide an API to request client certificates \ + after the initial handshake. Consider requesting the client certificate \ + during the initial handshake by setting the appropriate verify_mode before \ + calling do_handshake()." + .to_owned(), + )) } - } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_hits(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_HIT, 0, std::ptr::null_mut()) } - } + #[pymethod] + fn get_channel_binding( + &self, + cb_type: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult> { + let cb_type_str = cb_type.as_ref().map_or("tls-unique", |s| s.as_str()); - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_misses(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { sys::SSL_CTX_ctrl(ctx as *mut _, SSL_CTRL_SESS_MISSES, 0, std::ptr::null_mut()) } - } + // rustls doesn't support channel binding (tls-unique, tls-server-end-point, etc.) + // This is because: + // 1. tls-unique requires access to TLS Finished messages, which rustls doesn't expose + // 2. tls-server-end-point requires the server certificate, which we don't track here + // 3. TLS 1.3 deprecated tls-unique anyway + // + // For compatibility, we'll return None (no channel binding available) + // rather than raising an error - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_timeouts(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_TIMEOUTS, - 0, - std::ptr::null_mut(), - ) - } - } + if cb_type_str != "tls-unique" { + return Err(vm.new_value_error(format!( + "Unsupported channel binding type '{cb_type_str}'", + ))); + } - #[allow(non_snake_case)] - unsafe fn SSL_CTX_sess_cache_full(ctx: *const sys::SSL_CTX) -> libc::c_long { - unsafe { - sys::SSL_CTX_ctrl( - ctx as *mut _, - SSL_CTRL_SESS_CACHE_FULL, - 0, - std::ptr::null_mut(), - ) + // Return None to indicate channel binding is not available + // This matches the behavior when the handshake hasn't completed yet + Ok(None) } } - // DH parameters functions - unsafe extern "C" { - fn PEM_read_DHparams( - fp: *mut libc::FILE, - x: *mut *mut sys::DH, - cb: *mut libc::c_void, - u: *mut libc::c_void, - ) -> *mut sys::DH; - } - - // OpenSSL BIO helper functions - // These are typically macros in OpenSSL, implemented via BIO_ctrl - const BIO_CTRL_PENDING: libc::c_int = 10; - const BIO_CTRL_SET_EOF: libc::c_int = 2; - - #[allow(non_snake_case)] - unsafe fn BIO_ctrl_pending(bio: *mut sys::BIO) -> usize { - unsafe { sys::BIO_ctrl(bio, BIO_CTRL_PENDING, 0, std::ptr::null_mut()) as usize } - } + impl Constructor for PySSLSocket { + type Args = (); - #[allow(non_snake_case)] - unsafe fn BIO_set_mem_eof_return(bio: *mut sys::BIO, eof: libc::c_int) -> libc::c_int { - unsafe { - sys::BIO_ctrl( - bio, - BIO_CTRL_SET_EOF, - eof as libc::c_long, - std::ptr::null_mut(), - ) as libc::c_int + fn py_new(_cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error( + "Cannot directly instantiate SSLSocket, use SSLContext.wrap_socket()", + )) } } - #[allow(non_snake_case)] - unsafe fn BIO_clear_retry_flags(bio: *mut sys::BIO) { - unsafe { - sys::BIO_clear_flags(bio, sys::BIO_FLAGS_RWS | sys::BIO_FLAGS_SHOULD_RETRY); - } + // MemoryBIO - provides in-memory buffer for SSL/TLS I/O + #[pyattr] + #[pyclass(name = "MemoryBIO", module = "ssl")] + #[derive(Debug, PyPayload)] + struct PyMemoryBIO { + // Internal buffer + buffer: PyMutex>, + // EOF flag + eof: PyRwLock, } - impl Constructor for PySslMemoryBio { - type Args = (); - - fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { - unsafe { - let bio = sys::BIO_new(sys::BIO_s_mem()); - if bio.is_null() { - return Err(vm.new_memory_error("failed to allocate BIO".to_owned())); - } + #[pyclass(with(Constructor), flags(BASETYPE))] + impl PyMemoryBIO { + #[pymethod] + fn read(&self, len: OptionalArg, vm: &VirtualMachine) -> PyResult { + let mut buffer = self.buffer.lock(); - sys::BIO_set_retry_read(bio); - BIO_set_mem_eof_return(bio, -1); + if buffer.is_empty() && *self.eof.read() { + // Return empty bytes at EOF + return Ok(vm.ctx.new_bytes(vec![])); + } - PySslMemoryBio { - bio, - eof_written: AtomicCell::new(false), + let read_len = match len { + OptionalArg::Present(n) if n >= 0 => n as usize, + OptionalArg::Present(n) => { + return Err(vm.new_value_error(format!("negative read length: {n}"))); } - .into_ref_with_type(vm, cls) - .map(Into::into) - } - } - } + OptionalArg::Missing => buffer.len(), // Read all available + }; - #[pyclass(flags(IMMUTABLETYPE), with(Constructor))] - impl PySslMemoryBio { - #[pygetset] - fn pending(&self) -> usize { - unsafe { BIO_ctrl_pending(self.bio) } - } + let actual_len = read_len.min(buffer.len()); + let data = buffer.drain(..actual_len).collect::>(); - #[pygetset] - fn eof(&self) -> bool { - let pending = unsafe { BIO_ctrl_pending(self.bio) }; - pending == 0 && self.eof_written.load() + Ok(vm.ctx.new_bytes(data)) } #[pymethod] - fn read(&self, size: OptionalArg, vm: &VirtualMachine) -> PyResult> { - unsafe { - let avail = BIO_ctrl_pending(self.bio).min(i32::MAX as usize) as i32; - let len = size.unwrap_or(-1); - let len = if len < 0 || len > avail { avail } else { len }; - - if len == 0 { - return Ok(Vec::new()); + fn write(&self, buf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // Check if it's a memoryview and if it's contiguous + if let Ok(mem_view) = buf.get_attr("c_contiguous", vm) { + // It's a memoryview, check if contiguous + let is_contiguous: bool = mem_view.try_to_bool(vm)?; + if !is_contiguous { + return Err(vm.new_exception_msg( + vm.ctx.exceptions.buffer_error.to_owned(), + "non-contiguous buffer is not supported".to_owned(), + )); } + } - let mut buf = vec![0u8; len as usize]; - let nbytes = sys::BIO_read(self.bio, buf.as_mut_ptr() as *mut _, len); + // Convert to bytes-like object + let bytes_like = ArgBytesLike::try_from_object(vm, buf)?; + let data = bytes_like.borrow_buf(); + let len = data.len(); - if nbytes < 0 { - return Err(convert_openssl_error(vm, ErrorStack::get())); - } + let mut buffer = self.buffer.lock(); + buffer.extend_from_slice(&data); - buf.truncate(nbytes as usize); - Ok(buf) - } + Ok(len) } #[pymethod] - fn write(&self, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { - if self.eof_written.load() { - return Err(vm.new_exception_msg( - PySslError::class(&vm.ctx).to_owned(), - "cannot write() after write_eof()".to_owned(), - )); - } + fn write_eof(&self, _vm: &VirtualMachine) -> PyResult<()> { + *self.eof.write() = true; + Ok(()) + } - data.with_ref(|buf| unsafe { - if buf.len() > i32::MAX as usize { - return Err( - vm.new_overflow_error(format!("string longer than {} bytes", i32::MAX)) - ); - } + #[pygetset] + fn pending(&self) -> i32 { + self.buffer.lock().len() as i32 + } - let nbytes = sys::BIO_write(self.bio, buf.as_ptr() as *const _, buf.len() as i32); - if nbytes < 0 { - return Err(convert_openssl_error(vm, ErrorStack::get())); - } + #[pygetset] + fn eof(&self) -> bool { + // EOF is true only when buffer is empty AND write_eof has been called + let pending = self.buffer.lock().len(); + pending == 0 && *self.eof.read() + } + } - Ok(nbytes) - }) + impl Representable for PyMemoryBIO { + #[inline] + fn repr_str(_zelf: &Py, _vm: &VirtualMachine) -> PyResult { + Ok("".to_owned()) } + } - #[pymethod] - fn write_eof(&self) { - self.eof_written.store(true); - unsafe { - BIO_clear_retry_flags(self.bio); - BIO_set_mem_eof_return(self.bio, 0); + impl Constructor for PyMemoryBIO { + type Args = (); + + fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + let obj = PyMemoryBIO { + buffer: PyMutex::new(Vec::new()), + eof: PyRwLock::new(false), } + .into_ref_with_type(vm, cls)?; + Ok(obj.into()) } } - #[pyclass(flags(IMMUTABLETYPE), with(Comparable))] - impl PySslSession { + // SSLSession - represents a cached SSL session + // NOTE: This is an EMULATION - actual session data is managed by Rustls internally + #[pyattr] + #[pyclass(name = "SSLSession", module = "ssl")] + #[derive(Debug, PyPayload)] + struct PySSLSession { + // Session data - serialized rustls session (EMULATED - kept empty) + session_data: Vec, + // Session ID - synthetic ID generated from metadata (NOT actual TLS session ID) + #[allow(dead_code)] + session_id: Vec, + // Session metadata + creation_time: std::time::SystemTime, + // Lifetime in seconds (default 7200 = 2 hours) + lifetime: u64, + } + + #[pyclass(flags(BASETYPE))] + impl PySSLSession { #[pygetset] fn time(&self) -> i64 { - unsafe { - #[cfg(ossl330)] - { - sys::SSL_SESSION_get_time(self.session) as i64 - } - #[cfg(not(ossl330))] - { - sys::SSL_SESSION_get_time(self.session) as i64 - } - } + // Return session creation time as Unix timestamp + self.creation_time + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i64 } #[pygetset] fn timeout(&self) -> i64 { - unsafe { sys::SSL_SESSION_get_timeout(self.session) as i64 } + // Return session timeout/lifetime in seconds + self.lifetime as i64 } #[pygetset] - fn ticket_lifetime_hint(&self) -> u64 { - // SSL_SESSION_get_ticket_lifetime_hint available in OpenSSL 1.1.0+ - #[cfg(ossl110)] - { - unsafe { SSL_SESSION_get_ticket_lifetime_hint(self.session) as u64 } - } - #[cfg(not(ossl110))] - { - // Not available in older OpenSSL versions - 0 - } + fn ticket_lifetime_hint(&self) -> i64 { + // Return ticket lifetime hint (same as timeout for rustls) + self.lifetime as i64 } #[pygetset] fn id(&self, vm: &VirtualMachine) -> PyBytesRef { - unsafe { - let mut len: libc::c_uint = 0; - let id_ptr = sys::SSL_SESSION_get_id(self.session, &mut len); - let id_slice = std::slice::from_raw_parts(id_ptr, len as usize); - vm.ctx.new_bytes(id_slice.to_vec()) - } + // Return session ID (hash of session data for uniqueness) + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + self.session_data.hash(&mut hasher); + let hash = hasher.finish(); + + // Convert hash to bytes + vm.ctx.new_bytes(hash.to_be_bytes().to_vec()) } #[pygetset] fn has_ticket(&self) -> bool { - // SSL_SESSION_has_ticket available in OpenSSL 1.1.0+ - #[cfg(ossl110)] - { - unsafe { SSL_SESSION_has_ticket(self.session) != 0 } - } - #[cfg(not(ossl110))] - { - // Not available in older OpenSSL versions - false - } + // For rustls, if we have session data, we have a ticket + !self.session_data.is_empty() } } - #[track_caller] - pub(crate) fn convert_openssl_error( - vm: &VirtualMachine, - err: ErrorStack, - ) -> PyBaseExceptionRef { - match err.errors().last() { - Some(e) => { - // Check if this is a system library error (errno-based) - // CPython: Modules/_ssl.c:667-671 - let lib = sys::ERR_GET_LIB(e.code()); - - if lib == sys::ERR_LIB_SYS { - // A system error is being reported; reason is set to errno - let reason = sys::ERR_GET_REASON(e.code()); - - // errno 2 = ENOENT = FileNotFoundError - let exc_type = if reason == 2 { - vm.ctx.exceptions.file_not_found_error.to_owned() - } else { - vm.ctx.exceptions.os_error.to_owned() - }; - let exc = vm.new_exception(exc_type, vec![vm.ctx.new_int(reason).into()]); - // Set errno attribute explicitly - let _ = exc - .as_object() - .set_attr("errno", vm.ctx.new_int(reason), vm); - return exc; - } - - let caller = std::panic::Location::caller(); - let (file, line) = (caller.file(), caller.line()); - let file = file - .rsplit_once(&['/', '\\'][..]) - .map_or(file, |(_, basename)| basename); - - // Get error codes - same approach as CPython - let lib = sys::ERR_GET_LIB(e.code()); - let reason = sys::ERR_GET_REASON(e.code()); - - // Look up error mnemonic from our static tables - // CPython uses dict lookup: err_codes_to_names[(lib, reason)] - let key = super::ssl_data::encode_error_key(lib, reason); - let errstr = super::ssl_data::ERROR_CODES - .get(&key) - .copied() - .or_else(|| { - // Fallback: use OpenSSL's error string - e.reason() - }) - .unwrap_or("unknown error"); - - // Check if this is a certificate verification error - // ERR_LIB_SSL = 20 (from _ssl_data_300.h) - // SSL_R_CERTIFICATE_VERIFY_FAILED = 134 (from _ssl_data_300.h) - let is_cert_verify_error = lib == 20 && reason == 134; - - // Look up library name from our static table - // CPython uses: lib_codes_to_names[lib] - let lib_name = super::ssl_data::LIBRARY_CODES.get(&(lib as u32)).copied(); - - // Use SSLCertVerificationError for certificate verification failures - let cls = if is_cert_verify_error { - PySslCertVerificationError::class(&vm.ctx).to_owned() - } else { - PySslError::class(&vm.ctx).to_owned() - }; - - // Build message - let msg = if let Some(lib_str) = lib_name { - format!("[{lib_str}] {errstr} ({file}:{line})") - } else { - format!("{errstr} ({file}:{line})") - }; - - // Create exception instance - let reason = sys::ERR_GET_REASON(e.code()); - let exc = vm.new_exception( - cls, - vec![vm.ctx.new_int(reason).into(), vm.ctx.new_str(msg).into()], - ); - - // Set attributes on instance, not class - let exc_obj: PyObjectRef = exc.into(); + impl Representable for PySSLSession { + #[inline] + fn repr_str(_zelf: &Py, _vm: &VirtualMachine) -> PyResult { + Ok("".to_owned()) + } + } - // Set reason attribute (always set, even if just the error string) - let reason_value = vm.ctx.new_str(errstr); - let _ = exc_obj.set_attr("reason", reason_value, vm); + // Helper functions - // Set library attribute (None if not available) - let library_value: PyObjectRef = if let Some(lib_str) = lib_name { - vm.ctx.new_str(lib_str).into() - } else { - vm.ctx.none() - }; - let _ = exc_obj.set_attr("library", library_value, vm); - - // For SSLCertVerificationError, set verify_code and verify_message - // Note: These will be set to None here, and can be updated by the caller - // if they have access to the SSL object - if is_cert_verify_error { - let _ = exc_obj.set_attr("verify_code", vm.ctx.none(), vm); - let _ = exc_obj.set_attr("verify_message", vm.ctx.none(), vm); - } + // OID module already imported at top of _ssl module - // Convert back to PyBaseExceptionRef - exc_obj.downcast().expect( - "exc_obj is created as PyBaseExceptionRef and must downcast successfully", - ) - } - None => { - let cls = PySslError::class(&vm.ctx).to_owned(); - vm.new_exception_empty(cls) - } - } + #[derive(FromArgs)] + struct Txt2ObjArgs { + txt: PyStrRef, + #[pyarg(named, optional)] + name: OptionalArg, } - // Helper function to set verify_code and verify_message on SSLCertVerificationError - fn set_verify_error_info( - exc: &PyBaseExceptionRef, - ssl_ptr: *const sys::SSL, - vm: &VirtualMachine, - ) { - // Get verify result - let verify_code = unsafe { sys::SSL_get_verify_result(ssl_ptr) }; - let verify_code_obj = vm.ctx.new_int(verify_code); - - // Get verify message - let verify_message = unsafe { - let verify_str = sys::X509_verify_cert_error_string(verify_code); - if verify_str.is_null() { - vm.ctx.none() - } else { - let c_str = std::ffi::CStr::from_ptr(verify_str); - vm.ctx.new_str(c_str.to_string_lossy()).into() - } + #[pyfunction] + fn txt2obj(args: Txt2ObjArgs, vm: &VirtualMachine) -> PyResult { + let txt = args.txt.as_str(); + let name = args.name.unwrap_or(false); + + // If name=False (default), only accept OID strings + // If name=True, accept both names and OID strings + let entry = if txt + .chars() + .next() + .map(|c| c.is_ascii_digit()) + .unwrap_or(false) + { + // Looks like an OID string (starts with digit) + oid::find_by_oid_string(txt) + } else if name { + // name=True: allow shortname/longname lookup + oid::find_by_name(txt) + } else { + // name=False: only OID strings allowed, not names + None }; - let exc_obj = exc.as_object(); - let _ = exc_obj.set_attr("verify_code", verify_code_obj, vm); - let _ = exc_obj.set_attr("verify_message", verify_message, vm); - } - #[track_caller] - fn convert_ssl_error( - vm: &VirtualMachine, - e: impl std::borrow::Borrow, - ) -> PyBaseExceptionRef { - let e = e.borrow(); - let (cls, msg) = match e.code() { - ssl::ErrorCode::WANT_READ => ( - PySslWantReadError::class(&vm.ctx).to_owned(), - "The operation did not complete (read)", - ), - ssl::ErrorCode::WANT_WRITE => ( - PySslWantWriteError::class(&vm.ctx).to_owned(), - "The operation did not complete (write)", - ), - ssl::ErrorCode::SYSCALL => match e.io_error() { - Some(io_err) => return io_err.to_pyexception(vm), - // When no I/O error and OpenSSL error queue is empty, - // this is an EOF in violation of protocol -> SSLEOFError - // Need to set args[0] = SSL_ERROR_EOF for suppress_ragged_eofs check - None => { - return vm.new_exception( - PySslEOFError::class(&vm.ctx).to_owned(), - vec![ - vm.ctx.new_int(SSL_ERROR_EOF).into(), - vm.ctx - .new_str("EOF occurred in violation of protocol") - .into(), - ], - ); - } - }, - ssl::ErrorCode::SSL => { - // Check for OpenSSL 3.0 SSL_R_UNEXPECTED_EOF_WHILE_READING - if let Some(ssl_err) = e.ssl_error() { - // In OpenSSL 3.0+, unexpected EOF is reported as SSL_ERROR_SSL - // with this specific reason code instead of SSL_ERROR_SYSCALL - unsafe { - let err_code = sys::ERR_peek_last_error(); - let reason = sys::ERR_GET_REASON(err_code); - let lib = sys::ERR_GET_LIB(err_code); - if lib == ERR_LIB_SSL && reason == SSL_R_UNEXPECTED_EOF_WHILE_READING { - return vm.new_exception( - PySslEOFError::class(&vm.ctx).to_owned(), - vec![ - vm.ctx.new_int(SSL_ERROR_EOF).into(), - vm.ctx - .new_str("EOF occurred in violation of protocol") - .into(), - ], - ); - } - } - return convert_openssl_error(vm, ssl_err.clone()); - } - ( - PySslError::class(&vm.ctx).to_owned(), - "A failure in the SSL library occurred", - ) - } - _ => ( - PySslError::class(&vm.ctx).to_owned(), - "A failure in the SSL library occurred", - ), - }; - vm.new_exception_msg(cls, msg.to_owned()) + let entry = entry.ok_or_else(|| vm.new_value_error(format!("unknown object '{txt}'")))?; + + // Return tuple: (nid, shortname, longname, oid) + Ok(vm + .new_tuple(( + vm.ctx.new_int(entry.nid), + vm.ctx.new_str(entry.short_name), + vm.ctx.new_str(entry.long_name), + vm.ctx.new_str(entry.oid_string()), + )) + .into()) } - // SSL_FILETYPE_ASN1 part of _add_ca_certs in CPython - fn x509_stack_from_der(der: &[u8]) -> Result, ErrorStack> { - unsafe { - openssl::init(); - let bio = bio::MemBioSlice::new(der)?; - - let mut certs = vec![]; - loop { - let cert = sys::d2i_X509_bio(bio.as_ptr(), std::ptr::null_mut()); - if cert.is_null() { - break; - } - certs.push(X509::from_ptr(cert)); - } + #[pyfunction] + fn nid2obj(nid: i32, vm: &VirtualMachine) -> PyResult { + let entry = oid::find_by_nid(nid) + .ok_or_else(|| vm.new_value_error(format!("unknown NID {nid}")))?; + + // Return tuple: (nid, shortname, longname, oid) + Ok(vm + .new_tuple(( + vm.ctx.new_int(entry.nid), + vm.ctx.new_str(entry.short_name), + vm.ctx.new_str(entry.long_name), + vm.ctx.new_str(entry.oid_string()), + )) + .into()) + } - let err = sys::ERR_peek_last_error(); + #[pyfunction] + fn get_default_verify_paths(vm: &VirtualMachine) -> PyResult { + // Return default certificate paths as a tuple + // Lib/ssl.py expects: (openssl_cafile_env, openssl_cafile, openssl_capath_env, openssl_capath) + // parts[0] = environment variable name for cafile + // parts[1] = default cafile path + // parts[2] = environment variable name for capath + // parts[3] = default capath path + + // Common default paths for different platforms + // These match the first candidates that rustls-native-certs/openssl-probe checks + #[cfg(target_os = "macos")] + let (default_cafile, default_capath) = { + // macOS primarily uses Keychain API, but provides fallback paths + // for compatibility and when Keychain access fails + (Some("/etc/ssl/cert.pem"), Some("/etc/ssl/certs")) + }; - if certs.is_empty() { - // let msg = if filetype == sys::SSL_FILETYPE_PEM { - // "no start line: cadata does not contain a certificate" - // } else { - // "not enough data: cadata does not contain a certificate" - // }; - return Err(ErrorStack::get()); - } - if err != 0 { - return Err(ErrorStack::get()); - } + #[cfg(target_os = "linux")] + let (default_cafile, default_capath) = { + // Linux: matches openssl-probe's first candidate (/etc/ssl/cert.pem) + // openssl-probe checks multiple locations at runtime, but we return + // OpenSSL's compile-time default + (Some("/etc/ssl/cert.pem"), Some("/etc/ssl/certs")) + }; - Ok(certs) - } + #[cfg(not(any(target_os = "macos", target_os = "linux")))] + let (default_cafile, default_capath): (Option<&str>, Option<&str>) = (None, None); + + let tuple = vm.ctx.new_tuple(vec![ + vm.ctx.new_str("SSL_CERT_FILE").into(), // openssl_cafile_env + default_cafile + .map(|s| vm.ctx.new_str(s).into()) + .unwrap_or_else(|| vm.ctx.none()), // openssl_cafile + vm.ctx.new_str("SSL_CERT_DIR").into(), // openssl_capath_env + default_capath + .map(|s| vm.ctx.new_str(s).into()) + .unwrap_or_else(|| vm.ctx.none()), // openssl_capath + ]); + Ok(tuple.into()) } - type CipherTuple = (&'static str, &'static str, i32); - - fn cipher_to_tuple(cipher: &ssl::SslCipherRef) -> CipherTuple { - (cipher.name(), cipher.version(), cipher.bits().secret) + #[pyfunction] + fn RAND_status() -> i32 { + 1 // Always have good randomness with aws-lc-rs } - fn cipher_description(cipher: *const sys::SSL_CIPHER) -> String { - unsafe { - // SSL_CIPHER_description writes up to 128 bytes - let mut buf = vec![0u8; 256]; - let result = sys::SSL_CIPHER_description( - cipher, - buf.as_mut_ptr() as *mut libc::c_char, - buf.len() as i32, - ); - if result.is_null() { - return String::from("No description available"); - } - // Find the null terminator - let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len()); - String::from_utf8_lossy(&buf[..len]).trim().to_string() - } + #[pyfunction] + fn RAND_add(_string: PyObjectRef, _entropy: f64) { + // No-op: aws-lc-rs handles its own entropy + // Accept any type (str, bytes, bytearray) } - impl Read for SocketStream { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let mut socket: &PySocket = &self.0; - socket.read(buf) + #[pyfunction] + fn RAND_bytes(n: i64, vm: &VirtualMachine) -> PyResult { + use aws_lc_rs::rand::{SecureRandom, SystemRandom}; + + // Validate n is not negative + if n < 0 { + return Err(vm.new_value_error("num must be positive")); } + + let n_usize = n as usize; + let rng = SystemRandom::new(); + let mut buf = vec![0u8; n_usize]; + rng.fill(&mut buf) + .map_err(|_| vm.new_os_error("Failed to generate random bytes"))?; + Ok(PyBytesRef::from(vm.ctx.new_bytes(buf))) } - impl Write for SocketStream { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - let mut socket: &PySocket = &self.0; - socket.write(buf) - } - fn flush(&mut self) -> std::io::Result<()> { - let mut socket: &PySocket = &self.0; - socket.flush() - } + #[pyfunction] + fn RAND_pseudo_bytes(n: i64, vm: &VirtualMachine) -> PyResult<(PyBytesRef, bool)> { + // In rustls/aws-lc-rs, all random bytes are cryptographically strong + let bytes = RAND_bytes(n, vm)?; + Ok((bytes, true)) } - #[cfg(target_os = "android")] - mod android { - use super::convert_openssl_error; - use crate::vm::{VirtualMachine, builtins::PyBaseExceptionRef}; - use openssl::{ - ssl::SslContextBuilder, - x509::{X509, store::X509StoreBuilder}, - }; - use std::{ - fs::{File, read_dir}, - io::Read, - path::Path, + /// Test helper to decode a certificate from a file path + /// + /// This is a simplified wrapper around cert_der_to_dict_helper that handles + /// file reading and PEM/DER auto-detection. Used by test suite. + #[pyfunction] + fn _test_decode_cert(path: PyStrRef, vm: &VirtualMachine) -> PyResult { + // Read certificate file + let cert_data = std::fs::read(path.as_str()).map_err(|e| { + vm.new_os_error(format!( + "Failed to read certificate file {}: {}", + path.as_str(), + e + )) + })?; + + // Auto-detect PEM vs DER format + let cert_der = if cert_data + .windows(27) + .any(|w| w == b"-----BEGIN CERTIFICATE-----") + { + // Parse PEM format + let mut cursor = std::io::Cursor::new(&cert_data); + rustls_pemfile::certs(&mut cursor) + .find_map(|r| r.ok()) + .ok_or_else(|| vm.new_value_error("No valid certificate found in PEM file"))? + .to_vec() + } else { + // Assume DER format + cert_data }; - static CERT_DIR: &'static str = "/system/etc/security/cacerts"; - - pub(super) fn load_client_ca_list( - vm: &VirtualMachine, - b: &mut SslContextBuilder, - ) -> Result<(), PyBaseExceptionRef> { - let root = Path::new(CERT_DIR); - if !root.is_dir() { - return Err(vm.new_exception_msg( - vm.ctx.exceptions.file_not_found_error.to_owned(), - CERT_DIR.to_string(), - )); - } + // Reuse the comprehensive helper function + cert::cert_der_to_dict_helper(vm, &cert_der) + } - let mut combined_pem = String::new(); - let entries = read_dir(root) - .map_err(|err| vm.new_os_error(format!("read cert root: {}", err)))?; - for entry in entries { - let entry = - entry.map_err(|err| vm.new_os_error(format!("iter cert root: {}", err)))?; + #[pyfunction] + fn DER_cert_to_PEM_cert(der_cert: ArgBytesLike, vm: &VirtualMachine) -> PyResult { + let der_bytes = der_cert.borrow_buf(); + let bytes_slice: &[u8] = der_bytes.as_ref(); - let path = entry.path(); - if !path.is_file() { - continue; - } + // Use pem-rfc7468 for RFC 7468 compliant PEM encoding + let pem_str = encode_string("CERTIFICATE", LineEnding::LF, bytes_slice) + .map_err(|e| vm.new_value_error(format!("PEM encoding failed: {e}")))?; - File::open(&path) - .and_then(|mut file| file.read_to_string(&mut combined_pem)) - .map_err(|err| { - vm.new_os_error(format!("open cert file {}: {}", path.display(), err)) - })?; + Ok(vm.ctx.new_str(pem_str)) + } - combined_pem.push('\n'); - } + #[pyfunction] + fn PEM_cert_to_DER_cert(pem_cert: PyStrRef, vm: &VirtualMachine) -> PyResult { + let pem_str = pem_cert.as_str(); - let mut store_b = - X509StoreBuilder::new().map_err(|err| convert_openssl_error(vm, err))?; - let x509_vec = X509::stack_from_pem(combined_pem.as_bytes()) - .map_err(|err| convert_openssl_error(vm, err))?; - for x509 in x509_vec { - store_b - .add_cert(x509) - .map_err(|err| convert_openssl_error(vm, err))?; - } - b.set_cert_store(store_b.build()); + // Parse PEM format + let mut cursor = std::io::Cursor::new(pem_str.as_bytes()); + let mut certs = rustls_pemfile::certs(&mut cursor); - Ok(()) + if let Some(Ok(cert)) = certs.next() { + Ok(vm.ctx.new_bytes(cert.to_vec())) + } else { + Err(vm.new_value_error("Failed to parse PEM certificate")) } } -} - -#[cfg(not(ossl101))] -#[pymodule(sub)] -mod ossl101 {} -#[cfg(not(ossl111))] -#[pymodule(sub)] -mod ossl111 {} - -#[cfg(not(windows))] -#[pymodule(sub)] -mod windows {} - -#[allow(non_upper_case_globals)] -#[cfg(ossl101)] -#[pymodule(sub)] -mod ossl101 { - #[pyattr] - use openssl_sys::{ - SSL_OP_NO_COMPRESSION as OP_NO_COMPRESSION, SSL_OP_NO_TLSv1_1 as OP_NO_TLSv1_1, - SSL_OP_NO_TLSv1_2 as OP_NO_TLSv1_2, - }; -} - -#[allow(non_upper_case_globals)] -#[cfg(ossl111)] -#[pymodule(sub)] -mod ossl111 { + // Certificate type for SSL module (pure Rust implementation) #[pyattr] - use openssl_sys::SSL_OP_NO_TLSv1_3 as OP_NO_TLSv1_3; -} - -#[cfg(windows)] -#[pymodule(sub)] -mod windows { - use crate::{ - common::ascii, - vm::{ - PyObjectRef, PyPayload, PyResult, VirtualMachine, - builtins::{PyFrozenSet, PyStrRef}, - convert::ToPyException, - }, - }; - - #[pyfunction] - fn enum_certificates(store_name: PyStrRef, vm: &VirtualMachine) -> PyResult> { - use schannel::{RawPointer, cert_context::ValidUses, cert_store::CertStore}; - use windows_sys::Win32::Security::Cryptography; - - // TODO: check every store for it, not just 2 of them: - // https://github.com/python/cpython/blob/3.8/Modules/_ssl.c#L5603-L5610 - let open_fns = [CertStore::open_current_user, CertStore::open_local_machine]; - let stores = open_fns - .iter() - .filter_map(|open| open(store_name.as_str()).ok()) - .collect::>(); - let certs = stores.iter().flat_map(|s| s.certs()).map(|c| { - let cert = vm.ctx.new_bytes(c.to_der().to_owned()); - let enc_type = unsafe { - let ptr = c.as_ptr() as *const Cryptography::CERT_CONTEXT; - (*ptr).dwCertEncodingType - }; - let enc_type = match enc_type { - Cryptography::X509_ASN_ENCODING => vm.new_pyobj(ascii!("x509_asn")), - Cryptography::PKCS_7_ASN_ENCODING => vm.new_pyobj(ascii!("pkcs_7_asn")), - other => vm.new_pyobj(other), - }; - let usage: PyObjectRef = match c.valid_uses().map_err(|e| e.to_pyexception(vm))? { - ValidUses::All => vm.ctx.new_bool(true).into(), - ValidUses::Oids(oids) => PyFrozenSet::from_iter( - vm, - oids.into_iter().map(|oid| vm.ctx.new_str(oid).into()), - )? - .into_ref(&vm.ctx) - .into(), - }; - Ok(vm.new_tuple((cert, enc_type, usage)).into()) - }); - let certs: Vec = certs.collect::>>()?; - Ok(certs) + #[pyclass(module = "_ssl", name = "Certificate")] + #[derive(Debug, PyPayload)] + pub struct PySSLCertificate { + // Store the raw DER bytes + der_bytes: Vec, } -} - -mod bio { - //! based off rust-openssl's private `bio` module - use libc::c_int; - use openssl::error::ErrorStack; - use openssl_sys as sys; - use std::marker::PhantomData; + impl PySSLCertificate { + // Parse the certificate lazily + fn parse(&self) -> Result, String> { + match x509_parser::parse_x509_certificate(&self.der_bytes) { + Ok((_, cert)) => Ok(cert), + Err(e) => Err(format!("Failed to parse certificate: {e}")), + } + } + } - pub struct MemBioSlice<'a>(*mut sys::BIO, PhantomData<&'a [u8]>); + #[pyclass(with(Comparable, Hashable, Representable))] + impl PySSLCertificate { + #[pymethod] + fn public_bytes( + &self, + format: OptionalArg, + vm: &VirtualMachine, + ) -> PyResult { + let format = format.unwrap_or(ENCODING_PEM); - impl Drop for MemBioSlice<'_> { - fn drop(&mut self) { - unsafe { - sys::BIO_free_all(self.0); + match format { + x if x == ENCODING_DER => { + // Return DER bytes directly + Ok(vm.ctx.new_bytes(self.der_bytes.clone()).into()) + } + x if x == ENCODING_PEM => { + // Convert DER to PEM using RFC 7468 compliant encoding + let pem_str = encode_string("CERTIFICATE", LineEnding::LF, &self.der_bytes) + .map_err(|e| vm.new_value_error(format!("PEM encoding failed: {e}")))?; + Ok(vm.ctx.new_str(pem_str).into()) + } + _ => Err(vm.new_value_error("Unsupported format")), } } + + #[pymethod] + fn get_info(&self, vm: &VirtualMachine) -> PyResult { + let cert = self.parse().map_err(|e| vm.new_value_error(e))?; + cert::cert_to_dict(vm, &cert) + } } - impl<'a> MemBioSlice<'a> { - pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { - openssl::init(); + // Implement Comparable trait for PySSLCertificate + impl Comparable for PySSLCertificate { + fn cmp( + zelf: &Py, + other: &PyObject, + op: PyComparisonOp, + _vm: &VirtualMachine, + ) -> PyResult { + op.eq_only(|| { + if let Some(other_cert) = other.downcast_ref::() { + Ok((zelf.der_bytes == other_cert.der_bytes).into()) + } else { + Ok(PyComparisonValue::NotImplemented) + } + }) + } + } - assert!(buf.len() <= c_int::MAX as usize); - let bio = unsafe { sys::BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int) }; - if bio.is_null() { - return Err(ErrorStack::get()); - } + // Implement Hashable trait for PySSLCertificate + impl Hashable for PySSLCertificate { + fn hash(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; - Ok(MemBioSlice(bio, PhantomData)) + let mut hasher = DefaultHasher::new(); + zelf.der_bytes.hash(&mut hasher); + Ok(hasher.finish() as PyHash) } + } - pub fn as_ptr(&self) -> *mut sys::BIO { - self.0 + // Implement Representable trait for PySSLCertificate + impl Representable for PySSLCertificate { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + // Try to parse and show subject + match zelf.parse() { + Ok(cert) => { + let subject = cert.subject(); + // Get CN if available + let cn = subject + .iter_common_name() + .next() + .and_then(|attr| attr.as_str().ok()) + .unwrap_or("Unknown"); + Ok(format!("")) + } + Err(_) => Ok("".to_owned()), + } } } } diff --git a/stdlib/src/ssl/cert.rs b/stdlib/src/ssl/cert.rs index 19dd09f3379..2baefad700b 100644 --- a/stdlib/src/ssl/cert.rs +++ b/stdlib/src/ssl/cert.rs @@ -1,232 +1,1774 @@ -pub(super) use ssl_cert::{PySSLCertificate, cert_to_certificate, cert_to_py, obj2txt}; - -// Certificate type for SSL module - -#[pymodule(sub)] -pub(crate) mod ssl_cert { - use crate::{ - common::ascii, - vm::{ - PyObjectRef, PyPayload, PyResult, VirtualMachine, - convert::{ToPyException, ToPyObject}, - function::{FsPath, OptionalArg}, - }, +// cspell: ignore accessdescs + +//! Certificate parsing, validation, and conversion utilities for SSL/TLS +//! +//! This module provides reusable functions for working with X.509 certificates: +//! - Parsing PEM/DER encoded certificates +//! - Validating certificate properties (CA status, etc.) +//! - Converting certificates to Python dict format +//! - Building and verifying certificate chains +//! - Loading certificates from files, directories, and bytes + +use chrono::{DateTime, Utc}; +use parking_lot::RwLock as ParkingRwLock; +use rustls::{ + DigitallySignedStruct, RootCertStore, SignatureScheme, + client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime}, + server::danger::{ClientCertVerified, ClientCertVerifier}, +}; +use rustpython_vm::{PyObjectRef, PyResult, VirtualMachine}; +use std::collections::HashSet; +use std::sync::Arc; +use x509_parser::prelude::*; + +use super::compat::{VERIFY_X509_PARTIAL_CHAIN, VERIFY_X509_STRICT}; + +// Certificate Verification Constants + +/// All supported signature schemes for certificate verification +/// +/// This list includes all modern signature algorithms supported by rustls. +/// Used by verifiers that accept any signature scheme (NoVerifier, EmptyRootStoreVerifier). +const ALL_SIGNATURE_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, +]; + +// Error Handling Utilities + +/// Certificate loading error types with specific error messages +/// +/// This module provides consistent error creation functions for certificate +/// operations, reducing code duplication and ensuring uniform error messages +/// across the codebase. +mod cert_error { + use std::io; + use std::sync::Arc; + + /// Create InvalidData error with formatted message + pub fn invalid_data(msg: impl Into) -> io::Error { + io::Error::new(io::ErrorKind::InvalidData, msg.into()) + } + + /// PEM parsing error variants + pub mod pem { + use super::*; + + pub fn no_start_line(context: &str) -> io::Error { + invalid_data(format!("no start line: {context}")) + } + + pub fn parse_failed(e: impl std::fmt::Display) -> io::Error { + invalid_data(format!("Failed to parse PEM certificate: {e}")) + } + + pub fn parse_failed_debug(e: impl std::fmt::Debug) -> io::Error { + invalid_data(format!("Failed to parse PEM certificate: {e:?}")) + } + + pub fn invalid_cert() -> io::Error { + invalid_data("No certificates found in certificate file") + } + } + + /// DER parsing error variants + pub mod der { + use super::*; + + pub fn not_enough_data(context: &str) -> io::Error { + invalid_data(format!("not enough data: {context}")) + } + + pub fn parse_failed(e: impl std::fmt::Display) -> io::Error { + invalid_data(format!("Failed to parse DER certificate: {e}")) + } + } + + /// Private key error variants + pub mod key { + use super::*; + + pub fn not_found(context: &str) -> io::Error { + invalid_data(format!("No private key found in {context}")) + } + + pub fn parse_failed(e: impl std::fmt::Display) -> io::Error { + invalid_data(format!("Failed to parse private key: {e}")) + } + + pub fn parse_encrypted_failed(e: impl std::fmt::Display) -> io::Error { + invalid_data(format!("Failed to parse encrypted private key: {e}")) + } + + pub fn decrypt_failed(e: impl std::fmt::Display) -> io::Error { + io::Error::other(format!( + "Failed to decrypt private key (wrong password?): {e}", + )) + } + } + + /// Convert error message to rustls::Error with InvalidCertificate wrapper + pub fn to_rustls_invalid_cert(msg: impl Into) -> rustls::Error { + rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError( + Arc::new(invalid_data(msg)), + ))) + } + + /// Convert error message to rustls::Error with InvalidCertificate wrapper and custom ErrorKind + pub fn to_rustls_cert_error(kind: io::ErrorKind, msg: impl Into) -> rustls::Error { + rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError( + Arc::new(io::Error::new(kind, msg.into())), + ))) + } +} + +// Helper Functions for Certificate Parsing + +/// Map X.509 OID to human-readable attribute name +/// +/// Converts common X.509 Distinguished Name OIDs to their standard names. +/// Returns the OID string itself if not recognized. +fn oid_to_attribute_name(oid_str: &str) -> &str { + match oid_str { + "2.5.4.3" => "commonName", + "2.5.4.6" => "countryName", + "2.5.4.7" => "localityName", + "2.5.4.8" => "stateOrProvinceName", + "2.5.4.10" => "organizationName", + "2.5.4.11" => "organizationalUnitName", + "1.2.840.113549.1.9.1" => "emailAddress", + _ => oid_str, + } +} + +/// Format IP address (IPv4 or IPv6) to string +/// +/// Formats raw IP address bytes according to standard notation: +/// - IPv4: dotted decimal (e.g., "192.0.2.1") +/// - IPv6: colon-separated hex (e.g., "2001:DB8:0:0:0:0:0:1") +fn format_ip_address(ip: &[u8]) -> String { + if ip.len() == 4 { + // IPv4 + format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]) + } else if ip.len() == 16 { + // IPv6 - format in full form without compression (uppercase) + // CPython returns IPv6 in full form: 2001:DB8:0:0:0:0:0:1 (not 2001:db8::1) + let segments = [ + u16::from_be_bytes([ip[0], ip[1]]), + u16::from_be_bytes([ip[2], ip[3]]), + u16::from_be_bytes([ip[4], ip[5]]), + u16::from_be_bytes([ip[6], ip[7]]), + u16::from_be_bytes([ip[8], ip[9]]), + u16::from_be_bytes([ip[10], ip[11]]), + u16::from_be_bytes([ip[12], ip[13]]), + u16::from_be_bytes([ip[14], ip[15]]), + ]; + format!( + "{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}", + segments[0], + segments[1], + segments[2], + segments[3], + segments[4], + segments[5], + segments[6], + segments[7] + ) + } else { + // Unknown format - return as debug string + format!("{ip:?}") + } +} + +/// Format ASN.1 time to string +/// +/// Formats certificate validity dates in the format: +/// "Mon DD HH:MM:SS YYYY GMT" +fn format_asn1_time(time: &x509_parser::time::ASN1Time) -> String { + let timestamp = time.timestamp(); + DateTime::::from_timestamp(timestamp, 0) + .expect("ASN1Time must be valid timestamp") + .format("%b %e %H:%M:%S %Y GMT") + .to_string() +} + +/// Format certificate serial number to hexadecimal string with even padding +/// +/// Converts a BigUint serial number to uppercase hex string, ensuring +/// even length by prepending '0' if necessary. +fn format_serial_number(serial: &num_bigint::BigUint) -> String { + let mut serial_str = serial.to_str_radix(16).to_uppercase(); + if serial_str.len() % 2 == 1 { + serial_str.insert(0, '0'); + } + serial_str +} + +/// Normalize wildcard hostname by stripping "*." prefix +/// +/// Returns the normalized hostname without the wildcard prefix. +/// Used for wildcard certificate matching. +fn normalize_wildcard_hostname(hostname: &str) -> &str { + hostname.strip_prefix("*.").unwrap_or(hostname) +} + +/// Process Subject Alternative Name (SAN) general names into Python tuples +/// +/// Converts X.509 GeneralName entries into Python tuple format. +/// Returns a vector of PyObjectRef tuples in the format: (type, value) +fn process_san_general_names( + vm: &VirtualMachine, + general_names: &[GeneralName<'_>], +) -> Vec { + general_names + .iter() + .filter_map(|name| match name { + GeneralName::DNSName(dns) => Some(vm.new_tuple(("DNS", *dns)).into()), + GeneralName::IPAddress(ip) => { + let ip_str = format_ip_address(ip); + Some(vm.new_tuple(("IP Address", ip_str)).into()) + } + GeneralName::RFC822Name(email) => Some(vm.new_tuple(("email", *email)).into()), + GeneralName::URI(uri) => Some(vm.new_tuple(("URI", *uri)).into()), + GeneralName::DirectoryName(dn) => { + let dn_str = format!("{dn}"); + Some(vm.new_tuple(("DirName", dn_str)).into()) + } + GeneralName::RegisteredID(oid) => { + let oid_str = oid.to_string(); + Some(vm.new_tuple(("Registered ID", oid_str)).into()) + } + GeneralName::OtherName(oid, value) => { + let oid_str = oid.to_string(); + let value_str = format!("{value:?}"); + Some( + vm.new_tuple(("othername", format!("{oid_str}:{value_str}"))) + .into(), + ) + } + _ => None, + }) + .collect() +} + +// Certificate Validation and Parsing + +/// Check if a certificate is a CA certificate by examining the Basic Constraints extension +/// +/// Returns `true` if the certificate has Basic Constraints with CA=true, +/// `false` otherwise (including parse errors or missing extension). +/// This matches OpenSSL's X509_check_ca() behavior. +pub fn is_ca_certificate(cert_der: &[u8]) -> bool { + // Parse the certificate + let Ok((_, cert)) = X509Certificate::from_der(cert_der) else { + return false; }; - use foreign_types_shared::ForeignTypeRef; - use openssl::{ - asn1::Asn1ObjectRef, - x509::{self, X509, X509Ref}, + + // Check Basic Constraints extension + // If extension exists and CA=true, it's a CA certificate + // Otherwise (no extension or CA=false), it's NOT a CA certificate + if let Ok(Some(ext)) = cert.basic_constraints() { + return ext.value.ca; + } + + // No Basic Constraints extension -> NOT a CA certificate + // (matches OpenSSL X509_check_ca() behavior) + false +} + +/// Convert an X509Name to Python nested tuple format for SSL certificate dicts +/// +/// Format: ((('CN', 'example.com'),), (('O', 'Example Org'),), ...) +fn name_to_py(vm: &VirtualMachine, name: &x509_parser::x509::X509Name<'_>) -> PyResult { + let list: Vec = name + .iter() + .flat_map(|rdn| { + // Each RDN can have multiple attributes + rdn.iter() + .map(|attr| { + let oid_str = attr.attr_type().to_id_string(); + let value_str = attr.attr_value().as_str().unwrap_or("").to_string(); + let key = oid_to_attribute_name(&oid_str); + + vm.new_tuple((vm.new_tuple((vm.ctx.new_str(key), vm.ctx.new_str(value_str))),)) + .into() + }) + .collect::>() + }) + .collect(); + + Ok(vm.ctx.new_tuple(list).into()) +} + +/// Convert DER-encoded certificate to Python dict (for getpeercert with binary_form=False) +/// +/// Returns a dict with fields: subject, issuer, version, serialNumber, +/// notBefore, notAfter, subjectAltName (if present) +pub fn cert_to_dict( + vm: &VirtualMachine, + cert: &x509_parser::certificate::X509Certificate<'_>, +) -> PyResult { + let dict = vm.ctx.new_dict(); + + // Subject and Issuer + dict.set_item("subject", name_to_py(vm, cert.subject())?, vm)?; + dict.set_item("issuer", name_to_py(vm, cert.issuer())?, vm)?; + + // Version (X.509 v3 = version 2 in the cert, but Python uses 3) + dict.set_item( + "version", + vm.ctx.new_int(cert.version().0 as i32 + 1).into(), + vm, + )?; + + // Serial number - hex format with even length + let serial = format_serial_number(&cert.serial); + dict.set_item("serialNumber", vm.ctx.new_str(serial).into(), vm)?; + + // Validity dates - format with GMT using chrono + dict.set_item( + "notBefore", + vm.ctx + .new_str(format_asn1_time(&cert.validity().not_before)) + .into(), + vm, + )?; + dict.set_item( + "notAfter", + vm.ctx + .new_str(format_asn1_time(&cert.validity().not_after)) + .into(), + vm, + )?; + + // Subject Alternative Names (if present) + if let Ok(Some(san_ext)) = cert.subject_alternative_name() { + let san_list = process_san_general_names(vm, &san_ext.value.general_names); + + if !san_list.is_empty() { + dict.set_item("subjectAltName", vm.ctx.new_tuple(san_list).into(), vm)?; + } + } + + Ok(dict.into()) +} + +/// Convert DER-encoded certificate to Python dict (for get_ca_certs) +/// +/// Similar to cert_to_dict but includes additional fields like crlDistributionPoints +/// and uses CPython's specific ordering: issuer, notAfter, notBefore, serialNumber, subject, version +pub fn cert_der_to_dict_helper(vm: &VirtualMachine, cert_der: &[u8]) -> PyResult { + // Parse the certificate using x509-parser + let (_, cert) = x509_parser::parse_x509_certificate(cert_der) + .map_err(|e| vm.new_value_error(format!("Failed to parse certificate: {e}")))?; + + // Helper to convert X509Name to nested tuple format + let name_to_tuple = |name: &x509_parser::x509::X509Name<'_>| -> PyResult { + let mut entries = Vec::new(); + for rdn in name.iter() { + for attr in rdn.iter() { + let oid_str = attr.attr_type().to_id_string(); + + // Get value as bytes and convert to string + let value_str = if let Ok(s) = attr.attr_value().as_str() { + s.to_string() + } else { + let value_bytes = attr.attr_value().data; + match std::str::from_utf8(value_bytes) { + Ok(s) => s.to_string(), + Err(_) => String::from_utf8_lossy(value_bytes).into_owned(), + } + }; + + let key = oid_to_attribute_name(&oid_str); + + let entry = + vm.new_tuple((vm.ctx.new_str(key.to_string()), vm.ctx.new_str(value_str))); + entries.push(vm.new_tuple((entry,)).into()); + } + } + Ok(vm.ctx.new_tuple(entries).into()) }; - use openssl_sys as sys; - use std::fmt; - - // Import constants and error converter from _ssl module - use crate::ssl::_ssl::{ENCODING_DER, ENCODING_PEM, convert_openssl_error}; - - pub(crate) fn obj2txt(obj: &Asn1ObjectRef, no_name: bool) -> Option { - let no_name = i32::from(no_name); - let ptr = obj.as_ptr(); - let b = unsafe { - let buflen = sys::OBJ_obj2txt(std::ptr::null_mut(), 0, ptr, no_name); - assert!(buflen >= 0); - if buflen == 0 { - return None; - } - let buflen = buflen as usize; - let mut buf = Vec::::with_capacity(buflen + 1); - let ret = sys::OBJ_obj2txt( - buf.as_mut_ptr() as *mut libc::c_char, - buf.capacity() as _, - ptr, - no_name, - ); - assert!(ret >= 0); - // SAFETY: OBJ_obj2txt initialized the buffer successfully - buf.set_len(buflen); - buf + + let dict = vm.ctx.new_dict(); + + // CPython ordering: issuer, notAfter, notBefore, serialNumber, subject, version + dict.set_item("issuer", name_to_tuple(cert.issuer())?, vm)?; + + // Validity - format with GMT using chrono + dict.set_item( + "notAfter", + vm.ctx + .new_str(format_asn1_time(&cert.validity().not_after)) + .into(), + vm, + )?; + dict.set_item( + "notBefore", + vm.ctx + .new_str(format_asn1_time(&cert.validity().not_before)) + .into(), + vm, + )?; + + // Serial number - hex format with even length + let serial = format_serial_number(&cert.serial); + dict.set_item("serialNumber", vm.ctx.new_str(serial).into(), vm)?; + + dict.set_item("subject", name_to_tuple(cert.subject())?, vm)?; + + // Version + dict.set_item( + "version", + vm.ctx.new_int(cert.version().0 as i32 + 1).into(), + vm, + )?; + + // Authority Information Access (OCSP and caIssuers) - use x509-parser's extensions_map + let mut ocsp_urls = Vec::new(); + let mut ca_issuer_urls = Vec::new(); + let mut crl_urls = Vec::new(); + + if let Ok(ext_map) = cert.tbs_certificate.extensions_map() { + use x509_parser::extensions::{GeneralName, ParsedExtension}; + use x509_parser::oid_registry::{ + OID_PKIX_AUTHORITY_INFO_ACCESS, OID_X509_EXT_CRL_DISTRIBUTION_POINTS, }; - let s = String::from_utf8(b) - .unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned()); - Some(s) + + // Authority Information Access + if let Some(ext) = ext_map.get(&OID_PKIX_AUTHORITY_INFO_ACCESS) + && let ParsedExtension::AuthorityInfoAccess(aia) = &ext.parsed_extension() + { + for desc in &aia.accessdescs { + if let GeneralName::URI(uri) = &desc.access_location { + let method_str = desc.access_method.to_id_string(); + if method_str == "1.3.6.1.5.5.7.48.1" { + // OCSP + ocsp_urls.push(vm.ctx.new_str(uri.to_string()).into()); + } else if method_str == "1.3.6.1.5.5.7.48.2" { + // caIssuers + ca_issuer_urls.push(vm.ctx.new_str(uri.to_string()).into()); + } + } + } + } + + // CRL Distribution Points + if let Some(ext) = ext_map.get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) + && let ParsedExtension::CRLDistributionPoints(cdp) = &ext.parsed_extension() + { + for dp in cdp.points.iter() { + if let Some(dist_point) = &dp.distribution_point { + use x509_parser::extensions::DistributionPointName; + if let DistributionPointName::FullName(names) = dist_point { + for name in names { + if let GeneralName::URI(uri) = name { + crl_urls.push(vm.ctx.new_str(uri.to_string()).into()); + } + } + } + } + } + } } - #[pyattr] - #[pyclass(module = "ssl", name = "Certificate")] - #[derive(PyPayload)] - pub(crate) struct PySSLCertificate { - cert: X509, + if !ocsp_urls.is_empty() { + dict.set_item("OCSP", vm.ctx.new_tuple(ocsp_urls).into(), vm)?; + } + if !ca_issuer_urls.is_empty() { + dict.set_item("caIssuers", vm.ctx.new_tuple(ca_issuer_urls).into(), vm)?; + } + if !crl_urls.is_empty() { + dict.set_item( + "crlDistributionPoints", + vm.ctx.new_tuple(crl_urls).into(), + vm, + )?; } - impl fmt::Debug for PySSLCertificate { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Certificate") + // Subject Alternative Names + if let Ok(Some(san_ext)) = cert.subject_alternative_name() { + let mut san_entries = Vec::new(); + for name in &san_ext.value.general_names { + use x509_parser::extensions::GeneralName; + match name { + GeneralName::DNSName(dns) => { + san_entries.push(vm.new_tuple(("DNS", *dns)).into()); + } + GeneralName::IPAddress(ip) => { + let ip_str = format_ip_address(ip); + san_entries.push(vm.new_tuple(("IP Address", ip_str)).into()); + } + GeneralName::RFC822Name(email) => { + san_entries.push(vm.new_tuple(("email", *email)).into()); + } + GeneralName::URI(uri) => { + san_entries.push(vm.new_tuple(("URI", *uri)).into()); + } + GeneralName::OtherName(_oid, _data) => { + // OtherName is not fully supported, mark as unsupported + san_entries.push(vm.new_tuple(("othername", "")).into()); + } + GeneralName::DirectoryName(name) => { + // Convert X509Name to nested tuple format + let dir_tuple = name_to_tuple(name)?; + san_entries.push(vm.new_tuple(("DirName", dir_tuple)).into()); + } + GeneralName::RegisteredID(oid) => { + // Convert OID to string representation + let oid_str = oid.to_id_string(); + san_entries.push(vm.new_tuple(("Registered ID", oid_str)).into()); + } + _ => {} + } + } + if !san_entries.is_empty() { + dict.set_item("subjectAltName", vm.ctx.new_tuple(san_entries).into(), vm)?; } } - #[pyclass] - impl PySSLCertificate { - #[pymethod] - fn public_bytes( - &self, - format: OptionalArg, - vm: &VirtualMachine, - ) -> PyResult { - let format = format.unwrap_or(ENCODING_PEM); + Ok(dict.into()) +} + +/// Build a verified certificate chain by adding CA certificates from the trust store +/// +/// Takes peer certificates (from TLS handshake) and extends the chain by finding +/// issuer certificates from the trust store until reaching a root certificate. +/// +/// Returns the complete chain as DER-encoded bytes. +pub fn build_verified_chain( + peer_certs: &[CertificateDer<'static>], + ca_certs_der: &[Vec], +) -> Vec> { + let mut chain_der: Vec> = Vec::new(); + + // Start with peer certificates (what was sent during handshake) + for cert in peer_certs { + chain_der.push(cert.as_ref().to_vec()); + } + + // Keep adding issuers until we reach a root or can't find the issuer + while let Some(der) = chain_der.last() { + let last_cert_der = der; + + // Parse the last certificate in the chain + let (_, last_cert) = match X509Certificate::from_der(last_cert_der) { + Ok(parsed) => parsed, + Err(_) => break, + }; + + // Check if it's self-signed (root certificate) + if last_cert.subject() == last_cert.issuer() { + // This is a root certificate, we're done + break; + } - match format { - x if x == ENCODING_DER => { - // DER encoding - let der = self - .cert - .to_der() - .map_err(|e| convert_openssl_error(vm, e))?; - Ok(vm.ctx.new_bytes(der).into()) + // Try to find the issuer in the trust store + let issuer_name = last_cert.issuer(); + let mut found_issuer = false; + + for ca_der in ca_certs_der.iter() { + let (_, ca_cert) = match X509Certificate::from_der(ca_der) { + Ok(parsed) => parsed, + Err(_) => continue, + }; + + // Check if this CA's subject matches the certificate's issuer + if ca_cert.subject() == issuer_name { + // Check if we already have this certificate in the chain + if !chain_der.iter().any(|existing| existing == ca_der) { + chain_der.push(ca_der.clone()); + found_issuer = true; + break; } - x if x == ENCODING_PEM => { - // PEM encoding - let pem = self - .cert - .to_pem() - .map_err(|e| convert_openssl_error(vm, e))?; - Ok(vm.ctx.new_bytes(pem).into()) + } + } + + if !found_issuer { + // Can't find issuer, stop here + break; + } + } + + chain_der +} + +/// Statistics from certificate loading operations +#[derive(Debug, Clone, Default)] +pub struct CertStats { + pub total_certs: usize, + pub ca_certs: usize, +} + +/// Certificate loader that handles PEM/DER parsing and validation +/// +/// This structure encapsulates the common pattern of loading certificates +/// from various sources (files, directories, bytes) and adding them to +/// a RootCertStore while tracking statistics. +/// +/// Duplicate certificates are detected and only counted once. +pub struct CertLoader<'a> { + store: &'a mut RootCertStore, + ca_certs_der: &'a mut Vec>, + seen_certs: HashSet>, +} + +impl<'a> CertLoader<'a> { + /// Create a new CertLoader with references to the store and DER cache + pub fn new(store: &'a mut RootCertStore, ca_certs_der: &'a mut Vec>) -> Self { + // Initialize seen_certs with existing certificates + let seen_certs = ca_certs_der.iter().cloned().collect(); + Self { + store, + ca_certs_der, + seen_certs, + } + } + + /// Load certificates from a file (supports both PEM and DER formats) + /// + /// Returns statistics about loaded certificates + pub fn load_from_file(&mut self, path: &str) -> Result { + let contents = std::fs::read(path)?; + self.load_from_bytes(&contents) + } + + /// Load certificates from a directory + /// + /// Reads all files in the directory and attempts to parse them as certificates. + /// Invalid files are silently skipped (matches OpenSSL capath behavior). + pub fn load_from_dir(&mut self, dir_path: &str) -> Result { + let entries = std::fs::read_dir(dir_path)?; + let mut stats = CertStats::default(); + + for entry in entries { + let entry = entry?; + let path = entry.path(); + + // Skip directories and process all files + // OpenSSL capath uses hash-based naming like "4e1295a3.0" + if path.is_file() + && let Ok(contents) = std::fs::read(&path) + { + // Ignore errors for individual files (some may not be certs) + if let Ok(file_stats) = self.load_from_bytes(&contents) { + stats.total_certs += file_stats.total_certs; + stats.ca_certs += file_stats.ca_certs; } - _ => Err(vm.new_value_error("Unsupported format".to_owned())), } } - #[pymethod] - fn get_info(&self, vm: &VirtualMachine) -> PyResult { - cert_to_dict(vm, &self.cert) + Ok(stats) + } + + /// Helper: Add a certificate to the store with duplicate checking + /// + /// Returns true if the certificate was added (not a duplicate), false if it was a duplicate. + fn add_cert_to_store( + &mut self, + cert_bytes: Vec, + cert_der: CertificateDer<'static>, + treat_all_as_ca: bool, + stats: &mut CertStats, + ) -> bool { + // Check for duplicates using HashSet + if !self.seen_certs.insert(cert_bytes.clone()) { + return false; // Duplicate certificate - skip + } + + // Determine if this is a CA certificate + let is_ca = if treat_all_as_ca { + true + } else { + is_ca_certificate(&cert_bytes) + }; + + // Store full DER for get_ca_certs() + self.ca_certs_der.push(cert_bytes); + + // Add to trust store (rustls may handle duplicates internally) + let _ = self.store.add(cert_der); + + // Update statistics + stats.total_certs += 1; + if is_ca { + stats.ca_certs += 1; } + + true } - fn name_to_py(vm: &VirtualMachine, name: &x509::X509NameRef) -> PyResult { - let list = name - .entries() - .map(|entry| { - let txt = obj2txt(entry.object(), false).to_pyobject(vm); - let asn1_str = entry.data(); - let data_bytes = asn1_str.as_slice(); - let data = match std::str::from_utf8(data_bytes) { - Ok(s) => vm.ctx.new_str(s.to_owned()), - Err(_) => vm - .ctx - .new_str(String::from_utf8_lossy(data_bytes).into_owned()), - }; - Ok(vm.new_tuple(((txt, data),)).into()) - }) - .collect::>()?; - Ok(vm.ctx.new_tuple(list).into()) - } - - // Helper to convert X509 to dict (for getpeercert with binary=False) - fn cert_to_dict(vm: &VirtualMachine, cert: &X509Ref) -> PyResult { - let dict = vm.ctx.new_dict(); - - dict.set_item("subject", name_to_py(vm, cert.subject_name())?, vm)?; - dict.set_item("issuer", name_to_py(vm, cert.issuer_name())?, vm)?; - // X.509 version: OpenSSL uses 0-based (0=v1, 1=v2, 2=v3) but Python uses 1-based (1=v1, 2=v2, 3=v3) - dict.set_item("version", vm.new_pyobj(cert.version() + 1), vm)?; - - let serial_num = cert - .serial_number() - .to_bn() - .and_then(|bn| bn.to_hex_str()) - .map_err(|e| convert_openssl_error(vm, e))?; - dict.set_item( - "serialNumber", - vm.ctx.new_str(serial_num.to_owned()).into(), - vm, - )?; + /// Load certificates from byte slice (auto-detects PEM vs DER format) + /// + /// Tries to parse as PEM first, falls back to DER if that fails. + /// Duplicate certificates are detected and only counted once. + /// + /// If `treat_all_as_ca` is true, all certificates are counted as CA certificates + /// regardless of their Basic Constraints (this matches + /// load_verify_locations with cadata parameter). + /// + /// If `pem_only` is true, only PEM parsing is attempted (for string input) + pub fn load_from_bytes_ex( + &mut self, + data: &[u8], + treat_all_as_ca: bool, + pem_only: bool, + ) -> Result { + let mut stats = CertStats::default(); - dict.set_item( - "notBefore", - vm.ctx.new_str(cert.not_before().to_string()).into(), - vm, - )?; - dict.set_item( - "notAfter", - vm.ctx.new_str(cert.not_after().to_string()).into(), - vm, - )?; + // Try to parse as PEM first + let mut cursor = std::io::Cursor::new(data); + let certs_iter = rustls_pemfile::certs(&mut cursor); + + let mut found_any = false; + let mut first_pem_error = None; // Store first PEM parsing error + for cert_result in certs_iter { + match cert_result { + Ok(cert) => { + found_any = true; + let cert_bytes = cert.to_vec(); + + // Validate that this is actually a valid X.509 certificate + // rustls_pemfile only does base64 decoding, not X.509 validation + if let Err(e) = X509Certificate::from_der(&cert_bytes) { + // Invalid X.509 certificate + return Err(cert_error::pem::parse_failed_debug(e)); + } + + // Add certificate using helper method (handles duplicates) + self.add_cert_to_store(cert_bytes, cert, treat_all_as_ca, &mut stats); + // Helper returns false for duplicates (skip counting) + } + Err(e) if !found_any => { + // PEM parsing failed on first certificate + if pem_only { + // For string input (PEM only), return "no start line" error + return Err(cert_error::pem::no_start_line( + "cadata does not contain a certificate", + )); + } + // Store the error and break to try DER format below + first_pem_error = Some(e); + break; + } + Err(e) => { + // PEM parsing failed after some certs were loaded + return Err(cert_error::pem::parse_failed(e)); + } + } + } + + // If PEM parsing found nothing, try DER format (unless pem_only) + // DER can have multiple certificates concatenated, so parse them sequentially + if !found_any && stats.total_certs == 0 { + // If we had a PEM parsing error, return it instead of trying DER fallback + // This ensures that malformed PEM files (like badcert.pem) raise an error + if let Some(e) = first_pem_error { + return Err(cert_error::pem::parse_failed(e)); + } - if let Some(names) = cert.subject_alt_names() { - let san: Vec = names - .iter() - .map(|gen_name| { - if let Some(email) = gen_name.email() { - vm.new_tuple((ascii!("email"), email)).into() - } else if let Some(dnsname) = gen_name.dnsname() { - vm.new_tuple((ascii!("DNS"), dnsname)).into() - } else if let Some(ip) = gen_name.ipaddress() { - // Parse IP address properly (IPv4 or IPv6) - let ip_str = if ip.len() == 4 { - // IPv4 - format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]) - } else if ip.len() == 16 { - // IPv6 - format with all zeros visible (not compressed) - let ip_addr = std::net::Ipv6Addr::from([ - ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], - ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15], - ]); - let s = ip_addr.segments(); - format!( - "{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}:{:X}", - s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7] - ) + // For PEM-only mode (string input), don't fallback to DER + if pem_only { + return Err(cert_error::pem::no_start_line( + "cadata does not contain a certificate", + )); + } + let mut remaining = data; + let mut loaded_count = 0; + + while !remaining.is_empty() { + match X509Certificate::from_der(remaining) { + Ok((rest, _parsed_cert)) => { + // Extract the DER bytes for this certificate + // Length = total remaining - bytes left after parsing + let cert_len = remaining.len() - rest.len(); + let cert_bytes = &remaining[..cert_len]; + let cert_der = CertificateDer::from(cert_bytes.to_vec()); + + // Add certificate using helper method (handles duplicates) + self.add_cert_to_store( + cert_bytes.to_vec(), + cert_der, + treat_all_as_ca, + &mut stats, + ); + + loaded_count += 1; + remaining = rest; // Move to next certificate + } + Err(e) => { + if loaded_count == 0 { + // Failed to parse first certificate - invalid data + return Err(cert_error::der::not_enough_data( + "cadata does not contain a certificate", + )); } else { - // Fallback for unexpected length - String::from_utf8_lossy(ip).into_owned() - }; - vm.new_tuple((ascii!("IP Address"), ip_str)).into() - } else if let Some(uri) = gen_name.uri() { - vm.new_tuple((ascii!("URI"), uri)).into() - } else { - // Handle DirName, Registered ID, and othername - // Check if this is a directory name - if let Some(dirname) = gen_name.directory_name() - && let Ok(py_name) = name_to_py(vm, dirname) - { - return vm.new_tuple((ascii!("DirName"), py_name)).into(); + // Loaded some certificates but failed on subsequent data (garbage) + return Err(cert_error::der::parse_failed(e)); } + } + } + } + + // If we somehow got here with no certificates loaded + if loaded_count == 0 { + return Err(cert_error::der::not_enough_data( + "cadata does not contain a certificate", + )); + } + } + + Ok(stats) + } + + /// Load certificates from byte slice (auto-detects PEM vs DER format) + /// + /// This is a convenience wrapper that calls load_from_bytes_ex with treat_all_as_ca=false + /// and pem_only=false. + pub fn load_from_bytes(&mut self, data: &[u8]) -> Result { + self.load_from_bytes_ex(data, false, false) + } +} + +// NoVerifier: disables certificate verification (for CERT_NONE mode) +#[derive(Debug)] +pub struct NoVerifier; + +impl ServerCertVerifier for NoVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + // Accept all certificates without verification + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + // Accept all signatures without verification + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + // Accept all signatures without verification + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + ALL_SIGNATURE_SCHEMES.to_vec() + } +} - // TODO: Handle Registered ID (GEN_RID) - // CPython implementation uses i2t_ASN1_OBJECT to convert OID - // This requires accessing GENERAL_NAME union which is complex in Rust - // For now, we return for unhandled types +// HostnameIgnoringVerifier: verifies certificate chain but ignores hostname +// This is used when check_hostname=False but verify_mode != CERT_NONE +// +// Unlike the previous implementation that used an inner WebPkiServerVerifier, +// this version uses webpki directly to verify only the certificate chain, +// completely bypassing hostname verification. +#[derive(Debug)] +pub struct HostnameIgnoringVerifier { + inner: Arc, +} - // For othername and other unsupported types - vm.new_tuple((ascii!("othername"), ascii!(""))) - .into() +impl HostnameIgnoringVerifier { + /// Create a new HostnameIgnoringVerifier with a pre-built verifier + /// This is useful when you need to configure the verifier with CRLs or other options + pub fn new_with_verifier(inner: Arc) -> Self { + Self { inner } + } +} + +impl ServerCertVerifier for HostnameIgnoringVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, // Intentionally ignored + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + // Extract a hostname from the certificate to pass to inner verifier + // The inner verifier will validate certificate chain, trust anchors, etc. + // but may fail on hostname mismatch - we'll catch and ignore that error + let dummy_hostname = extract_first_dns_name(end_entity) + .unwrap_or_else(|| ServerName::try_from("localhost").expect("localhost is valid")); + + // Call inner verifier for full certificate validation + match self.inner.verify_server_cert( + end_entity, + intermediates, + &dummy_hostname, + ocsp_response, + now, + ) { + Ok(verified) => Ok(verified), + Err(e) => { + // Check if the error is a hostname mismatch + // If so, ignore it (that's the whole point of HostnameIgnoringVerifier) + match e { + rustls::Error::InvalidCertificate( + rustls::CertificateError::NotValidForName, + ) + | rustls::Error::InvalidCertificate( + rustls::CertificateError::NotValidForNameContext { .. }, + ) => { + // Hostname mismatch - this is expected and acceptable + // The certificate chain, trust anchor, and expiry are valid + Ok(ServerCertVerified::assertion()) } - }) - .collect(); - dict.set_item("subjectAltName", vm.ctx.new_tuple(san).into(), vm)?; - }; + _ => { + // Other errors (expired cert, untrusted CA, etc.) should propagate + Err(e) + } + } + } + } + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() + } +} + +// Helper function to extract the first DNS name from a certificate +fn extract_first_dns_name(cert_der: &CertificateDer<'_>) -> Option> { + let (_, cert) = X509Certificate::from_der(cert_der.as_ref()).ok()?; + + // Try Subject Alternative Names first + if let Ok(Some(san_ext)) = cert.subject_alternative_name() { + for name in &san_ext.value.general_names { + if let x509_parser::extensions::GeneralName::DNSName(dns) = name { + // Remove wildcard prefix if present (e.g., "*.example.com" → "example.com") + // This allows us to use the domain for certificate chain verification + // when check_hostname=False + let dns_str = dns.to_string(); + let normalized_dns = normalize_wildcard_hostname(&dns_str); + + match ServerName::try_from(normalized_dns.to_string()) { + Ok(server_name) => { + return Some(server_name); + } + Err(_e) => { + // Continue to next + } + } + } + } + } - Ok(dict.into()) + // Fallback to Common Name + for rdn in cert.subject().iter() { + for attr in rdn.iter() { + if attr.attr_type() == &x509_parser::oid_registry::OID_X509_COMMON_NAME + && let Ok(cn) = attr.attr_value().as_str() + { + // Remove wildcard prefix if present + let normalized_cn = normalize_wildcard_hostname(cn); + + match ServerName::try_from(normalized_cn.to_string()) { + Ok(server_name) => { + return Some(server_name); + } + Err(_e) => {} + } + } + } } - // Helper to create Certificate object from X509 - pub(crate) fn cert_to_certificate(vm: &VirtualMachine, cert: X509) -> PyResult { - Ok(PySSLCertificate { cert }.into_ref(&vm.ctx).into()) + None +} + +// Custom client certificate verifier for TLS 1.3 deferred validation +// This verifier always succeeds during handshake but stores verification errors +// for later retrieval during I/O operations +#[derive(Debug)] +pub struct DeferredClientCertVerifier { + // The actual verifier that performs validation + inner: Arc, + // Shared storage for deferred error message + deferred_error: Arc>>, +} + +impl DeferredClientCertVerifier { + pub fn new( + inner: Arc, + deferred_error: Arc>>, + ) -> Self { + Self { + inner, + deferred_error, + } } +} - // For getpeercert() - returns bytes or dict depending on binary flag - pub(crate) fn cert_to_py(vm: &VirtualMachine, cert: &X509Ref, binary: bool) -> PyResult { - if binary { - let b = cert.to_der().map_err(|e| convert_openssl_error(vm, e))?; - Ok(vm.ctx.new_bytes(b).into()) +impl ClientCertVerifier for DeferredClientCertVerifier { + fn offer_client_auth(&self) -> bool { + self.inner.offer_client_auth() + } + + fn client_auth_mandatory(&self) -> bool { + // Delegate to inner verifier to respect CERT_REQUIRED mode + // This ensures client certificates are mandatory when verify_mode=CERT_REQUIRED + self.inner.client_auth_mandatory() + } + + fn root_hint_subjects(&self) -> &[rustls::DistinguishedName] { + self.inner.root_hint_subjects() + } + + fn verify_client_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + now: UnixTime, + ) -> Result { + // Perform the actual verification + let result = self + .inner + .verify_client_cert(end_entity, intermediates, now); + + // If verification failed, store the error for later + if result.is_err() { + let error_msg = "TLS handshake failed: received fatal alert: UnknownCA".to_string(); + *self.deferred_error.write() = Some(error_msg); + } + + // Always return success to allow handshake to complete + // The error will be raised during the first I/O operation + Ok(ClientCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() + } +} + +// Public Utility Functions + +/// Load certificate chain and private key from files +/// +/// This function loads a certificate chain from `cert_path` and a private key +/// from `key_path`. If `password` is provided, it will be used to decrypt +/// an encrypted private key. +/// +/// Returns (certificate_chain, private_key) on success. +/// +/// # Arguments +/// * `cert_path` - Path to certificate file (PEM or DER format) +/// * `key_path` - Path to private key file (PEM or DER format, optionally encrypted) +/// * `password` - Optional password for encrypted private key +/// +/// # Errors +/// Returns error if: +/// - Files cannot be read +/// - Certificate or key cannot be parsed +/// - Password is incorrect for encrypted key +pub(super) fn load_cert_chain_from_file( + cert_path: &str, + key_path: &str, + password: Option<&str>, +) -> Result<(Vec>, PrivateKeyDer<'static>), Box> { + // Load certificate file - preserve io::Error for errno + let cert_contents = std::fs::read(cert_path)?; + + // Parse certificates (PEM format) + let mut cert_cursor = std::io::Cursor::new(&cert_contents); + let certs: Vec> = rustls_pemfile::certs(&mut cert_cursor) + .collect::, _>>() + .map_err(cert_error::pem::parse_failed)?; + + if certs.is_empty() { + return Err(Box::new(cert_error::pem::invalid_cert())); + } + + // Load private key file - preserve io::Error for errno + let key_contents = std::fs::read(key_path)?; + + // Parse private key (supports PKCS8, RSA, EC formats) + let private_key = if let Some(pwd) = password { + // Try to parse as encrypted PKCS#8 + use der::SecretDocument; + use pkcs8::EncryptedPrivateKeyInfo; + use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; + + let pem_str = String::from_utf8_lossy(&key_contents); + + // Extract just the ENCRYPTED PRIVATE KEY block if present + // (file may contain multiple PEM blocks like key + certificate) + let encrypted_key_pem = if let Some(start) = + pem_str.find("-----BEGIN ENCRYPTED PRIVATE KEY-----") + { + if let Some(end_marker) = pem_str[start..].find("-----END ENCRYPTED PRIVATE KEY-----") { + let end = start + end_marker + "-----END ENCRYPTED PRIVATE KEY-----".len(); + Some(&pem_str[start..end]) + } else { + None + } } else { - cert_to_dict(vm, cert) + None + }; + + // Try to decode and decrypt PEM-encoded encrypted private key using pkcs8's PEM support + let decrypted_key_result = if let Some(key_pem) = encrypted_key_pem { + match SecretDocument::from_pem(key_pem) { + Ok((label, doc)) => { + if label == "ENCRYPTED PRIVATE KEY" { + // Parse encrypted key info from DER + match EncryptedPrivateKeyInfo::try_from(doc.as_bytes()) { + Ok(encrypted_key) => { + // Decrypt with password + match encrypted_key.decrypt(pwd.as_bytes()) { + Ok(decrypted) => { + // Convert decrypted SecretDocument to PrivateKeyDer + let key_vec: Vec = decrypted.as_bytes().to_vec(); + let pkcs8_key: PrivatePkcs8KeyDer<'static> = key_vec.into(); + Some(PrivateKeyDer::Pkcs8(pkcs8_key)) + } + Err(e) => { + return Err(Box::new(cert_error::key::decrypt_failed(e))); + } + } + } + Err(e) => { + return Err(Box::new(cert_error::key::parse_encrypted_failed(e))); + } + } + } else { + None + } + } + Err(_) => None, + } + } else { + None + }; + + match decrypted_key_result { + Some(key) => key, + None => { + // Not encrypted PKCS#8, try as unencrypted key + // (password might have been provided for an unencrypted key) + let mut key_cursor = std::io::Cursor::new(&key_contents); + match rustls_pemfile::private_key(&mut key_cursor) { + Ok(Some(key)) => key, + Ok(None) => { + return Err(Box::new(cert_error::key::not_found("key file"))); + } + Err(e) => { + return Err(Box::new(cert_error::key::parse_failed(e))); + } + } + } + } + } else { + // No password provided - try to parse unencrypted key + let mut key_cursor = std::io::Cursor::new(&key_contents); + match rustls_pemfile::private_key(&mut key_cursor) { + Ok(Some(key)) => key, + Ok(None) => { + return Err(Box::new(cert_error::key::not_found("key file"))); + } + Err(e) => { + return Err(Box::new(cert_error::key::parse_failed(e))); + } + } + }; + + Ok((certs, private_key)) +} + +/// Validate that a certificate and private key match +/// +/// This function checks that the public key in the certificate matches +/// the provided private key. This is a basic sanity check to prevent +/// configuration errors. +/// +/// # Arguments +/// * `certs` - Certificate chain (first certificate is the leaf) +/// * `private_key` - Private key to validate against +/// +/// # Errors +/// Returns error if: +/// - Certificate chain is empty +/// - Public key extraction fails +/// - Keys don't match +/// +/// Note: This is a simplified validation. Full validation would require +/// signing and verifying a test message, which is complex with rustls. +pub fn validate_cert_key_match( + certs: &[CertificateDer<'_>], + private_key: &PrivateKeyDer<'_>, +) -> Result<(), String> { + if certs.is_empty() { + return Err("Certificate chain is empty".to_string()); + } + + // For rustls, the actual validation happens when creating CertifiedKey + // We can attempt to create a signing key to verify the key is valid + use rustls::crypto::aws_lc_rs::sign::any_supported_type; + + match any_supported_type(private_key) { + Ok(_signing_key) => { + // If we can create a signing key, the private key is valid + // Rustls will validate the cert-key match when building config + Ok(()) + } + Err(_) => Err("PEM lib".to_string()), + } +} + +/// StrictCertVerifier: wraps a ServerCertVerifier and adds RFC 5280 strict validation +/// +/// When VERIFY_X509_STRICT flag is set, performs additional validation: +/// - Checks for Authority Key Identifier (AKI) extension (required by RFC 5280 Section 4.2.1.1) +/// - Validates other RFC 5280 compliance requirements +/// +/// This matches X509_V_FLAG_X509_STRICT behavior in OpenSSL. +#[derive(Debug)] +pub struct StrictCertVerifier { + inner: Arc, + verify_flags: i32, +} + +impl StrictCertVerifier { + /// Create a new StrictCertVerifier + /// + /// # Arguments + /// * `inner` - The underlying verifier to wrap + /// * `verify_flags` - SSL verification flags (e.g., VERIFY_X509_STRICT) + pub fn new(inner: Arc, verify_flags: i32) -> Self { + Self { + inner, + verify_flags, } } - #[pyfunction] - pub(crate) fn _test_decode_cert(path: FsPath, vm: &VirtualMachine) -> PyResult { - let path = path.to_path_buf(vm)?; - let pem = std::fs::read(path).map_err(|e| e.to_pyexception(vm))?; - let x509 = X509::from_pem(&pem).map_err(|e| convert_openssl_error(vm, e))?; - cert_to_py(vm, &x509, false) + /// Check if a certificate has the Authority Key Identifier extension + /// + /// RFC 5280 Section 4.2.1.1 states that conforming CAs MUST include this + /// extension in all certificates except self-signed certificates. + fn check_aki_present(cert_der: &[u8]) -> Result<(), String> { + let (_, cert) = X509Certificate::from_der(cert_der) + .map_err(|e| format!("Failed to parse certificate: {e}"))?; + + // Check for Authority Key Identifier extension (OID 2.5.29.35) + let has_aki = cert + .tbs_certificate + .extensions() + .iter() + .any(|ext| ext.oid == oid_registry::OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER); + + if !has_aki { + return Err( + "certificate verification failed: certificate missing required Authority Key Identifier extension" + .to_string(), + ); + } + + Ok(()) } } + +impl ServerCertVerifier for StrictCertVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + // First, perform the standard verification + let result = self.inner.verify_server_cert( + end_entity, + intermediates, + server_name, + ocsp_response, + now, + )?; + + // If VERIFY_X509_STRICT flag is set, perform additional validation + if self.verify_flags & VERIFY_X509_STRICT != 0 { + // Check end entity certificate for AKI + // RFC 5280 Section 4.2.1.1: self-signed certificates are exempt from AKI requirement + if !is_self_signed(end_entity) { + Self::check_aki_present(end_entity.as_ref()) + .map_err(cert_error::to_rustls_invalid_cert)?; + } + + // Check intermediate certificates for AKI + for intermediate in intermediates { + Self::check_aki_present(intermediate.as_ref()) + .map_err(cert_error::to_rustls_invalid_cert)?; + } + } + + Ok(result) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() + } +} + +/// EmptyRootStoreVerifier: used when verify_mode != CERT_NONE but no CA certs are loaded +/// +/// This verifier always fails certificate verification with UnknownIssuer error, +/// when no root certificates are available. +/// This allows the SSL context to be created successfully, but handshake will fail +/// with a proper SSLCertVerificationError (verify_code=20, UNABLE_TO_GET_ISSUER_CERT_LOCALLY). +#[derive(Debug)] +pub struct EmptyRootStoreVerifier; + +impl ServerCertVerifier for EmptyRootStoreVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + // Always fail with UnknownIssuer - when no CA certs loaded + // This will be mapped to X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20) + Err(rustls::Error::InvalidCertificate( + rustls::CertificateError::UnknownIssuer, + )) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + // Accept signatures during handshake - the cert verification will fail anyway + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result { + // Accept signatures during handshake - the cert verification will fail anyway + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + ALL_SIGNATURE_SCHEMES.to_vec() + } +} + +/// CRLCheckVerifier: Wraps a verifier to enforce CRL checking when flags are set +/// +/// This verifier ensures that when CRL checking flags are set (VERIFY_CRL_CHECK_LEAF = 4) +/// but no CRLs have been loaded, the verification fails with UnknownRevocationStatus. +/// This matches X509_V_FLAG_CRL_CHECK without loaded CRLs +/// causes "unable to get CRL" error. +#[derive(Debug)] +pub struct CRLCheckVerifier { + inner: Arc, + has_crls: bool, + crl_check_enabled: bool, +} + +impl CRLCheckVerifier { + pub fn new( + inner: Arc, + has_crls: bool, + crl_check_enabled: bool, + ) -> Self { + Self { + inner, + has_crls, + crl_check_enabled, + } + } +} + +impl ServerCertVerifier for CRLCheckVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + // If CRL checking is enabled but no CRLs are loaded, fail with UnknownRevocationStatus + // X509_V_ERR_UNABLE_TO_GET_CRL (3) + if self.crl_check_enabled && !self.has_crls { + return Err(rustls::Error::InvalidCertificate( + rustls::CertificateError::UnknownRevocationStatus, + )); + } + + // Otherwise, delegate to inner verifier + self.inner + .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() + } +} + +/// Partial Chain Verifier - Handles VERIFY_X509_PARTIAL_CHAIN flag +/// +/// OpenSSL's X509_V_FLAG_PARTIAL_CHAIN allows verification to succeed if any certificate +/// in the presented chain is found in the trust store, not just the root CA. This is useful +/// for trusting intermediate certificates or self-signed certificates directly. +/// +/// rustls's WebPkiServerVerifier doesn't support this behavior by default, so we wrap it +/// to add partial chain support when the flag is set. +/// +/// Behavior: +/// 1. Try standard verification first (full chain to trusted root) +/// 2. If that fails and VERIFY_X509_PARTIAL_CHAIN is set: +/// - Check if the end-entity certificate is in the trust store +/// - If yes, accept the certificate as trusted +/// +/// This matches accepting self-signed certificates that +/// are explicitly loaded via load_verify_locations(). +#[derive(Debug)] +pub struct PartialChainVerifier { + inner: Arc, + ca_certs_der: Vec>, + verify_flags: i32, +} + +impl PartialChainVerifier { + pub fn new( + inner: Arc, + ca_certs_der: Vec>, + verify_flags: i32, + ) -> Self { + Self { + inner, + ca_certs_der, + verify_flags, + } + } +} + +impl ServerCertVerifier for PartialChainVerifier { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + // Try standard verification first + match self.inner.verify_server_cert( + end_entity, + intermediates, + server_name, + ocsp_response, + now, + ) { + Ok(result) => Ok(result), + Err(e) => { + // If verification failed, check if the end-entity certificate is in the trust store + // OpenSSL behavior: + // 1. Self-signed certs in trust store: ALWAYS trusted (flag not required) + // 2. Non-self-signed end-entity certs in trust store: require VERIFY_X509_PARTIAL_CHAIN + // 3. Intermediate certs in trust store: require VERIFY_X509_PARTIAL_CHAIN + let end_entity_der = end_entity.as_ref(); + if self + .ca_certs_der + .iter() + .any(|cert_der| cert_der.as_slice() == end_entity_der) + { + // End-entity certificate is in the trust store + // Check if this is a self-signed certificate + let is_self_signed_cert = is_self_signed(end_entity); + + // Self-signed: always trust (OpenSSL behavior) + // Non-self-signed: require VERIFY_X509_PARTIAL_CHAIN flag + if is_self_signed_cert || (self.verify_flags & VERIFY_X509_PARTIAL_CHAIN != 0) { + // Certificate is trusted, but still perform hostname verification + verify_hostname(end_entity, server_name)?; + return Ok(ServerCertVerified::assertion()); + } + } + // No match found or non-self-signed without flag - return original error + Err(e) + } + } + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls12_signature(message, cert, dss) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + self.inner.verify_tls13_signature(message, cert, dss) + } + + fn supported_verify_schemes(&self) -> Vec { + self.inner.supported_verify_schemes() + } +} + +// Hostname Verification: + +/// Check if a certificate is self-signed by comparing issuer and subject. +/// Returns true if the certificate is self-signed (issuer == subject). +fn is_self_signed(cert_der: &CertificateDer<'_>) -> bool { + use x509_parser::prelude::*; + + // Parse the certificate + let Ok((_, cert)) = X509Certificate::from_der(cert_der.as_ref()) else { + // If we can't parse it, assume it's not self-signed (conservative approach) + return false; + }; + + // Compare issuer and subject + // A certificate is self-signed if issuer == subject + cert.issuer() == cert.subject() +} + +/// Verify that a certificate is valid for the given hostname/IP address. +/// This function checks Subject Alternative Names (SAN) and Common Name (CN). +fn verify_hostname( + cert_der: &CertificateDer<'_>, + server_name: &ServerName<'_>, +) -> Result<(), rustls::Error> { + use x509_parser::extensions::GeneralName; + use x509_parser::prelude::*; + + // Parse the certificate + let (_, cert) = X509Certificate::from_der(cert_der.as_ref()).map_err(|e| { + cert_error::to_rustls_invalid_cert(format!( + "Failed to parse certificate for hostname verification: {e}" + )) + })?; + + match server_name { + ServerName::DnsName(dns) => { + let expected_name = dns.as_ref(); + + // 1. Check Subject Alternative Names (SAN) - preferred method + if let Ok(Some(san_ext)) = cert.subject_alternative_name() { + for name in &san_ext.value.general_names { + if let GeneralName::DNSName(dns_name) = name + && hostname_matches(expected_name, dns_name) + { + return Ok(()); + } + } + } + + // 2. Fallback to Common Name (CN) - deprecated but still checked for compatibility + for rdn in cert.subject().iter() { + for attr in rdn.iter() { + if attr.attr_type() == &x509_parser::oid_registry::OID_X509_COMMON_NAME + && let Ok(cn) = attr.attr_value().as_str() + && hostname_matches(expected_name, cn) + { + return Ok(()); + } + } + } + + // No match found - return error + Err(cert_error::to_rustls_invalid_cert(format!( + "Hostname mismatch: certificate is not valid for '{expected_name}'", + ))) + } + ServerName::IpAddress(ip) => verify_ip_address(&cert, ip), + _ => { + // Unknown server name type + Err(cert_error::to_rustls_cert_error( + std::io::ErrorKind::InvalidInput, + "Unsupported server name type for hostname verification", + )) + } + } +} + +/// Match a hostname against a pattern, supporting wildcard certificates (*.example.com). +/// Implements RFC 6125 wildcard matching rules: +/// - Wildcard must be in the leftmost label +/// - Wildcard must be the only character in that label +/// - Wildcard must match at least one character +fn hostname_matches(expected: &str, pattern: &str) -> bool { + // Wildcard matching for *.example.com + if let Some(pattern_base) = pattern.strip_prefix("*.") { + // Find the first dot in expected hostname + if let Some(dot_pos) = expected.find('.') { + let expected_base = &expected[dot_pos + 1..]; + + // The base domains must match (case insensitive) + // and the leftmost label must not be empty + return dot_pos > 0 && expected_base.eq_ignore_ascii_case(pattern_base); + } + + // No dot in expected, can't match wildcard + return false; + } + + // Exact match (case insensitive per RFC 4343) + expected.eq_ignore_ascii_case(pattern) +} + +/// Verify that a certificate is valid for the given IP address. +/// Checks Subject Alternative Names for IP Address entries. +fn verify_ip_address( + cert: &X509Certificate<'_>, + expected_ip: &rustls::pki_types::IpAddr, +) -> Result<(), rustls::Error> { + use std::net::IpAddr; + use x509_parser::extensions::GeneralName; + + // Convert rustls IpAddr to std::net::IpAddr for comparison + let expected_std_ip: IpAddr = match expected_ip { + rustls::pki_types::IpAddr::V4(octets) => IpAddr::V4(std::net::Ipv4Addr::from(*octets)), + rustls::pki_types::IpAddr::V6(octets) => IpAddr::V6(std::net::Ipv6Addr::from(*octets)), + }; + + // Check Subject Alternative Names for IP addresses + if let Ok(Some(san_ext)) = cert.subject_alternative_name() { + for name in &san_ext.value.general_names { + if let GeneralName::IPAddress(cert_ip_bytes) = name { + // Parse the IP address from the certificate + let cert_ip = match cert_ip_bytes.len() { + 4 => { + // IPv4 + if let Ok(octets) = <[u8; 4]>::try_from(*cert_ip_bytes) { + IpAddr::V4(std::net::Ipv4Addr::from(octets)) + } else { + continue; + } + } + 16 => { + // IPv6 + if let Ok(octets) = <[u8; 16]>::try_from(*cert_ip_bytes) { + IpAddr::V6(std::net::Ipv6Addr::from(octets)) + } else { + continue; + } + } + _ => continue, // Invalid IP address length + }; + + if cert_ip == expected_std_ip { + return Ok(()); + } + } + } + } + + // No matching IP address found + Err(cert_error::to_rustls_invalid_cert(format!( + "IP address mismatch: certificate is not valid for '{expected_std_ip}'", + ))) +} diff --git a/stdlib/src/ssl/compat.rs b/stdlib/src/ssl/compat.rs new file mode 100644 index 00000000000..e4f979968e4 --- /dev/null +++ b/stdlib/src/ssl/compat.rs @@ -0,0 +1,1786 @@ +// spell-checker: ignore webpki ssleof sslerror akid certsign sslerr aesgcm + +// OpenSSL compatibility layer for rustls +// +// This module provides OpenSSL-like abstractions over rustls APIs, +// making the code more readable and maintainable. Each function is named +// after its OpenSSL equivalent (e.g., ssl_do_handshake corresponds to SSL_do_handshake). + +// SSL error code data tables (shared with OpenSSL backend for compatibility) +// These map OpenSSL error codes to human-readable strings +#[path = "../openssl/ssl_data_31.rs"] +mod ssl_data; + +use crate::socket::{SelectKind, timeout_error_msg}; +use crate::vm::VirtualMachine; +use parking_lot::RwLock as ParkingRwLock; +use rustls::RootCertStore; +use rustls::client::ClientConfig; +use rustls::client::ClientConnection; +use rustls::crypto::SupportedKxGroup; +use rustls::pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer}; +use rustls::server::ResolvesServerCert; +use rustls::server::ServerConfig; +use rustls::server::ServerConnection; +use rustls::sign::CertifiedKey; +use rustpython_vm::builtins::PyBaseExceptionRef; +use rustpython_vm::function::ArgBytesLike; +use rustpython_vm::{AsObject, PyObjectRef, PyPayload, PyResult, TryFromObject}; +use std::io::Read; +use std::sync::{Arc, Once}; + +// Import PySSLSocket and helper functions from parent module +use super::_ssl::{ + PySSLCertVerificationError, PySSLError, PySSLSocket, create_ssl_eof_error, + create_ssl_want_read_error, create_ssl_want_write_error, create_ssl_zero_return_error, +}; + +// SSL Verification Flags +/// VERIFY_X509_STRICT flag for RFC 5280 strict compliance +/// When set, performs additional validation including AKI extension checks +pub const VERIFY_X509_STRICT: i32 = 0x20; + +/// VERIFY_X509_PARTIAL_CHAIN flag for partial chain validation +/// When set, accept certificates if any certificate in the chain is in the trust store +/// (not just root CAs). This matches OpenSSL's X509_V_FLAG_PARTIAL_CHAIN behavior. +pub const VERIFY_X509_PARTIAL_CHAIN: i32 = 0x80000; + +// CryptoProvider Initialization: + +/// Ensure the default CryptoProvider is installed (thread-safe, runs once) +/// +/// This is necessary because rustls 0.23+ requires a process-level CryptoProvider +/// to be installed before using default_provider(). We use Once to ensure this +/// happens exactly once, even if called from multiple threads. +static INIT_PROVIDER: Once = Once::new(); + +fn ensure_default_provider() { + INIT_PROVIDER.call_once(|| { + let _ = rustls::crypto::CryptoProvider::install_default( + rustls::crypto::aws_lc_rs::default_provider(), + ); + }); +} + +// OpenSSL Constants: + +// OpenSSL TLS record maximum plaintext size (ssl/ssl_local.h) +// #define SSL3_RT_MAX_PLAIN_LENGTH 16384 +const SSL3_RT_MAX_PLAIN_LENGTH: usize = 16384; + +// OpenSSL error library codes (include/openssl/err.h) +// #define ERR_LIB_SSL 20 +const ERR_LIB_SSL: i32 = 20; + +// OpenSSL SSL error reason codes (include/openssl/sslerr.h) +// #define SSL_R_NO_SHARED_CIPHER 193 +const SSL_R_NO_SHARED_CIPHER: i32 = 193; + +// OpenSSL X509 verification flags (include/openssl/x509_vfy.h) +// #define X509_V_FLAG_CRL_CHECK 4 +const X509_V_FLAG_CRL_CHECK: i32 = 4; + +// X509 Certificate Verification Error Codes (OpenSSL Compatible): +// +// These constants match OpenSSL's X509_V_ERR_* values for certificate +// verification. They are used to map rustls certificate errors to OpenSSL +// error codes for compatibility. + +pub use x509::{ + X509_V_ERR_CERT_HAS_EXPIRED, X509_V_ERR_CERT_NOT_YET_VALID, X509_V_ERR_CERT_REVOKED, + X509_V_ERR_HOSTNAME_MISMATCH, X509_V_ERR_INVALID_PURPOSE, X509_V_ERR_IP_ADDRESS_MISMATCH, + X509_V_ERR_UNABLE_TO_GET_CRL, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + X509_V_ERR_UNSPECIFIED, +}; + +#[allow(dead_code)] +mod x509 { + pub const X509_V_OK: i32 = 0; + pub const X509_V_ERR_UNSPECIFIED: i32 = 1; + pub const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: i32 = 2; + pub const X509_V_ERR_UNABLE_TO_GET_CRL: i32 = 3; + pub const X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: i32 = 4; + pub const X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: i32 = 5; + pub const X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: i32 = 6; + pub const X509_V_ERR_CERT_SIGNATURE_FAILURE: i32 = 7; + pub const X509_V_ERR_CRL_SIGNATURE_FAILURE: i32 = 8; + pub const X509_V_ERR_CERT_NOT_YET_VALID: i32 = 9; + pub const X509_V_ERR_CERT_HAS_EXPIRED: i32 = 10; + pub const X509_V_ERR_CRL_NOT_YET_VALID: i32 = 11; + pub const X509_V_ERR_CRL_HAS_EXPIRED: i32 = 12; + pub const X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: i32 = 13; + pub const X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: i32 = 14; + pub const X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: i32 = 15; + pub const X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: i32 = 16; + pub const X509_V_ERR_OUT_OF_MEM: i32 = 17; + pub const X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: i32 = 18; + pub const X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: i32 = 19; + pub const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: i32 = 20; + pub const X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: i32 = 21; + pub const X509_V_ERR_CERT_CHAIN_TOO_LONG: i32 = 22; + pub const X509_V_ERR_CERT_REVOKED: i32 = 23; + pub const X509_V_ERR_INVALID_CA: i32 = 24; + pub const X509_V_ERR_PATH_LENGTH_EXCEEDED: i32 = 25; + pub const X509_V_ERR_INVALID_PURPOSE: i32 = 26; + pub const X509_V_ERR_CERT_UNTRUSTED: i32 = 27; + pub const X509_V_ERR_CERT_REJECTED: i32 = 28; + pub const X509_V_ERR_SUBJECT_ISSUER_MISMATCH: i32 = 29; + pub const X509_V_ERR_AKID_SKID_MISMATCH: i32 = 30; + pub const X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: i32 = 31; + pub const X509_V_ERR_KEYUSAGE_NO_CERTSIGN: i32 = 32; + pub const X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: i32 = 33; + pub const X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: i32 = 34; + pub const X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: i32 = 35; + pub const X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: i32 = 36; + pub const X509_V_ERR_INVALID_NON_CA: i32 = 37; + pub const X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: i32 = 38; + pub const X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: i32 = 39; + pub const X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: i32 = 40; + pub const X509_V_ERR_INVALID_EXTENSION: i32 = 41; + pub const X509_V_ERR_INVALID_POLICY_EXTENSION: i32 = 42; + pub const X509_V_ERR_NO_EXPLICIT_POLICY: i32 = 43; + pub const X509_V_ERR_DIFFERENT_CRL_SCOPE: i32 = 44; + pub const X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: i32 = 45; + pub const X509_V_ERR_UNNESTED_RESOURCE: i32 = 46; + pub const X509_V_ERR_PERMITTED_VIOLATION: i32 = 47; + pub const X509_V_ERR_EXCLUDED_VIOLATION: i32 = 48; + pub const X509_V_ERR_SUBTREE_MINMAX: i32 = 49; + pub const X509_V_ERR_APPLICATION_VERIFICATION: i32 = 50; + pub const X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: i32 = 51; + pub const X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: i32 = 52; + pub const X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: i32 = 53; + pub const X509_V_ERR_CRL_PATH_VALIDATION_ERROR: i32 = 54; + pub const X509_V_ERR_HOSTNAME_MISMATCH: i32 = 62; + pub const X509_V_ERR_EMAIL_MISMATCH: i32 = 63; + pub const X509_V_ERR_IP_ADDRESS_MISMATCH: i32 = 64; +} + +// Certificate Error Conversion Functions: + +/// Convert rustls CertificateError to X509 verification code and message +/// +/// Maps rustls certificate errors to OpenSSL X509_V_ERR_* codes for compatibility. +/// Returns (verify_code, verify_message) tuple. +fn rustls_cert_error_to_verify_info(cert_err: &rustls::CertificateError) -> (i32, &'static str) { + use rustls::CertificateError; + + match cert_err { + CertificateError::UnknownIssuer => ( + X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + "unable to get local issuer certificate", + ), + CertificateError::Expired => (X509_V_ERR_CERT_HAS_EXPIRED, "certificate has expired"), + CertificateError::NotValidYet => ( + X509_V_ERR_CERT_NOT_YET_VALID, + "certificate is not yet valid", + ), + CertificateError::Revoked => (X509_V_ERR_CERT_REVOKED, "certificate revoked"), + CertificateError::UnknownRevocationStatus => ( + X509_V_ERR_UNABLE_TO_GET_CRL, + "unable to get certificate CRL", + ), + CertificateError::InvalidPurpose => ( + X509_V_ERR_INVALID_PURPOSE, + "unsupported certificate purpose", + ), + CertificateError::Other(other_err) => { + // Check if this is a hostname mismatch error from our verify_hostname function + let err_msg = format!("{other_err:?}"); + if err_msg.contains("Hostname mismatch") || err_msg.contains("not valid for") { + ( + X509_V_ERR_HOSTNAME_MISMATCH, + "Hostname mismatch, certificate is not valid for", + ) + } else if err_msg.contains("IP address mismatch") { + ( + X509_V_ERR_IP_ADDRESS_MISMATCH, + "IP address mismatch, certificate is not valid for", + ) + } else { + (X509_V_ERR_UNSPECIFIED, "certificate verification failed") + } + } + _ => (X509_V_ERR_UNSPECIFIED, "certificate verification failed"), + } +} + +/// Create SSLCertVerificationError with proper attributes +/// +/// Matches CPython's _ssl.c fill_and_set_sslerror() behavior. +/// This function creates a Python SSLCertVerificationError exception with verify_code +/// and verify_message attributes set appropriately for the given rustls certificate error. +/// +/// # Note +/// If attribute setting fails (extremely rare), returns the exception without attributes +pub(super) fn create_ssl_cert_verification_error( + vm: &VirtualMachine, + cert_err: &rustls::CertificateError, +) -> PyResult { + let (verify_code, verify_message) = rustls_cert_error_to_verify_info(cert_err); + + let msg = + format!("[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: {verify_message}",); + + let exc = vm.new_exception_msg(PySSLCertVerificationError::class(&vm.ctx).to_owned(), msg); + + // Set verify_code and verify_message attributes + // Ignore errors as they're extremely rare (e.g., out of memory) + exc.as_object().set_attr( + "verify_code", + vm.ctx.new_int(verify_code).as_object().to_owned(), + vm, + )?; + exc.as_object().set_attr( + "verify_message", + vm.ctx.new_str(verify_message).as_object().to_owned(), + vm, + )?; + + exc.as_object() + .set_attr("library", vm.ctx.new_str("SSL").as_object().to_owned(), vm)?; + exc.as_object().set_attr( + "reason", + vm.ctx + .new_str("CERTIFICATE_VERIFY_FAILED") + .as_object() + .to_owned(), + vm, + )?; + + Ok(exc) +} + +/// Unified TLS connection type (client or server) +#[derive(Debug)] +pub(super) enum TlsConnection { + Client(ClientConnection), + Server(ServerConnection), +} + +impl TlsConnection { + /// Check if handshake is in progress + pub fn is_handshaking(&self) -> bool { + match self { + TlsConnection::Client(conn) => conn.is_handshaking(), + TlsConnection::Server(conn) => conn.is_handshaking(), + } + } + + /// Check if connection wants to read data + pub fn wants_read(&self) -> bool { + match self { + TlsConnection::Client(conn) => conn.wants_read(), + TlsConnection::Server(conn) => conn.wants_read(), + } + } + + /// Check if connection wants to write data + pub fn wants_write(&self) -> bool { + match self { + TlsConnection::Client(conn) => conn.wants_write(), + TlsConnection::Server(conn) => conn.wants_write(), + } + } + + /// Read TLS data from socket + pub fn read_tls(&mut self, reader: &mut dyn std::io::Read) -> std::io::Result { + match self { + TlsConnection::Client(conn) => conn.read_tls(reader), + TlsConnection::Server(conn) => conn.read_tls(reader), + } + } + + /// Write TLS data to socket + pub fn write_tls(&mut self, writer: &mut dyn std::io::Write) -> std::io::Result { + match self { + TlsConnection::Client(conn) => conn.write_tls(writer), + TlsConnection::Server(conn) => conn.write_tls(writer), + } + } + + /// Process new TLS packets + pub fn process_new_packets(&mut self) -> Result { + match self { + TlsConnection::Client(conn) => conn.process_new_packets(), + TlsConnection::Server(conn) => conn.process_new_packets(), + } + } + + /// Get reader for plaintext data (rustls native type) + pub fn reader(&mut self) -> rustls::Reader<'_> { + match self { + TlsConnection::Client(conn) => conn.reader(), + TlsConnection::Server(conn) => conn.reader(), + } + } + + /// Get writer for plaintext data (rustls native type) + pub fn writer(&mut self) -> rustls::Writer<'_> { + match self { + TlsConnection::Client(conn) => conn.writer(), + TlsConnection::Server(conn) => conn.writer(), + } + } + + /// Check if session was resumed + pub fn is_session_resumed(&self) -> bool { + use rustls::HandshakeKind; + match self { + TlsConnection::Client(conn) => { + matches!(conn.handshake_kind(), Some(HandshakeKind::Resumed)) + } + TlsConnection::Server(conn) => { + matches!(conn.handshake_kind(), Some(HandshakeKind::Resumed)) + } + } + } + + /// Send close_notify alert + pub fn send_close_notify(&mut self) { + match self { + TlsConnection::Client(conn) => conn.send_close_notify(), + TlsConnection::Server(conn) => conn.send_close_notify(), + } + } + + /// Get negotiated ALPN protocol + pub fn alpn_protocol(&self) -> Option<&[u8]> { + match self { + TlsConnection::Client(conn) => conn.alpn_protocol(), + TlsConnection::Server(conn) => conn.alpn_protocol(), + } + } + + /// Get negotiated cipher suite + pub fn negotiated_cipher_suite(&self) -> Option { + match self { + TlsConnection::Client(conn) => conn.negotiated_cipher_suite(), + TlsConnection::Server(conn) => conn.negotiated_cipher_suite(), + } + } + + /// Get peer certificates + pub fn peer_certificates(&self) -> Option<&[rustls::pki_types::CertificateDer<'static>]> { + match self { + TlsConnection::Client(conn) => conn.peer_certificates(), + TlsConnection::Server(conn) => conn.peer_certificates(), + } + } +} + +/// Error types matching OpenSSL error codes +#[derive(Debug)] +pub(super) enum SslError { + /// SSL_ERROR_WANT_READ + WantRead, + /// SSL_ERROR_WANT_WRITE + WantWrite, + /// SSL_ERROR_SYSCALL + Syscall(String), + /// SSL_ERROR_SSL + Ssl(String), + /// SSL_ERROR_ZERO_RETURN (clean closure with close_notify) + ZeroReturn, + /// Unexpected EOF without close_notify (protocol violation) + Eof, + /// Certificate verification error + CertVerification(rustls::CertificateError), + /// I/O error + Io(std::io::Error), + /// Timeout error (socket.timeout) + Timeout(String), + /// SNI callback triggered - need to restart handshake + SniCallbackRestart, + /// Python exception (pass through directly) + Py(PyBaseExceptionRef), + /// TLS alert received with OpenSSL-compatible error code + AlertReceived { lib: i32, reason: i32 }, + /// NO_SHARED_CIPHER error (OpenSSL SSL_R_NO_SHARED_CIPHER) + NoCipherSuites, +} + +impl SslError { + /// Convert TLS alert code to OpenSSL error reason code + /// OpenSSL uses reason = 1000 + alert_code for TLS alerts + fn alert_to_openssl_reason(alert: rustls::AlertDescription) -> i32 { + // AlertDescription can be converted to u8 via as u8 cast + 1000 + (u8::from(alert) as i32) + } + + /// Convert rustls error to SslError + pub fn from_rustls(err: rustls::Error) -> Self { + match err { + rustls::Error::InvalidCertificate(cert_err) => SslError::CertVerification(cert_err), + rustls::Error::AlertReceived(alert_desc) => { + // Map TLS alerts to OpenSSL-compatible error codes + // lib = 20 (ERR_LIB_SSL), reason = 1000 + alert_code + match alert_desc { + rustls::AlertDescription::CloseNotify => { + // Special case: close_notify is handled as ZeroReturn + SslError::ZeroReturn + } + _ => { + // All other alerts: convert to OpenSSL error code + // This includes InternalError (80 -> reason 1080) + SslError::AlertReceived { + lib: ERR_LIB_SSL, + reason: Self::alert_to_openssl_reason(alert_desc), + } + } + } + } + // OpenSSL 3.0 changed transport EOF from SSL_ERROR_SYSCALL with + // zero return value to SSL_ERROR_SSL with SSL_R_UNEXPECTED_EOF_WHILE_READING. + // In rustls, these cases correspond to unexpected connection closure: + rustls::Error::InvalidMessage(_) => { + // UnexpectedMessage, CorruptMessage, etc. → SSLEOFError + // Matches CPython's "EOF occurred in violation of protocol" + SslError::Eof + } + rustls::Error::PeerIncompatible(peer_err) => { + // Check for specific incompatibility types + use rustls::PeerIncompatible; + match peer_err { + PeerIncompatible::NoCipherSuitesInCommon => { + // Maps to OpenSSL SSL_R_NO_SHARED_CIPHER (lib=20, reason=193) + SslError::NoCipherSuites + } + _ => { + // Other protocol incompatibilities → SSLEOFError + SslError::Eof + } + } + } + _ => SslError::Ssl(format!("{err}")), + } + } + + /// Create SSLError with library and reason from string values + /// + /// This is the base helper for creating SSLError with _library and _reason + /// attributes when you already have the string values. + /// + /// # Arguments + /// * `vm` - Virtual machine reference + /// * `library` - Library name (e.g., "PEM", "SSL") + /// * `reason` - Error reason (e.g., "PEM lib", "NO_SHARED_CIPHER") + /// * `message` - Main error message + /// + /// # Returns + /// PyBaseExceptionRef with _library and _reason attributes set + /// + /// # Note + /// If attribute setting fails (extremely rare), returns the exception without attributes + pub(super) fn create_ssl_error_with_reason( + vm: &VirtualMachine, + library: &str, + reason: &str, + message: impl Into, + ) -> PyBaseExceptionRef { + let exc = vm.new_exception_msg(PySSLError::class(&vm.ctx).to_owned(), message.into()); + + // Set library and reason attributes + // Ignore errors as they're extremely rare (e.g., out of memory) + let _ = exc.as_object().set_attr( + "library", + vm.ctx.new_str(library).as_object().to_owned(), + vm, + ); + let _ = + exc.as_object() + .set_attr("reason", vm.ctx.new_str(reason).as_object().to_owned(), vm); + + exc + } + + /// Create SSLError with library and reason from ssl_data codes + /// + /// This helper converts OpenSSL numeric error codes to Python SSLError exceptions + /// with proper _library and _reason attributes by looking up the error strings + /// in ssl_data tables, then delegates to create_ssl_error_with_reason. + /// + /// # Arguments + /// * `vm` - Virtual machine reference + /// * `lib` - OpenSSL library code (e.g., ERR_LIB_SSL = 20) + /// * `reason` - OpenSSL reason code (e.g., SSL_R_NO_SHARED_CIPHER = 193) + /// + /// # Returns + /// PyBaseExceptionRef with _library and _reason attributes set + fn create_ssl_error_from_codes( + vm: &VirtualMachine, + lib: i32, + reason: i32, + ) -> PyBaseExceptionRef { + // Look up error strings from ssl_data tables + let key = ssl_data::encode_error_key(lib, reason); + let reason_str = ssl_data::ERROR_CODES + .get(&key) + .copied() + .unwrap_or("unknown error"); + + let lib_str = ssl_data::LIBRARY_CODES + .get(&(lib as u32)) + .copied() + .unwrap_or("UNKNOWN"); + + // Delegate to create_ssl_error_with_reason for actual exception creation + Self::create_ssl_error_with_reason(vm, lib_str, reason_str, format!("[SSL] {reason_str}")) + } + + /// Convert to Python exception + pub fn into_py_err(self, vm: &VirtualMachine) -> PyBaseExceptionRef { + match self { + SslError::WantRead => create_ssl_want_read_error(vm), + SslError::WantWrite => create_ssl_want_write_error(vm), + SslError::Timeout(msg) => timeout_error_msg(vm, msg), + SslError::Syscall(msg) => vm.new_os_error(msg), + SslError::Ssl(msg) => vm.new_exception_msg( + PySSLError::class(&vm.ctx).to_owned(), + format!("SSL error: {msg}"), + ), + SslError::ZeroReturn => create_ssl_zero_return_error(vm), + SslError::Eof => create_ssl_eof_error(vm), + SslError::CertVerification(cert_err) => { + // Use the proper cert verification error creator + create_ssl_cert_verification_error(vm, &cert_err).expect("unlikely to happen") + } + SslError::Io(err) => vm.new_os_error(format!("I/O error: {err}")), + SslError::SniCallbackRestart => { + // This should be handled at PySSLSocket level + unreachable!("SniCallbackRestart should not reach Python layer") + } + SslError::Py(exc) => exc, + SslError::AlertReceived { lib, reason } => { + Self::create_ssl_error_from_codes(vm, lib, reason) + } + SslError::NoCipherSuites => { + // OpenSSL error: lib=20 (ERR_LIB_SSL), reason=193 (SSL_R_NO_SHARED_CIPHER) + Self::create_ssl_error_from_codes(vm, ERR_LIB_SSL, SSL_R_NO_SHARED_CIPHER) + } + } + } +} + +pub type SslResult = Result; +/// Common protocol settings shared between client and server connections +#[derive(Debug)] +pub struct ProtocolSettings { + pub versions: &'static [&'static rustls::SupportedProtocolVersion], + pub kx_groups: Option>, + pub cipher_suites: Option>, + pub alpn_protocols: Vec>, +} + +/// Options for creating a server TLS configuration +#[derive(Debug)] +pub struct ServerConfigOptions { + /// Common protocol settings (versions, ALPN, KX groups, cipher suites) + pub protocol_settings: ProtocolSettings, + /// Server certificate chain + pub cert_chain: Vec>, + /// Server private key + pub private_key: PrivateKeyDer<'static>, + /// Root certificates for client verification (if required) + pub root_store: Option, + /// Whether to request client certificate + pub request_client_cert: bool, + /// Whether to use deferred client certificate validation (TLS 1.3) + pub use_deferred_validation: bool, + /// Custom certificate resolver (for SNI support) + pub cert_resolver: Option>, + /// Deferred certificate error storage (for TLS 1.3) + pub deferred_cert_error: Option>>>, + /// Session storage for server-side session resumption + pub session_storage: Option>, + /// Shared ticketer for TLS 1.2 session tickets (stateless resumption) + pub ticketer: Option>, +} + +/// Options for creating a client TLS configuration +#[derive(Debug)] +pub struct ClientConfigOptions { + /// Common protocol settings (versions, ALPN, KX groups, cipher suites) + pub protocol_settings: ProtocolSettings, + /// Root certificates for server verification + pub root_store: Option, + /// DER-encoded CA certificates (for partial chain verification) + pub ca_certs_der: Vec>, + /// Client certificate chain (for mTLS) + pub cert_chain: Option>>, + /// Client private key (for mTLS) + pub private_key: Option>, + /// Whether to verify server certificates (CERT_NONE disables verification) + pub verify_server_cert: bool, + /// Whether to check hostname against certificate (check_hostname) + pub check_hostname: bool, + /// SSL verification flags (e.g., VERIFY_X509_STRICT) + pub verify_flags: i32, + /// Session store for client-side session resumption + pub session_store: Option>, + /// Certificate Revocation Lists for CRL checking + pub crls: Vec>, +} + +/// Create custom CryptoProvider with specified cipher suites and key exchange groups +/// +/// This helper function consolidates the duplicated CryptoProvider creation logic +/// for both server and client configurations. +fn create_custom_crypto_provider( + cipher_suites: Option>, + kx_groups: Option>, +) -> Arc { + use rustls::crypto::aws_lc_rs::{ALL_CIPHER_SUITES, ALL_KX_GROUPS}; + let default_provider = rustls::crypto::aws_lc_rs::default_provider(); + + Arc::new(rustls::crypto::CryptoProvider { + cipher_suites: cipher_suites.unwrap_or_else(|| ALL_CIPHER_SUITES.to_vec()), + kx_groups: kx_groups.unwrap_or_else(|| ALL_KX_GROUPS.to_vec()), + signature_verification_algorithms: default_provider.signature_verification_algorithms, + secure_random: default_provider.secure_random, + key_provider: default_provider.key_provider, + }) +} + +/// Create a server TLS configuration +/// +/// This abstracts the complex rustls ServerConfig building logic, +/// matching SSL_CTX initialization for server sockets. +pub(super) fn create_server_config(options: ServerConfigOptions) -> Result { + use rustls::server::WebPkiClientVerifier; + + // Ensure default CryptoProvider is installed + ensure_default_provider(); + + // Create custom crypto provider using helper function + let custom_provider = create_custom_crypto_provider( + options.protocol_settings.cipher_suites.clone(), + options.protocol_settings.kx_groups.clone(), + ); + + // Step 1: Build the appropriate client cert verifier based on settings + let client_cert_verifier: Option> = + if let Some(root_store) = options.root_store { + if options.request_client_cert { + // Client certificate verification required + let base_verifier = WebPkiClientVerifier::builder(Arc::new(root_store)) + .build() + .map_err(|e| format!("Failed to create client verifier: {e}"))?; + + if options.use_deferred_validation { + // TLS 1.3: Use deferred validation + if let Some(deferred_error) = options.deferred_cert_error { + use crate::ssl::cert::DeferredClientCertVerifier; + let deferred_verifier = + DeferredClientCertVerifier::new(base_verifier, deferred_error); + Some(Arc::new(deferred_verifier)) + } else { + // No deferred error storage provided, use immediate validation + Some(base_verifier) + } + } else { + // TLS 1.2 or non-deferred: Use immediate validation + Some(base_verifier) + } + } else { + // No client authentication + None + } + } else { + // No root store - no client authentication + None + }; + + // Step 2: Create ServerConfig builder once with the selected verifier + let builder = ServerConfig::builder_with_provider(custom_provider.clone()) + .with_protocol_versions(options.protocol_settings.versions) + .map_err(|e| format!("Failed to create server config builder: {e}"))?; + + let builder = if let Some(verifier) = client_cert_verifier { + builder.with_client_cert_verifier(verifier) + } else { + builder.with_no_client_auth() + }; + + // Add certificate + let mut config = if let Some(resolver) = options.cert_resolver { + // Use custom cert resolver (e.g., for SNI) + builder.with_cert_resolver(resolver) + } else { + // Use single certificate + builder + .with_single_cert(options.cert_chain, options.private_key) + .map_err(|e| format!("Failed to set server certificate: {e}"))? + }; + + // Set ALPN protocols with fallback + apply_alpn_with_fallback( + &mut config.alpn_protocols, + &options.protocol_settings.alpn_protocols, + ); + + // Set session storage for server-side session resumption (TLS 1.3) + if let Some(session_storage) = options.session_storage { + config.session_storage = session_storage; + } + + // Set ticketer for TLS 1.2 session tickets (stateless resumption) + if let Some(ticketer) = options.ticketer { + config.ticketer = ticketer.clone(); + } + + Ok(config) +} + +/// Build WebPki verifier with CRL support +/// +/// This helper function consolidates the duplicated CRL setup logic for both +/// check_hostname=True and check_hostname=False cases. +fn build_webpki_verifier_with_crls( + root_store: Arc, + crls: Vec>, + verify_flags: i32, +) -> Result, String> { + use rustls::client::WebPkiServerVerifier; + + let mut verifier_builder = WebPkiServerVerifier::builder(root_store); + + // Check if CRL verification is requested + let crl_check_requested = verify_flags & X509_V_FLAG_CRL_CHECK != 0; + let has_crls = !crls.is_empty(); + + // Add CRLs if provided OR if CRL checking is explicitly requested + // (even with empty CRLs, rustls will fail verification if CRL checking is enabled) + if has_crls || crl_check_requested { + verifier_builder = verifier_builder.with_crls(crls); + + // Check if we should only verify end-entity (leaf) certificates + if verify_flags & X509_V_FLAG_CRL_CHECK != 0 { + verifier_builder = verifier_builder.only_check_end_entity_revocation(); + } + } + + let webpki_verifier = verifier_builder + .build() + .map_err(|e| format!("Failed to build WebPkiServerVerifier: {e}"))?; + + Ok(webpki_verifier as Arc) +} + +/// Apply verifier wrappers (CRLCheckVerifier and StrictCertVerifier) +/// +/// This helper function consolidates the duplicated verifier wrapping logic. +fn apply_verifier_wrappers( + verifier: Arc, + verify_flags: i32, + has_crls: bool, + ca_certs_der: Vec>, +) -> Arc { + let crl_check_requested = verify_flags & X509_V_FLAG_CRL_CHECK != 0; + + // Wrap with CRLCheckVerifier to enforce CRL checking when flags are set + let verifier = if crl_check_requested { + use crate::ssl::cert::CRLCheckVerifier; + Arc::new(CRLCheckVerifier::new( + verifier, + has_crls, + crl_check_requested, + )) + } else { + verifier + }; + + // Always use PartialChainVerifier when trust store is not empty + // This allows self-signed certificates in trust store to be trusted + // (OpenSSL behavior: self-signed certs are always trusted, non-self-signed require flag) + let verifier = if !ca_certs_der.is_empty() { + use crate::ssl::cert::PartialChainVerifier; + Arc::new(PartialChainVerifier::new( + verifier, + ca_certs_der, + verify_flags, + )) + } else { + verifier + }; + + // Wrap with StrictCertVerifier if VERIFY_X509_STRICT flag is set + if verify_flags & VERIFY_X509_STRICT != 0 { + Arc::new(super::cert::StrictCertVerifier::new(verifier, verify_flags)) + } else { + verifier + } +} + +/// Apply ALPN protocols +/// +/// OpenSSL 1.1.0f+ allows ALPN negotiation to fail without aborting handshake. +/// rustls follows RFC 7301 strictly and rejects connections with no matching protocol. +/// To emulate OpenSSL behavior, we add a special fallback protocol (null byte). +fn apply_alpn_with_fallback(config_alpn: &mut Vec>, alpn_protocols: &[Vec]) { + if !alpn_protocols.is_empty() { + *config_alpn = alpn_protocols.to_vec(); + config_alpn.push(vec![0u8]); // Add null byte as fallback marker + } +} + +/// Create a client TLS configuration +/// +/// This abstracts the complex rustls ClientConfig building logic, +/// matching SSL_CTX initialization for client sockets. +pub(super) fn create_client_config(options: ClientConfigOptions) -> Result { + // Ensure default CryptoProvider is installed + ensure_default_provider(); + + // Create custom crypto provider using helper function + let custom_provider = create_custom_crypto_provider( + options.protocol_settings.cipher_suites.clone(), + options.protocol_settings.kx_groups.clone(), + ); + + // Step 1: Build the appropriate verifier based on verification settings + let verifier: Arc = if options + .verify_server_cert + { + // Verify server certificates + let root_store = options + .root_store + .ok_or("Root store required for server verification")?; + + let root_store_arc = Arc::new(root_store); + + // Check if root_store is empty (no CA certs loaded) + // CPython allows this and fails during handshake with SSLCertVerificationError + if root_store_arc.is_empty() { + // Use EmptyRootStoreVerifier - always fails with UnknownIssuer during handshake + use crate::ssl::cert::EmptyRootStoreVerifier; + Arc::new(EmptyRootStoreVerifier) + } else { + // Calculate has_crls once for both hostname verification paths + let has_crls = !options.crls.is_empty(); + + if options.check_hostname { + // Default behavior: verify both certificate chain and hostname + let base_verifier = build_webpki_verifier_with_crls( + root_store_arc.clone(), + options.crls, + options.verify_flags, + )?; + + // Apply CRL and Strict verifier wrappers using helper function + apply_verifier_wrappers( + base_verifier, + options.verify_flags, + has_crls, + options.ca_certs_der.clone(), + ) + } else { + // check_hostname=False: verify certificate chain but ignore hostname + use crate::ssl::cert::HostnameIgnoringVerifier; + + // Build verifier with CRL support using helper function + let webpki_verifier = build_webpki_verifier_with_crls( + root_store_arc.clone(), + options.crls, + options.verify_flags, + )?; + + // Apply CRL verifier wrapper if needed (without Strict wrapper yet) + let crl_check_requested = options.verify_flags & X509_V_FLAG_CRL_CHECK != 0; + let verifier = if crl_check_requested { + use crate::ssl::cert::CRLCheckVerifier; + Arc::new(CRLCheckVerifier::new( + webpki_verifier, + has_crls, + crl_check_requested, + )) as Arc + } else { + webpki_verifier + }; + + // Wrap with PartialChainVerifier if VERIFY_X509_PARTIAL_CHAIN is set + const VERIFY_X509_PARTIAL_CHAIN: i32 = 0x80000; + let verifier = if options.verify_flags & VERIFY_X509_PARTIAL_CHAIN != 0 { + use crate::ssl::cert::PartialChainVerifier; + Arc::new(PartialChainVerifier::new( + verifier, + options.ca_certs_der.clone(), + options.verify_flags, + )) as Arc + } else { + verifier + }; + + // Wrap with HostnameIgnoringVerifier to bypass hostname checking + let hostname_ignoring_verifier: Arc< + dyn rustls::client::danger::ServerCertVerifier, + > = Arc::new(HostnameIgnoringVerifier::new_with_verifier(verifier)); + + // Apply Strict verifier wrapper once at the end if needed + if options.verify_flags & VERIFY_X509_STRICT != 0 { + Arc::new(crate::ssl::cert::StrictCertVerifier::new( + hostname_ignoring_verifier, + options.verify_flags, + )) + } else { + hostname_ignoring_verifier + } + } + } + } else { + // CERT_NONE: disable all verification + use crate::ssl::cert::NoVerifier; + Arc::new(NoVerifier) + }; + + // Step 2: Create ClientConfig builder once with the selected verifier + let builder = ClientConfig::builder_with_provider(custom_provider.clone()) + .with_protocol_versions(options.protocol_settings.versions) + .map_err(|e| format!("Failed to create client config builder: {e}"))? + .dangerous() + .with_custom_certificate_verifier(verifier); + + // Add client certificate if provided (mTLS) + let mut config = + if let (Some(cert_chain), Some(private_key)) = (options.cert_chain, options.private_key) { + builder + .with_client_auth_cert(cert_chain, private_key) + .map_err(|e| format!("Failed to set client certificate: {e}"))? + } else { + builder.with_no_client_auth() + }; + + // Set ALPN protocols + apply_alpn_with_fallback( + &mut config.alpn_protocols, + &options.protocol_settings.alpn_protocols, + ); + + // Set session resumption + if let Some(session_store) = options.session_store { + use rustls::client::Resumption; + config.resumption = Resumption::store(session_store); + } + + Ok(config) +} + +/// Helper function - check if error is BlockingIOError +pub(super) fn is_blocking_io_error(err: &PyBaseExceptionRef, vm: &VirtualMachine) -> bool { + err.fast_isinstance(vm.ctx.exceptions.blocking_io_error) +} + +// Handshake Helper Functions + +/// Write TLS handshake data to socket/BIO +/// +/// Drains all pending TLS data from rustls and sends it to the peer. +/// Returns whether any progress was made. +fn handshake_write_loop( + conn: &mut TlsConnection, + socket: &PySSLSocket, + force_initial_write: bool, + vm: &VirtualMachine, +) -> SslResult { + let mut made_progress = false; + + while conn.wants_write() || force_initial_write { + if force_initial_write && !conn.wants_write() { + // No data to write on first iteration - break to avoid infinite loop + break; + } + + let mut buf = Vec::new(); + let written = conn + .write_tls(&mut buf as &mut dyn std::io::Write) + .map_err(SslError::Io)?; + + if written > 0 && !buf.is_empty() { + // Send directly without select - blocking sockets will wait automatically + // Handle BlockingIOError from non-blocking sockets + match socket.sock_send(buf, vm) { + Ok(_) => { + made_progress = true; + } + Err(e) => { + if is_blocking_io_error(&e, vm) { + // Non-blocking socket would block - return SSLWantWriteError + return Err(SslError::WantWrite); + } + return Err(SslError::Py(e)); + } + } + } else if written == 0 { + // No data written but wants_write is true - should not happen normally + // Break to avoid infinite loop + break; + } + + // Check if there's more to write + if !conn.wants_write() { + break; + } + } + + Ok(made_progress) +} + +/// Read TLS handshake data from socket/BIO +/// +/// Waits for and reads TLS records from the peer, handling SNI callback setup. +/// Returns (made_progress, is_first_sni_read). +fn handshake_read_data( + conn: &mut TlsConnection, + socket: &PySSLSocket, + is_bio: bool, + is_server: bool, + vm: &VirtualMachine, +) -> SslResult<(bool, bool)> { + if !conn.wants_read() { + return Ok((false, false)); + } + + // SERVER-SPECIFIC: Check if this is the first read (for SNI callback) + // Must check BEFORE reading data, so we can detect first time + let is_first_sni_read = is_server && socket.is_first_sni_read(); + + // Wait for data in socket mode + if !is_bio { + let timed_out = socket + .sock_wait_for_io_impl(SelectKind::Read, vm) + .map_err(SslError::Py)?; + + if timed_out { + // This should rarely happen now - only if socket itself has a timeout + // and we're waiting for required handshake data + return Err(SslError::Timeout("timed out".to_string())); + } + } + + let data_obj = match socket.sock_recv(SSL3_RT_MAX_PLAIN_LENGTH, vm) { + Ok(d) => d, + Err(e) => { + if is_blocking_io_error(&e, vm) { + return Err(SslError::WantRead); + } + // In socket mode, if recv times out and we're only waiting for read + // (no wants_write), we might be waiting for optional NewSessionTicket in TLS 1.3 + // Consider the handshake complete + if !is_bio && !conn.wants_write() { + // Check if it's a timeout exception + if e.fast_isinstance(vm.ctx.exceptions.timeout_error) { + // Timeout waiting for optional data - handshake is complete + return Ok((false, false)); + } + } + return Err(SslError::Py(e)); + } + }; + + // SERVER-SPECIFIC: Save ClientHello on first read for potential connection recreation + if is_first_sni_read { + // Extract bytes from PyObjectRef + use rustpython_vm::builtins::PyBytes; + if let Some(bytes_obj) = data_obj.downcast_ref::() { + socket.save_client_hello_from_bytes(bytes_obj.as_bytes()); + } + } + + // Feed data to rustls + ssl_read_tls_records(conn, data_obj, is_bio, vm)?; + + Ok((true, is_first_sni_read)) +} + +/// Handle handshake completion for server-side TLS 1.3 +/// +/// Tries to send NewSessionTicket in non-blocking mode to avoid deadlocks. +/// Returns true if handshake is complete and we should exit. +fn handle_handshake_complete( + conn: &mut TlsConnection, + socket: &PySSLSocket, + _is_server: bool, + vm: &VirtualMachine, +) -> SslResult { + if conn.is_handshaking() { + return Ok(false); // Not complete yet + } + + // Handshake is complete! + // + // Different behavior for BIO mode vs socket mode: + // + // BIO mode (CPython-compatible): + // - Python code calls outgoing.read() to get pending data + // - We just return here and let Python handle the data + // + // Socket mode (rustls-specific): + // - OpenSSL automatically writes to socket in SSL_do_handshake() + // - We must explicitly call write_tls() to send pending data + // - Without this, client hangs waiting for server's NewSessionTicket + + if socket.is_bio_mode() { + // BIO mode: Write pending data to outgoing BIO (one-time drain) + // Python's ssl_io_loop will read from outgoing BIO + if conn.wants_write() { + // Call write_tls ONCE to drain pending data + // Do NOT loop on wants_write() - avoid infinite loop/deadlock + let tls_data = ssl_write_tls_records(conn)?; + if !tls_data.is_empty() { + socket.sock_send(tls_data, vm).map_err(SslError::Py)?; + } + + // IMPORTANT: Don't check wants_write() again! + // Python's ssl_io_loop will call do_handshake() again if needed + } + } else if conn.wants_write() { + // Send all pending data (e.g., TLS 1.3 NewSessionTicket) to socket + while conn.wants_write() { + let tls_data = ssl_write_tls_records(conn)?; + if tls_data.is_empty() { + break; + } + socket.sock_send(tls_data, vm).map_err(SslError::Py)?; + } + } + + Ok(true) +} + +/// Try to read plaintext data from TLS connection buffer +/// +/// Returns Ok(Some(n)) if n bytes were read, Ok(None) if would block, +/// or Err on real errors. +fn try_read_plaintext(conn: &mut TlsConnection, buf: &mut [u8]) -> SslResult> { + let mut reader = conn.reader(); + match reader.read(buf) { + Ok(0) => { + // EOF from TLS connection + Ok(Some(0)) + } + Ok(n) => { + // Successfully read n bytes + Ok(Some(n)) + } + Err(e) if e.kind() != std::io::ErrorKind::WouldBlock => { + // Real error + Err(SslError::Io(e)) + } + Err(_) => { + // WouldBlock - no plaintext available + Ok(None) + } + } +} + +/// Equivalent to OpenSSL's SSL_do_handshake() +/// +/// Performs TLS handshake by exchanging data with the peer until completion. +/// This abstracts away the low-level rustls read_tls/write_tls loop. +/// +/// = SSL_do_handshake() +pub(super) fn ssl_do_handshake( + conn: &mut TlsConnection, + socket: &PySSLSocket, + vm: &VirtualMachine, +) -> SslResult<()> { + // Check if handshake is already done + if !conn.is_handshaking() { + return Ok(()); + } + + let is_bio = socket.is_bio_mode(); + let is_server = matches!(conn, TlsConnection::Server(_)); + let mut first_iteration = true; // Track if this is the first loop iteration + let mut iteration_count = 0; + + loop { + iteration_count += 1; + let mut made_progress = false; + + // IMPORTANT: In BIO mode, force initial write even if wants_write() is false + // rustls requires write_tls() to be called to generate ClientHello/ServerHello + let force_initial_write = is_bio && first_iteration; + + // Write TLS handshake data to socket/BIO + let write_progress = handshake_write_loop(conn, socket, force_initial_write, vm)?; + made_progress |= write_progress; + + // Read TLS handshake data from socket/BIO + let (read_progress, is_first_sni_read) = + handshake_read_data(conn, socket, is_bio, is_server, vm)?; + made_progress |= read_progress; + + // Process TLS packets (state machine) + if let Err(e) = conn.process_new_packets() { + // Send close_notify on error + if !is_bio { + conn.send_close_notify(); + // Actually send the close_notify alert + if let Ok(alert_data) = ssl_write_tls_records(conn) + && !alert_data.is_empty() + { + let _ = socket.sock_send(alert_data, vm); + } + } + + // Certificate verification errors are already handled by from_rustls + + return Err(SslError::from_rustls(e)); + } + + // SERVER-SPECIFIC: Check SNI callback after processing packets + // SNI name is extracted during process_new_packets() + // Invoke callback on FIRST read if callback is configured, regardless of SNI presence + if is_server && is_first_sni_read && socket.has_sni_callback() { + // IMPORTANT: Do NOT call the callback here! + // The connection lock is still held, which would cause deadlock. + // Return SniCallbackRestart to signal do_handshake to: + // 1. Drop conn_guard + // 2. Call the callback (with Some(name) or None) + // 3. Restart handshake + return Err(SslError::SniCallbackRestart); + } + + // Check if handshake is complete and handle post-handshake processing + // CRITICAL: We do NOT check wants_read() - this matches CPython/OpenSSL behavior! + if handle_handshake_complete(conn, socket, is_server, vm)? { + return Ok(()); + } + + // In BIO mode, stop after one iteration + if is_bio { + // Before returning WANT error, write any pending TLS data to BIO + // This is critical: if wants_write is true after process_new_packets, + // we need to write that data to the outgoing BIO before returning + if conn.wants_write() { + // Write all pending TLS data to outgoing BIO + loop { + let mut buf = vec![0u8; SSL3_RT_MAX_PLAIN_LENGTH]; + let n = match conn.write_tls(&mut buf.as_mut_slice()) { + Ok(n) => n, + Err(_) => break, + }; + if n == 0 { + break; + } + // Send to outgoing BIO + socket + .sock_send(buf[..n].to_vec(), vm) + .map_err(SslError::Py)?; + // Check if there's more to write + if !conn.wants_write() { + break; + } + } + // After writing, check if we still want more + // If all data was written, wants_write may now be false + if conn.wants_write() { + // Still need more - return WANT_WRITE + return Err(SslError::WantWrite); + } + // Otherwise fall through to check wants_read + } + + // Check if we need to read + if conn.wants_read() { + return Err(SslError::WantRead); + } + break; + } + + // Mark that we've completed the first iteration + first_iteration = false; + + // Improved loop termination logic: + // Continue looping if: + // 1. Rustls wants more I/O (wants_read or wants_write), OR + // 2. We made progress in this iteration + // + // This is more robust than just checking made_progress, because: + // - Rustls may need multiple iterations to process TLS state machine + // - Network delays may cause temporary "no progress" situations + // - wants_read/wants_write accurately reflect Rustls internal state + let should_continue = conn.wants_read() || conn.wants_write() || made_progress; + + if !should_continue { + break; + } + + // Safety check: prevent truly infinite loops (should never happen) + if iteration_count > 1000 { + break; + } + } + + // If we exit the loop without completing handshake, return error + // Check rustls state to provide better error message + if conn.is_handshaking() { + Err(SslError::Syscall(format!( + "SSL handshake failed: incomplete after {iteration_count} iterations", + ))) + } else { + // Handshake completed successfully (shouldn't reach here normally) + Ok(()) + } +} + +/// Equivalent to OpenSSL's SSL_read() +/// +/// Reads application data from TLS connection. +/// Automatically handles TLS record I/O as needed. +/// +/// = SSL_read_ex() +pub(super) fn ssl_read( + conn: &mut TlsConnection, + buf: &mut [u8], + socket: &PySSLSocket, + vm: &VirtualMachine, +) -> SslResult { + let is_bio = socket.is_bio_mode(); + + // Get socket timeout and calculate deadline (= _PyDeadline_Init) + let deadline = if !is_bio { + match socket.get_socket_timeout(vm).map_err(SslError::Py)? { + Some(timeout) if !timeout.is_zero() => Some(std::time::Instant::now() + timeout), + _ => None, // None = blocking (no deadline), Some(0) = non-blocking (handled below) + } + } else { + None // BIO mode has no deadline + }; + + // Loop to handle TLS records and post-handshake messages + // Matches SSL_read behavior which loops until data is available + // - CPython uses OpenSSL's SSL_read which loops on SSL_ERROR_WANT_READ/WANT_WRITE + // - We use rustls which requires manual read_tls/process_new_packets loop + // - No iteration limit: relies on deadline and blocking I/O + // - Blocking sockets: sock_select() and recv() wait at kernel level (no CPU busy-wait) + // - Non-blocking sockets: immediate return on first WantRead + // - Deadline prevents timeout issues + loop { + // Check deadline + if let Some(deadline) = deadline + && std::time::Instant::now() >= deadline + { + // Timeout expired + return Err(SslError::Timeout( + "The read operation timed out".to_string(), + )); + } + // Check if we need to read more TLS records BEFORE trying plaintext read + // This ensures we don't miss data that's already been processed + let needs_more_tls = conn.wants_read(); + + // Try to read plaintext from rustls buffer + if let Some(n) = try_read_plaintext(conn, buf)? { + return Ok(n); + } + + // No plaintext available and cannot read more TLS records + if !needs_more_tls { + if is_bio && let Some(bio_obj) = socket.incoming_bio() { + let is_eof = bio_obj + .get_attr("eof", vm) + .and_then(|v| v.try_into_value::(vm)) + .unwrap_or(false); + if is_eof { + return Err(SslError::Eof); + } + } + return Err(SslError::WantRead); + } + + // Read and process TLS records + // This will block for blocking sockets + match ssl_ensure_data_available(conn, socket, vm) { + Ok(_bytes_read) => { + // Successfully read and processed TLS data + // Continue loop to try reading plaintext + } + Err(SslError::Io(ref io_err)) if io_err.to_string().contains("message buffer full") => { + // Buffer is full - we need to consume plaintext before reading more + // Try to read plaintext now + match try_read_plaintext(conn, buf)? { + Some(n) if n > 0 => { + // Have plaintext - return it + // Python will call read() again if it needs more data + return Ok(n); + } + _ => { + // No plaintext available yet - this is unusual + // Return WantRead to let Python retry + return Err(SslError::WantRead); + } + } + } + Err(e) => { + // Other errors - check for buffered plaintext before propagating + match try_read_plaintext(conn, buf)? { + Some(n) if n > 0 => { + // Have buffered plaintext - return it successfully + return Ok(n); + } + _ => { + // No buffered data - propagate the error + return Err(e); + } + } + } + } + } +} + +// Helper functions (private-ish, used by public SSL functions) + +/// Write TLS records from rustls to socket +fn ssl_write_tls_records(conn: &mut TlsConnection) -> SslResult> { + let mut buf = Vec::new(); + let n = conn + .write_tls(&mut buf as &mut dyn std::io::Write) + .map_err(SslError::Io)?; + + if n > 0 { Ok(buf) } else { Ok(Vec::new()) } +} + +/// Read TLS records from socket to rustls +fn ssl_read_tls_records( + conn: &mut TlsConnection, + data: PyObjectRef, + is_bio: bool, + vm: &VirtualMachine, +) -> SslResult<()> { + // Convert PyObject to bytes-like (supports bytes, bytearray, etc.) + let bytes = ArgBytesLike::try_from_object(vm, data) + .map_err(|_| SslError::Syscall("Expected bytes-like object".to_string()))?; + + let bytes_data = bytes.borrow_buf(); + + if bytes_data.is_empty() { + // different error for BIO vs socket mode + if is_bio { + // In BIO mode, no data means WANT_READ + return Err(SslError::WantRead); + } else { + // In socket mode, empty recv() means TCP EOF (FIN received) + // Need to distinguish: + // 1. Clean shutdown: received TLS close_notify → return ZeroReturn (0 bytes) + // 2. Unexpected EOF: no close_notify → return Eof (SSLEOFError) + // + // SSL_ERROR_ZERO_RETURN vs SSL_ERROR_SYSCALL(errno=0) logic + // CPython checks SSL_get_shutdown() & SSL_RECEIVED_SHUTDOWN + // + // Process any buffered TLS records (may contain close_notify) + let _ = conn.process_new_packets(); + + // IMPORTANT: CPython's default behavior (suppress_ragged_eofs=True) + // treats empty recv() as clean shutdown, returning 0 bytes instead of raising SSLEOFError. + // + // This is necessary for HTTP/1.0 servers that: + // 1. Send response without Content-Length header + // 2. Signal end-of-response by closing connection (TCP FIN) + // 3. Don't send TLS close_notify before TCP close + // + // While this could theoretically allow truncation attacks, + // it's the standard behavior for compatibility with real-world servers. + // Python only raises SSLEOFError when suppress_ragged_eofs=False is explicitly set. + // + // TODO: Implement suppress_ragged_eofs parameter if needed for strict security mode. + return Err(SslError::ZeroReturn); + } + } + + // Feed all received data to read_tls - loop to consume all data + // read_tls may not consume all data in one call + let mut offset = 0; + while offset < bytes_data.len() { + let remaining = &bytes_data[offset..]; + let mut cursor = std::io::Cursor::new(remaining); + + match conn.read_tls(&mut cursor) { + Ok(read_bytes) => { + if read_bytes == 0 { + // No more data can be consumed + break; + } + offset += read_bytes; + } + Err(e) => { + // Real error - propagate it + return Err(SslError::Io(e)); + } + } + } + + Ok(()) +} + +/// Ensure TLS data is available for reading +/// Returns the number of bytes read from the socket +fn ssl_ensure_data_available( + conn: &mut TlsConnection, + socket: &PySSLSocket, + vm: &VirtualMachine, +) -> SslResult { + // Unlike OpenSSL's SSL_read, rustls requires explicit I/O + if conn.wants_read() { + let is_bio = socket.is_bio_mode(); + + // For non-BIO mode (regular sockets), check if socket is ready first + // PERFORMANCE OPTIMIZATION: Only use select for sockets with timeout + // - Blocking sockets (timeout=None): Skip select, recv() will block naturally + // - Timeout sockets: Use select to enforce timeout + // - Non-blocking sockets: Skip select, recv() will return EAGAIN immediately + if !is_bio { + let timeout = socket.get_socket_timeout(vm).map_err(SslError::Py)?; + + // Only use select if socket has a positive timeout + if let Some(t) = timeout + && !t.is_zero() + { + // Socket has timeout - use select to enforce it + let timed_out = socket + .sock_wait_for_io_impl(SelectKind::Read, vm) + .map_err(SslError::Py)?; + if timed_out { + // Socket not ready within timeout + return Err(SslError::WantRead); + } + } + // else: non-blocking socket (timeout=0) or blocking socket (timeout=None) - skip select + } + + let data = socket.sock_recv(2048, vm).map_err(SslError::Py)?; + + // Get the size of received data + let bytes_read = data + .clone() + .try_into_value::(vm) + .map(|b| b.as_bytes().len()) + .unwrap_or(0); + + // Check if BIO has EOF set (incoming BIO closed) + let is_eof = if is_bio { + // Check incoming BIO's eof property + if let Some(bio_obj) = socket.incoming_bio() { + bio_obj + .get_attr("eof", vm) + .and_then(|v| v.try_into_value::(vm)) + .unwrap_or(false) + } else { + false + } + } else { + false + }; + + // If BIO EOF is set and no data available, treat as connection EOF + if is_eof && bytes_read == 0 { + return Err(SslError::Eof); + } + + // Feed data to rustls and process packets + ssl_read_tls_records(conn, data, is_bio, vm)?; + + // Process any packets we successfully read + conn.process_new_packets().map_err(SslError::from_rustls)?; + + Ok(bytes_read) + } else { + // No data to read + Ok(0) + } +} + +// Multi-Certificate Resolver for RSA/ECC Support + +/// Multi-certificate resolver that selects appropriate certificate based on client capabilities +/// +/// This resolver implements OpenSSL's behavior of supporting multiple certificate/key pairs +/// (e.g., one RSA and one ECC) and selecting the appropriate one based on the client's +/// supported signature algorithms during the TLS handshake. +/// +/// OpenSSL's SSL_CTX_use_certificate_chain_file can be called multiple +/// times to add different certificate types, and OpenSSL automatically selects the best one. +#[derive(Debug)] +pub(super) struct MultiCertResolver { + cert_keys: Vec>, +} + +impl MultiCertResolver { + /// Create a new multi-certificate resolver + pub fn new(cert_keys: Vec>) -> Self { + Self { cert_keys } + } +} + +impl ResolvesServerCert for MultiCertResolver { + fn resolve(&self, client_hello: rustls::server::ClientHello<'_>) -> Option> { + // Get the signature schemes supported by the client + let client_schemes = client_hello.signature_schemes(); + + // Try to find a certificate that matches the client's signature schemes + for cert_key in &self.cert_keys { + // Check if this certificate's signing key is compatible with any of the + // client's supported signature schemes + if let Some(_scheme) = cert_key.key.choose_scheme(client_schemes) { + return Some(cert_key.clone()); + } + } + + // If no perfect match, return the first certificate as fallback + // (This matches OpenSSL's behavior of using the first loaded cert if negotiation fails) + self.cert_keys.first().cloned() + } +} + +// Helper Functions for OpenSSL Compatibility: + +/// Normalize cipher suite name for OpenSSL compatibility +/// +/// Converts rustls cipher names to OpenSSL format: +/// - TLS_AES_256_GCM_SHA384 → AES256-GCM-SHA384 +/// - Replaces "AES-256" with "AES256" and "AES-128" with "AES128" +pub(super) fn normalize_cipher_name(rustls_name: &str) -> String { + rustls_name + .strip_prefix("TLS_") + .unwrap_or(rustls_name) + .replace("_WITH_", "_") + .replace('_', "-") + .replace("AES-256", "AES256") + .replace("AES-128", "AES128") +} + +/// Get cipher key size in bits from cipher suite name +/// +/// Returns: +/// - 256 for AES-256 and ChaCha20 +/// - 128 for AES-128 +/// - 0 for unknown ciphers +pub(super) fn get_cipher_key_bits(cipher_name: &str) -> i32 { + if cipher_name.contains("256") || cipher_name.contains("CHACHA20") { + 256 + } else if cipher_name.contains("128") { + 128 + } else { + 0 + } +} + +/// Get encryption algorithm description from cipher name +/// +/// Returns human-readable encryption description for OpenSSL compatibility +pub(super) fn get_cipher_encryption_desc(cipher_name: &str) -> &'static str { + if cipher_name.contains("AES256") { + "AESGCM(256)" + } else if cipher_name.contains("AES128") { + "AESGCM(128)" + } else if cipher_name.contains("CHACHA20") { + "CHACHA20-POLY1305(256)" + } else { + "Unknown" + } +} + +/// Normalize rustls cipher suite name to IANA standard format +/// +/// Converts rustls Debug format names to IANA standard: +/// - "TLS13_AES_256_GCM_SHA384" -> "TLS_AES_256_GCM_SHA384" +/// - Other names remain unchanged +pub(super) fn normalize_rustls_cipher_name(rustls_name: &str) -> String { + if rustls_name.starts_with("TLS13_") { + rustls_name.replace("TLS13_", "TLS_") + } else { + rustls_name.to_string() + } +} + +/// Convert rustls protocol version to string representation +/// +/// Returns the TLS version string +/// - TLSv1.2, TLSv1.3, or "Unknown" +pub(super) fn get_protocol_version_str(version: &rustls::SupportedProtocolVersion) -> &'static str { + match version.version { + rustls::ProtocolVersion::TLSv1_2 => "TLSv1.2", + rustls::ProtocolVersion::TLSv1_3 => "TLSv1.3", + _ => "Unknown", + } +} + +/// Cipher suite information +/// +/// Contains all relevant cipher information extracted from a rustls CipherSuite +pub(super) struct CipherInfo { + /// IANA standard cipher name (e.g., "TLS_AES_256_GCM_SHA384") + pub name: String, + /// TLS protocol version (e.g., "TLSv1.2", "TLSv1.3") + pub protocol: &'static str, + /// Key size in bits (e.g., 128, 256) + pub bits: i32, +} + +/// Extract cipher information from a rustls CipherSuite +/// +/// This consolidates the common cipher extraction logic used across +/// get_ciphers(), cipher(), and shared_ciphers() methods. +pub(super) fn extract_cipher_info(suite: &rustls::SupportedCipherSuite) -> CipherInfo { + let rustls_name = format!("{:?}", suite.suite()); + let name = normalize_rustls_cipher_name(&rustls_name); + let protocol = get_protocol_version_str(suite.version()); + let bits = get_cipher_key_bits(&name); + + CipherInfo { + name, + protocol, + bits, + } +} + +/// Convert curve name to rustls key exchange group +/// +/// Maps OpenSSL curve names (e.g., "prime256v1", "secp384r1") to rustls KxGroups. +/// Returns an error if the curve is not supported by rustls. +pub(super) fn curve_name_to_kx_group( + curve: &str, +) -> Result, String> { + // Get the default crypto provider's key exchange groups + let provider = rustls::crypto::aws_lc_rs::default_provider(); + let all_groups = &provider.kx_groups; + + match curve { + // P-256 (also known as secp256r1 or prime256v1) + "prime256v1" | "secp256r1" => { + // Find SECP256R1 in the provider's groups + all_groups + .iter() + .find(|g| g.name() == rustls::NamedGroup::secp256r1) + .map(|g| vec![*g]) + .ok_or_else(|| "secp256r1 not supported by crypto provider".to_owned()) + } + // P-384 (also known as secp384r1 or prime384v1) + "secp384r1" | "prime384v1" => all_groups + .iter() + .find(|g| g.name() == rustls::NamedGroup::secp384r1) + .map(|g| vec![*g]) + .ok_or_else(|| "secp384r1 not supported by crypto provider".to_owned()), + // X25519 + "X25519" | "x25519" => all_groups + .iter() + .find(|g| g.name() == rustls::NamedGroup::X25519) + .map(|g| vec![*g]) + .ok_or_else(|| "X25519 not supported by crypto provider".to_owned()), + // P-521 (also known as secp521r1 or prime521v1) + // Now supported with aws-lc-rs crypto provider + "prime521v1" | "secp521r1" => all_groups + .iter() + .find(|g| g.name() == rustls::NamedGroup::secp521r1) + .map(|g| vec![*g]) + .ok_or_else(|| "secp521r1 not supported by crypto provider".to_owned()), + // X448 + // Now supported with aws-lc-rs crypto provider + "X448" | "x448" => all_groups + .iter() + .find(|g| g.name() == rustls::NamedGroup::X448) + .map(|g| vec![*g]) + .ok_or_else(|| "X448 not supported by crypto provider".to_owned()), + _ => Err(format!("unknown curve name '{curve}'")), + } +} diff --git a/stdlib/src/ssl/oid.rs b/stdlib/src/ssl/oid.rs new file mode 100644 index 00000000000..2e13733a2a2 --- /dev/null +++ b/stdlib/src/ssl/oid.rs @@ -0,0 +1,464 @@ +// spell-checker: disable + +//! OID (Object Identifier) management for SSL/TLS +//! +//! This module provides OID lookup functionality compatible with CPython's ssl module. +//! It uses oid-registry crate for well-known OIDs while maintaining NID (Numerical Identifier) +//! mappings for CPython compatibility. + +use oid_registry::asn1_rs::Oid; +use std::collections::HashMap; + +/// OID entry with openssl-compatible metadata +#[derive(Debug, Clone)] +pub struct OidEntry { + /// NID (OpenSSL Numerical Identifier) - must match CPython/OpenSSL values + pub nid: i32, + /// Short name (e.g., "CN", "serverAuth") + pub short_name: &'static str, + /// Long name/description (e.g., "commonName", "TLS Web Server Authentication") + pub long_name: &'static str, + /// OID reference (static or dynamic) + pub oid: OidRef, +} + +/// OID reference - either from oid-registry or runtime-created +#[derive(Debug, Clone)] +pub enum OidRef { + /// Static OID from oid-registry crate (stored as value) + Static(Oid<'static>), + /// OID string (for OIDs not in oid-registry) - parsed on demand + String(&'static str), +} + +impl OidEntry { + /// Create entry from oid-registry static constant + pub fn from_static( + nid: i32, + short_name: &'static str, + long_name: &'static str, + oid: &Oid<'static>, + ) -> Self { + Self { + nid, + short_name, + long_name, + oid: OidRef::Static(oid.clone()), + } + } + + /// Create entry from OID string (for OIDs not in oid-registry) + pub const fn from_string( + nid: i32, + short_name: &'static str, + long_name: &'static str, + oid_str: &'static str, + ) -> Self { + Self { + nid, + short_name, + long_name, + oid: OidRef::String(oid_str), + } + } + + /// Get OID as string (e.g., "2.5.4.3") + pub fn oid_string(&self) -> String { + match &self.oid { + OidRef::Static(oid) => oid.to_id_string(), + OidRef::String(s) => s.to_string(), + } + } +} + +/// OID table with multiple indices for fast lookup +pub struct OidTable { + /// All entries + entries: Vec, + /// NID -> index mapping + nid_to_idx: HashMap, + /// Short name -> index mapping + short_name_to_idx: HashMap<&'static str, usize>, + /// Long name -> index mapping (case-insensitive) + long_name_to_idx: HashMap, + /// OID string -> index mapping + oid_str_to_idx: HashMap, +} + +impl OidTable { + fn build() -> Self { + let entries = build_oid_entries(); + let mut nid_to_idx = HashMap::with_capacity(entries.len()); + let mut short_name_to_idx = HashMap::with_capacity(entries.len()); + let mut long_name_to_idx = HashMap::with_capacity(entries.len()); + let mut oid_str_to_idx = HashMap::with_capacity(entries.len()); + + for (idx, entry) in entries.iter().enumerate() { + nid_to_idx.insert(entry.nid, idx); + short_name_to_idx.insert(entry.short_name, idx); + long_name_to_idx.insert(entry.long_name.to_lowercase(), idx); + oid_str_to_idx.insert(entry.oid_string(), idx); + } + + Self { + entries, + nid_to_idx, + short_name_to_idx, + long_name_to_idx, + oid_str_to_idx, + } + } + + pub fn find_by_nid(&self, nid: i32) -> Option<&OidEntry> { + self.nid_to_idx.get(&nid).map(|&idx| &self.entries[idx]) + } + + pub fn find_by_oid_string(&self, oid_str: &str) -> Option<&OidEntry> { + self.oid_str_to_idx + .get(oid_str) + .map(|&idx| &self.entries[idx]) + } + + pub fn find_by_name(&self, name: &str) -> Option<&OidEntry> { + // Try short name first (exact match) + self.short_name_to_idx + .get(name) + .or_else(|| { + // Try long name (case-insensitive) + self.long_name_to_idx.get(&name.to_lowercase()) + }) + .map(|&idx| &self.entries[idx]) + } +} + +/// Global OID table +static OID_TABLE: std::sync::LazyLock = std::sync::LazyLock::new(OidTable::build); + +/// Macro to define OID entry using oid-registry constant +macro_rules! oid_static { + ($nid:expr, $short:expr, $long:expr, $oid_const:path) => { + OidEntry::from_static($nid, $short, $long, &$oid_const) + }; +} + +/// Macro to define OID entry from string +macro_rules! oid_string { + ($nid:expr, $short:expr, $long:expr, $oid_str:expr) => { + OidEntry::from_string($nid, $short, $long, $oid_str) + }; +} + +/// Build the complete OID table +fn build_oid_entries() -> Vec { + vec![ + // Priority 1: X.509 DN Attributes (OpenSSL NID values) + // These NIDs MUST match OpenSSL for CPython compatibility + oid_static!(13, "CN", "commonName", oid_registry::OID_X509_COMMON_NAME), + oid_static!(14, "C", "countryName", oid_registry::OID_X509_COUNTRY_NAME), + oid_static!( + 15, + "L", + "localityName", + oid_registry::OID_X509_LOCALITY_NAME + ), + oid_static!( + 16, + "ST", + "stateOrProvinceName", + oid_registry::OID_X509_STATE_OR_PROVINCE_NAME + ), + oid_static!( + 17, + "O", + "organizationName", + oid_registry::OID_X509_ORGANIZATION_NAME + ), + oid_static!( + 18, + "OU", + "organizationalUnitName", + oid_registry::OID_X509_ORGANIZATIONAL_UNIT + ), + oid_static!(41, "name", "name", oid_registry::OID_X509_NAME), + oid_static!(42, "GN", "givenName", oid_registry::OID_X509_GIVEN_NAME), + oid_static!(43, "initials", "initials", oid_registry::OID_X509_INITIALS), + oid_static!( + 4, + "serialNumber", + "serialNumber", + oid_registry::OID_X509_SERIALNUMBER + ), + oid_static!(100, "surname", "surname", oid_registry::OID_X509_SURNAME), + // emailAddress is special - it's in PKCS#9, not X.509 + oid_static!( + 48, + "emailAddress", + "emailAddress", + oid_registry::OID_PKCS9_EMAIL_ADDRESS + ), + // Priority 2: X.509 Extensions (Critical ones) + oid_static!( + 82, + "subjectKeyIdentifier", + "X509v3 Subject Key Identifier", + oid_registry::OID_X509_EXT_SUBJECT_KEY_IDENTIFIER + ), + oid_static!( + 83, + "keyUsage", + "X509v3 Key Usage", + oid_registry::OID_X509_EXT_KEY_USAGE + ), + oid_static!( + 85, + "subjectAltName", + "X509v3 Subject Alternative Name", + oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME + ), + oid_static!( + 86, + "issuerAltName", + "X509v3 Issuer Alternative Name", + oid_registry::OID_X509_EXT_ISSUER_ALT_NAME + ), + oid_static!( + 87, + "basicConstraints", + "X509v3 Basic Constraints", + oid_registry::OID_X509_EXT_BASIC_CONSTRAINTS + ), + oid_static!( + 88, + "crlNumber", + "X509v3 CRL Number", + oid_registry::OID_X509_EXT_CRL_NUMBER + ), + oid_static!( + 90, + "authorityKeyIdentifier", + "X509v3 Authority Key Identifier", + oid_registry::OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER + ), + oid_static!( + 126, + "extendedKeyUsage", + "X509v3 Extended Key Usage", + oid_registry::OID_X509_EXT_EXTENDED_KEY_USAGE + ), + oid_static!( + 103, + "crlDistributionPoints", + "X509v3 CRL Distribution Points", + oid_registry::OID_X509_EXT_CRL_DISTRIBUTION_POINTS + ), + oid_static!( + 89, + "certificatePolicies", + "X509v3 Certificate Policies", + oid_registry::OID_X509_EXT_CERTIFICATE_POLICIES + ), + oid_static!( + 177, + "authorityInfoAccess", + "Authority Information Access", + oid_registry::OID_PKIX_AUTHORITY_INFO_ACCESS + ), + oid_static!( + 105, + "nameConstraints", + "X509v3 Name Constraints", + oid_registry::OID_X509_EXT_NAME_CONSTRAINTS + ), + // Priority 3: Extended Key Usage OIDs (not in oid-registry) + // These are defined in RFC 5280 but not in oid-registry, so we use strings + oid_string!( + 129, + "serverAuth", + "TLS Web Server Authentication", + "1.3.6.1.5.5.7.3.1" + ), + oid_string!( + 130, + "clientAuth", + "TLS Web Client Authentication", + "1.3.6.1.5.5.7.3.2" + ), + oid_string!(131, "codeSigning", "Code Signing", "1.3.6.1.5.5.7.3.3"), + oid_string!( + 132, + "emailProtection", + "E-mail Protection", + "1.3.6.1.5.5.7.3.4" + ), + oid_string!(133, "timeStamping", "Time Stamping", "1.3.6.1.5.5.7.3.8"), + oid_string!(180, "OCSPSigning", "OCSP Signing", "1.3.6.1.5.5.7.3.9"), + // Priority 4: Signature Algorithms + oid_static!( + 6, + "rsaEncryption", + "rsaEncryption", + oid_registry::OID_PKCS1_RSAENCRYPTION + ), + oid_static!( + 65, + "sha1WithRSAEncryption", + "sha1WithRSAEncryption", + oid_registry::OID_PKCS1_SHA1WITHRSA + ), + oid_static!( + 668, + "sha256WithRSAEncryption", + "sha256WithRSAEncryption", + oid_registry::OID_PKCS1_SHA256WITHRSA + ), + oid_static!( + 669, + "sha384WithRSAEncryption", + "sha384WithRSAEncryption", + oid_registry::OID_PKCS1_SHA384WITHRSA + ), + oid_static!( + 670, + "sha512WithRSAEncryption", + "sha512WithRSAEncryption", + oid_registry::OID_PKCS1_SHA512WITHRSA + ), + oid_static!( + 408, + "id-ecPublicKey", + "id-ecPublicKey", + oid_registry::OID_KEY_TYPE_EC_PUBLIC_KEY + ), + oid_static!( + 794, + "ecdsa-with-SHA256", + "ecdsa-with-SHA256", + oid_registry::OID_SIG_ECDSA_WITH_SHA256 + ), + oid_static!( + 795, + "ecdsa-with-SHA384", + "ecdsa-with-SHA384", + oid_registry::OID_SIG_ECDSA_WITH_SHA384 + ), + oid_static!( + 796, + "ecdsa-with-SHA512", + "ecdsa-with-SHA512", + oid_registry::OID_SIG_ECDSA_WITH_SHA512 + ), + // Priority 5: Hash Algorithms + oid_string!(64, "sha1", "sha1", "1.3.14.3.2.26"), + oid_static!(672, "sha256", "sha256", oid_registry::OID_NIST_HASH_SHA256), + oid_static!(673, "sha384", "sha384", oid_registry::OID_NIST_HASH_SHA384), + oid_static!(674, "sha512", "sha512", oid_registry::OID_NIST_HASH_SHA512), + oid_string!(675, "sha224", "sha224", "2.16.840.1.101.3.4.2.4"), + // Priority 6: Elliptic Curve OIDs + oid_static!(714, "secp256r1", "secp256r1", oid_registry::OID_EC_P256), + oid_string!(715, "secp384r1", "secp384r1", "1.3.132.0.34"), + oid_string!(716, "secp521r1", "secp521r1", "1.3.132.0.35"), + oid_string!(1172, "X25519", "X25519", "1.3.101.110"), + oid_string!(1173, "Ed25519", "Ed25519", "1.3.101.112"), + // Additional useful OIDs + oid_string!( + 183, + "subjectInfoAccess", + "Subject Information Access", + "1.3.6.1.5.5.7.1.11" + ), + oid_string!(920, "OCSP", "OCSP", "1.3.6.1.5.5.7.48.1"), + oid_string!(921, "caIssuers", "CA Issuers", "1.3.6.1.5.5.7.48.2"), + ] +} + +// Public API Functions + +/// Find OID entry by NID +pub fn find_by_nid(nid: i32) -> Option<&'static OidEntry> { + OID_TABLE.find_by_nid(nid) +} + +/// Find OID entry by OID string (e.g., "2.5.4.3") +pub fn find_by_oid_string(oid_str: &str) -> Option<&'static OidEntry> { + OID_TABLE.find_by_oid_string(oid_str) +} + +/// Find OID entry by name (short or long name) +pub fn find_by_name(name: &str) -> Option<&'static OidEntry> { + OID_TABLE.find_by_name(name) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_find_by_nid() { + let entry = find_by_nid(13).unwrap(); + assert_eq!(entry.short_name, "CN"); + assert_eq!(entry.long_name, "commonName"); + assert_eq!(entry.oid_string(), "2.5.4.3"); + } + + #[test] + fn test_find_by_oid_string() { + let entry = find_by_oid_string("2.5.4.3").unwrap(); + assert_eq!(entry.nid, 13); + assert_eq!(entry.short_name, "CN"); + } + + #[test] + fn test_find_by_name_short() { + let entry = find_by_name("CN").unwrap(); + assert_eq!(entry.nid, 13); + assert_eq!(entry.oid_string(), "2.5.4.3"); + } + + #[test] + fn test_find_by_name_long() { + let entry = find_by_name("commonName").unwrap(); + assert_eq!(entry.nid, 13); + assert_eq!(entry.short_name, "CN"); + } + + #[test] + fn test_find_by_name_case_insensitive() { + let entry = find_by_name("COMMONNAME").unwrap(); + assert_eq!(entry.nid, 13); + } + + #[test] + fn test_subject_alt_name() { + let entry = find_by_nid(85).unwrap(); + assert_eq!(entry.short_name, "subjectAltName"); + assert_eq!(entry.oid_string(), "2.5.29.17"); + } + + #[test] + fn test_server_auth_eku() { + let entry = find_by_nid(129).unwrap(); + assert_eq!(entry.short_name, "serverAuth"); + assert_eq!(entry.oid_string(), "1.3.6.1.5.5.7.3.1"); + } + + #[test] + fn test_no_duplicate_nids() { + let table = &*OID_TABLE; + assert_eq!( + table.entries.len(), + table.nid_to_idx.len(), + "Duplicate NIDs detected!" + ); + } + + #[test] + fn test_oid_count() { + let table = &*OID_TABLE; + // We should have 50+ OIDs defined + assert!( + table.entries.len() >= 50, + "Expected at least 50 OIDs, got {}", + table.entries.len() + ); + } +} From 88eca9693af8e958784c5cab1d45198a2d44d1a0 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:25:45 +0200 Subject: [PATCH 0034/1459] Move `stdlib` -> `crates/stdlib` (#6268) --- Cargo.lock | 8 ++++++++ Cargo.toml | 3 +-- {stdlib => crates/stdlib}/Cargo.toml | 0 {stdlib => crates/stdlib}/build.rs | 0 {stdlib => crates/stdlib}/src/array.rs | 0 {stdlib => crates/stdlib}/src/binascii.rs | 0 {stdlib => crates/stdlib}/src/bisect.rs | 0 {stdlib => crates/stdlib}/src/blake2.rs | 0 {stdlib => crates/stdlib}/src/bz2.rs | 0 {stdlib => crates/stdlib}/src/cmath.rs | 0 {stdlib => crates/stdlib}/src/compression.rs | 0 {stdlib => crates/stdlib}/src/contextvars.rs | 0 {stdlib => crates/stdlib}/src/csv.rs | 0 {stdlib => crates/stdlib}/src/dis.rs | 0 {stdlib => crates/stdlib}/src/faulthandler.rs | 0 {stdlib => crates/stdlib}/src/fcntl.rs | 0 {stdlib => crates/stdlib}/src/gc.rs | 0 {stdlib => crates/stdlib}/src/grp.rs | 0 {stdlib => crates/stdlib}/src/hashlib.rs | 0 {stdlib => crates/stdlib}/src/json.rs | 0 {stdlib => crates/stdlib}/src/json/machinery.rs | 0 {stdlib => crates/stdlib}/src/lib.rs | 0 {stdlib => crates/stdlib}/src/locale.rs | 0 {stdlib => crates/stdlib}/src/lzma.rs | 0 {stdlib => crates/stdlib}/src/math.rs | 0 {stdlib => crates/stdlib}/src/md5.rs | 0 {stdlib => crates/stdlib}/src/mmap.rs | 0 {stdlib => crates/stdlib}/src/multiprocessing.rs | 0 {stdlib => crates/stdlib}/src/opcode.rs | 0 {stdlib => crates/stdlib}/src/openssl.rs | 0 {stdlib => crates/stdlib}/src/openssl/cert.rs | 0 {stdlib => crates/stdlib}/src/openssl/ssl_data_111.rs | 0 {stdlib => crates/stdlib}/src/openssl/ssl_data_300.rs | 0 {stdlib => crates/stdlib}/src/openssl/ssl_data_31.rs | 0 {stdlib => crates/stdlib}/src/overlapped.rs | 0 {stdlib => crates/stdlib}/src/posixsubprocess.rs | 0 {stdlib => crates/stdlib}/src/pyexpat.rs | 0 {stdlib => crates/stdlib}/src/pystruct.rs | 0 {stdlib => crates/stdlib}/src/random.rs | 0 {stdlib => crates/stdlib}/src/re.rs | 0 {stdlib => crates/stdlib}/src/resource.rs | 0 {stdlib => crates/stdlib}/src/scproxy.rs | 0 {stdlib => crates/stdlib}/src/select.rs | 0 {stdlib => crates/stdlib}/src/sha1.rs | 0 {stdlib => crates/stdlib}/src/sha256.rs | 0 {stdlib => crates/stdlib}/src/sha3.rs | 0 {stdlib => crates/stdlib}/src/sha512.rs | 0 {stdlib => crates/stdlib}/src/socket.rs | 0 {stdlib => crates/stdlib}/src/sqlite.rs | 0 {stdlib => crates/stdlib}/src/ssl.rs | 0 {stdlib => crates/stdlib}/src/ssl/cert.rs | 0 {stdlib => crates/stdlib}/src/ssl/compat.rs | 0 {stdlib => crates/stdlib}/src/ssl/oid.rs | 0 {stdlib => crates/stdlib}/src/statistics.rs | 0 {stdlib => crates/stdlib}/src/suggestions.rs | 0 {stdlib => crates/stdlib}/src/syslog.rs | 0 {stdlib => crates/stdlib}/src/termios.rs | 0 {stdlib => crates/stdlib}/src/tkinter.rs | 0 {stdlib => crates/stdlib}/src/unicodedata.rs | 0 {stdlib => crates/stdlib}/src/uuid.rs | 0 {stdlib => crates/stdlib}/src/zlib.rs | 0 61 files changed, 9 insertions(+), 2 deletions(-) rename {stdlib => crates/stdlib}/Cargo.toml (100%) rename {stdlib => crates/stdlib}/build.rs (100%) rename {stdlib => crates/stdlib}/src/array.rs (100%) rename {stdlib => crates/stdlib}/src/binascii.rs (100%) rename {stdlib => crates/stdlib}/src/bisect.rs (100%) rename {stdlib => crates/stdlib}/src/blake2.rs (100%) rename {stdlib => crates/stdlib}/src/bz2.rs (100%) rename {stdlib => crates/stdlib}/src/cmath.rs (100%) rename {stdlib => crates/stdlib}/src/compression.rs (100%) rename {stdlib => crates/stdlib}/src/contextvars.rs (100%) rename {stdlib => crates/stdlib}/src/csv.rs (100%) rename {stdlib => crates/stdlib}/src/dis.rs (100%) rename {stdlib => crates/stdlib}/src/faulthandler.rs (100%) rename {stdlib => crates/stdlib}/src/fcntl.rs (100%) rename {stdlib => crates/stdlib}/src/gc.rs (100%) rename {stdlib => crates/stdlib}/src/grp.rs (100%) rename {stdlib => crates/stdlib}/src/hashlib.rs (100%) rename {stdlib => crates/stdlib}/src/json.rs (100%) rename {stdlib => crates/stdlib}/src/json/machinery.rs (100%) rename {stdlib => crates/stdlib}/src/lib.rs (100%) rename {stdlib => crates/stdlib}/src/locale.rs (100%) rename {stdlib => crates/stdlib}/src/lzma.rs (100%) rename {stdlib => crates/stdlib}/src/math.rs (100%) rename {stdlib => crates/stdlib}/src/md5.rs (100%) rename {stdlib => crates/stdlib}/src/mmap.rs (100%) rename {stdlib => crates/stdlib}/src/multiprocessing.rs (100%) rename {stdlib => crates/stdlib}/src/opcode.rs (100%) rename {stdlib => crates/stdlib}/src/openssl.rs (100%) rename {stdlib => crates/stdlib}/src/openssl/cert.rs (100%) rename {stdlib => crates/stdlib}/src/openssl/ssl_data_111.rs (100%) rename {stdlib => crates/stdlib}/src/openssl/ssl_data_300.rs (100%) rename {stdlib => crates/stdlib}/src/openssl/ssl_data_31.rs (100%) rename {stdlib => crates/stdlib}/src/overlapped.rs (100%) rename {stdlib => crates/stdlib}/src/posixsubprocess.rs (100%) rename {stdlib => crates/stdlib}/src/pyexpat.rs (100%) rename {stdlib => crates/stdlib}/src/pystruct.rs (100%) rename {stdlib => crates/stdlib}/src/random.rs (100%) rename {stdlib => crates/stdlib}/src/re.rs (100%) rename {stdlib => crates/stdlib}/src/resource.rs (100%) rename {stdlib => crates/stdlib}/src/scproxy.rs (100%) rename {stdlib => crates/stdlib}/src/select.rs (100%) rename {stdlib => crates/stdlib}/src/sha1.rs (100%) rename {stdlib => crates/stdlib}/src/sha256.rs (100%) rename {stdlib => crates/stdlib}/src/sha3.rs (100%) rename {stdlib => crates/stdlib}/src/sha512.rs (100%) rename {stdlib => crates/stdlib}/src/socket.rs (100%) rename {stdlib => crates/stdlib}/src/sqlite.rs (100%) rename {stdlib => crates/stdlib}/src/ssl.rs (100%) rename {stdlib => crates/stdlib}/src/ssl/cert.rs (100%) rename {stdlib => crates/stdlib}/src/ssl/compat.rs (100%) rename {stdlib => crates/stdlib}/src/ssl/oid.rs (100%) rename {stdlib => crates/stdlib}/src/statistics.rs (100%) rename {stdlib => crates/stdlib}/src/suggestions.rs (100%) rename {stdlib => crates/stdlib}/src/syslog.rs (100%) rename {stdlib => crates/stdlib}/src/termios.rs (100%) rename {stdlib => crates/stdlib}/src/tkinter.rs (100%) rename {stdlib => crates/stdlib}/src/unicodedata.rs (100%) rename {stdlib => crates/stdlib}/src/uuid.rs (100%) rename {stdlib => crates/stdlib}/src/zlib.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index cec9f7afb47..4f749bc49b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3025,6 +3025,14 @@ dependencies = [ "rustpython-wtf8", ] +[[package]] +name = "rustpython-compiler-source" +version = "0.5.0+deprecated" +dependencies = [ + "ruff_source_file", + "ruff_text_size", +] + [[package]] name = "rustpython-derive" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index c1ee8eaa3d5..0773e8a410a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,7 +124,6 @@ template = "installer-config/installer.wxs" resolver = "2" members = [ ".", - "stdlib", "wasm/lib", "crates/*", ] @@ -148,7 +147,7 @@ rustpython-jit = { path = "crates/jit", version = "0.4.0" } rustpython-literal = { path = "crates/literal", version = "0.4.0" } rustpython-vm = { path = "crates/vm", default-features = false, version = "0.4.0" } rustpython-pylib = { path = "crates/pylib", version = "0.4.0" } -rustpython-stdlib = { path = "stdlib", default-features = false, version = "0.4.0" } +rustpython-stdlib = { path = "crates/stdlib", default-features = false, version = "0.4.0" } rustpython-sre_engine = { path = "crates/sre_engine", version = "0.4.0" } rustpython-wtf8 = { path = "crates/wtf8", version = "0.4.0" } rustpython-doc = { path = "crates/doc", version = "0.4.0" } diff --git a/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml similarity index 100% rename from stdlib/Cargo.toml rename to crates/stdlib/Cargo.toml diff --git a/stdlib/build.rs b/crates/stdlib/build.rs similarity index 100% rename from stdlib/build.rs rename to crates/stdlib/build.rs diff --git a/stdlib/src/array.rs b/crates/stdlib/src/array.rs similarity index 100% rename from stdlib/src/array.rs rename to crates/stdlib/src/array.rs diff --git a/stdlib/src/binascii.rs b/crates/stdlib/src/binascii.rs similarity index 100% rename from stdlib/src/binascii.rs rename to crates/stdlib/src/binascii.rs diff --git a/stdlib/src/bisect.rs b/crates/stdlib/src/bisect.rs similarity index 100% rename from stdlib/src/bisect.rs rename to crates/stdlib/src/bisect.rs diff --git a/stdlib/src/blake2.rs b/crates/stdlib/src/blake2.rs similarity index 100% rename from stdlib/src/blake2.rs rename to crates/stdlib/src/blake2.rs diff --git a/stdlib/src/bz2.rs b/crates/stdlib/src/bz2.rs similarity index 100% rename from stdlib/src/bz2.rs rename to crates/stdlib/src/bz2.rs diff --git a/stdlib/src/cmath.rs b/crates/stdlib/src/cmath.rs similarity index 100% rename from stdlib/src/cmath.rs rename to crates/stdlib/src/cmath.rs diff --git a/stdlib/src/compression.rs b/crates/stdlib/src/compression.rs similarity index 100% rename from stdlib/src/compression.rs rename to crates/stdlib/src/compression.rs diff --git a/stdlib/src/contextvars.rs b/crates/stdlib/src/contextvars.rs similarity index 100% rename from stdlib/src/contextvars.rs rename to crates/stdlib/src/contextvars.rs diff --git a/stdlib/src/csv.rs b/crates/stdlib/src/csv.rs similarity index 100% rename from stdlib/src/csv.rs rename to crates/stdlib/src/csv.rs diff --git a/stdlib/src/dis.rs b/crates/stdlib/src/dis.rs similarity index 100% rename from stdlib/src/dis.rs rename to crates/stdlib/src/dis.rs diff --git a/stdlib/src/faulthandler.rs b/crates/stdlib/src/faulthandler.rs similarity index 100% rename from stdlib/src/faulthandler.rs rename to crates/stdlib/src/faulthandler.rs diff --git a/stdlib/src/fcntl.rs b/crates/stdlib/src/fcntl.rs similarity index 100% rename from stdlib/src/fcntl.rs rename to crates/stdlib/src/fcntl.rs diff --git a/stdlib/src/gc.rs b/crates/stdlib/src/gc.rs similarity index 100% rename from stdlib/src/gc.rs rename to crates/stdlib/src/gc.rs diff --git a/stdlib/src/grp.rs b/crates/stdlib/src/grp.rs similarity index 100% rename from stdlib/src/grp.rs rename to crates/stdlib/src/grp.rs diff --git a/stdlib/src/hashlib.rs b/crates/stdlib/src/hashlib.rs similarity index 100% rename from stdlib/src/hashlib.rs rename to crates/stdlib/src/hashlib.rs diff --git a/stdlib/src/json.rs b/crates/stdlib/src/json.rs similarity index 100% rename from stdlib/src/json.rs rename to crates/stdlib/src/json.rs diff --git a/stdlib/src/json/machinery.rs b/crates/stdlib/src/json/machinery.rs similarity index 100% rename from stdlib/src/json/machinery.rs rename to crates/stdlib/src/json/machinery.rs diff --git a/stdlib/src/lib.rs b/crates/stdlib/src/lib.rs similarity index 100% rename from stdlib/src/lib.rs rename to crates/stdlib/src/lib.rs diff --git a/stdlib/src/locale.rs b/crates/stdlib/src/locale.rs similarity index 100% rename from stdlib/src/locale.rs rename to crates/stdlib/src/locale.rs diff --git a/stdlib/src/lzma.rs b/crates/stdlib/src/lzma.rs similarity index 100% rename from stdlib/src/lzma.rs rename to crates/stdlib/src/lzma.rs diff --git a/stdlib/src/math.rs b/crates/stdlib/src/math.rs similarity index 100% rename from stdlib/src/math.rs rename to crates/stdlib/src/math.rs diff --git a/stdlib/src/md5.rs b/crates/stdlib/src/md5.rs similarity index 100% rename from stdlib/src/md5.rs rename to crates/stdlib/src/md5.rs diff --git a/stdlib/src/mmap.rs b/crates/stdlib/src/mmap.rs similarity index 100% rename from stdlib/src/mmap.rs rename to crates/stdlib/src/mmap.rs diff --git a/stdlib/src/multiprocessing.rs b/crates/stdlib/src/multiprocessing.rs similarity index 100% rename from stdlib/src/multiprocessing.rs rename to crates/stdlib/src/multiprocessing.rs diff --git a/stdlib/src/opcode.rs b/crates/stdlib/src/opcode.rs similarity index 100% rename from stdlib/src/opcode.rs rename to crates/stdlib/src/opcode.rs diff --git a/stdlib/src/openssl.rs b/crates/stdlib/src/openssl.rs similarity index 100% rename from stdlib/src/openssl.rs rename to crates/stdlib/src/openssl.rs diff --git a/stdlib/src/openssl/cert.rs b/crates/stdlib/src/openssl/cert.rs similarity index 100% rename from stdlib/src/openssl/cert.rs rename to crates/stdlib/src/openssl/cert.rs diff --git a/stdlib/src/openssl/ssl_data_111.rs b/crates/stdlib/src/openssl/ssl_data_111.rs similarity index 100% rename from stdlib/src/openssl/ssl_data_111.rs rename to crates/stdlib/src/openssl/ssl_data_111.rs diff --git a/stdlib/src/openssl/ssl_data_300.rs b/crates/stdlib/src/openssl/ssl_data_300.rs similarity index 100% rename from stdlib/src/openssl/ssl_data_300.rs rename to crates/stdlib/src/openssl/ssl_data_300.rs diff --git a/stdlib/src/openssl/ssl_data_31.rs b/crates/stdlib/src/openssl/ssl_data_31.rs similarity index 100% rename from stdlib/src/openssl/ssl_data_31.rs rename to crates/stdlib/src/openssl/ssl_data_31.rs diff --git a/stdlib/src/overlapped.rs b/crates/stdlib/src/overlapped.rs similarity index 100% rename from stdlib/src/overlapped.rs rename to crates/stdlib/src/overlapped.rs diff --git a/stdlib/src/posixsubprocess.rs b/crates/stdlib/src/posixsubprocess.rs similarity index 100% rename from stdlib/src/posixsubprocess.rs rename to crates/stdlib/src/posixsubprocess.rs diff --git a/stdlib/src/pyexpat.rs b/crates/stdlib/src/pyexpat.rs similarity index 100% rename from stdlib/src/pyexpat.rs rename to crates/stdlib/src/pyexpat.rs diff --git a/stdlib/src/pystruct.rs b/crates/stdlib/src/pystruct.rs similarity index 100% rename from stdlib/src/pystruct.rs rename to crates/stdlib/src/pystruct.rs diff --git a/stdlib/src/random.rs b/crates/stdlib/src/random.rs similarity index 100% rename from stdlib/src/random.rs rename to crates/stdlib/src/random.rs diff --git a/stdlib/src/re.rs b/crates/stdlib/src/re.rs similarity index 100% rename from stdlib/src/re.rs rename to crates/stdlib/src/re.rs diff --git a/stdlib/src/resource.rs b/crates/stdlib/src/resource.rs similarity index 100% rename from stdlib/src/resource.rs rename to crates/stdlib/src/resource.rs diff --git a/stdlib/src/scproxy.rs b/crates/stdlib/src/scproxy.rs similarity index 100% rename from stdlib/src/scproxy.rs rename to crates/stdlib/src/scproxy.rs diff --git a/stdlib/src/select.rs b/crates/stdlib/src/select.rs similarity index 100% rename from stdlib/src/select.rs rename to crates/stdlib/src/select.rs diff --git a/stdlib/src/sha1.rs b/crates/stdlib/src/sha1.rs similarity index 100% rename from stdlib/src/sha1.rs rename to crates/stdlib/src/sha1.rs diff --git a/stdlib/src/sha256.rs b/crates/stdlib/src/sha256.rs similarity index 100% rename from stdlib/src/sha256.rs rename to crates/stdlib/src/sha256.rs diff --git a/stdlib/src/sha3.rs b/crates/stdlib/src/sha3.rs similarity index 100% rename from stdlib/src/sha3.rs rename to crates/stdlib/src/sha3.rs diff --git a/stdlib/src/sha512.rs b/crates/stdlib/src/sha512.rs similarity index 100% rename from stdlib/src/sha512.rs rename to crates/stdlib/src/sha512.rs diff --git a/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs similarity index 100% rename from stdlib/src/socket.rs rename to crates/stdlib/src/socket.rs diff --git a/stdlib/src/sqlite.rs b/crates/stdlib/src/sqlite.rs similarity index 100% rename from stdlib/src/sqlite.rs rename to crates/stdlib/src/sqlite.rs diff --git a/stdlib/src/ssl.rs b/crates/stdlib/src/ssl.rs similarity index 100% rename from stdlib/src/ssl.rs rename to crates/stdlib/src/ssl.rs diff --git a/stdlib/src/ssl/cert.rs b/crates/stdlib/src/ssl/cert.rs similarity index 100% rename from stdlib/src/ssl/cert.rs rename to crates/stdlib/src/ssl/cert.rs diff --git a/stdlib/src/ssl/compat.rs b/crates/stdlib/src/ssl/compat.rs similarity index 100% rename from stdlib/src/ssl/compat.rs rename to crates/stdlib/src/ssl/compat.rs diff --git a/stdlib/src/ssl/oid.rs b/crates/stdlib/src/ssl/oid.rs similarity index 100% rename from stdlib/src/ssl/oid.rs rename to crates/stdlib/src/ssl/oid.rs diff --git a/stdlib/src/statistics.rs b/crates/stdlib/src/statistics.rs similarity index 100% rename from stdlib/src/statistics.rs rename to crates/stdlib/src/statistics.rs diff --git a/stdlib/src/suggestions.rs b/crates/stdlib/src/suggestions.rs similarity index 100% rename from stdlib/src/suggestions.rs rename to crates/stdlib/src/suggestions.rs diff --git a/stdlib/src/syslog.rs b/crates/stdlib/src/syslog.rs similarity index 100% rename from stdlib/src/syslog.rs rename to crates/stdlib/src/syslog.rs diff --git a/stdlib/src/termios.rs b/crates/stdlib/src/termios.rs similarity index 100% rename from stdlib/src/termios.rs rename to crates/stdlib/src/termios.rs diff --git a/stdlib/src/tkinter.rs b/crates/stdlib/src/tkinter.rs similarity index 100% rename from stdlib/src/tkinter.rs rename to crates/stdlib/src/tkinter.rs diff --git a/stdlib/src/unicodedata.rs b/crates/stdlib/src/unicodedata.rs similarity index 100% rename from stdlib/src/unicodedata.rs rename to crates/stdlib/src/unicodedata.rs diff --git a/stdlib/src/uuid.rs b/crates/stdlib/src/uuid.rs similarity index 100% rename from stdlib/src/uuid.rs rename to crates/stdlib/src/uuid.rs diff --git a/stdlib/src/zlib.rs b/crates/stdlib/src/zlib.rs similarity index 100% rename from stdlib/src/zlib.rs rename to crates/stdlib/src/zlib.rs From 916d3ba94be74e82427c378ab604b87ea5806f76 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:47:05 +0200 Subject: [PATCH 0035/1459] Move `wasm/lib` -> `crates/wasm` (#6280) --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 1 - {wasm/lib => crates/wasm}/.cargo/config.toml | 0 {wasm/lib => crates/wasm}/Cargo.toml | 0 {wasm/lib => crates/wasm}/Lib/_microdistlib.py | 0 {wasm/lib => crates/wasm}/Lib/asyncweb.py | 0 {wasm/lib => crates/wasm}/Lib/browser.py | 0 {wasm/lib => crates/wasm}/Lib/whlimport.py | 0 {wasm/lib => crates/wasm}/README.md | 0 {wasm/lib => crates/wasm}/src/browser_module.rs | 0 {wasm/lib => crates/wasm}/src/convert.rs | 0 {wasm/lib => crates/wasm}/src/js_module.rs | 0 {wasm/lib => crates/wasm}/src/lib.rs | 0 {wasm/lib => crates/wasm}/src/vm_class.rs | 0 {wasm/lib => crates/wasm}/src/wasm_builtins.rs | 0 wasm/demo/webpack.config.js | 4 ++-- wasm/notebook/webpack.config.js | 2 +- 17 files changed, 4 insertions(+), 5 deletions(-) rename {wasm/lib => crates/wasm}/.cargo/config.toml (100%) rename {wasm/lib => crates/wasm}/Cargo.toml (100%) rename {wasm/lib => crates/wasm}/Lib/_microdistlib.py (100%) rename {wasm/lib => crates/wasm}/Lib/asyncweb.py (100%) rename {wasm/lib => crates/wasm}/Lib/browser.py (100%) rename {wasm/lib => crates/wasm}/Lib/whlimport.py (100%) rename {wasm/lib => crates/wasm}/README.md (100%) rename {wasm/lib => crates/wasm}/src/browser_module.rs (100%) rename {wasm/lib => crates/wasm}/src/convert.rs (100%) rename {wasm/lib => crates/wasm}/src/js_module.rs (100%) rename {wasm/lib => crates/wasm}/src/lib.rs (100%) rename {wasm/lib => crates/wasm}/src/vm_class.rs (100%) rename {wasm/lib => crates/wasm}/src/wasm_builtins.rs (100%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6963ee5f7c4..fe45a1d71bb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -316,7 +316,7 @@ jobs: with: components: clippy - name: run clippy on wasm - run: cargo clippy --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings + run: cargo clippy --manifest-path=crates/wasm/Cargo.toml -- -Dwarnings - uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} diff --git a/Cargo.toml b/Cargo.toml index 0773e8a410a..53d1eb6ea2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,7 +124,6 @@ template = "installer-config/installer.wxs" resolver = "2" members = [ ".", - "wasm/lib", "crates/*", ] diff --git a/wasm/lib/.cargo/config.toml b/crates/wasm/.cargo/config.toml similarity index 100% rename from wasm/lib/.cargo/config.toml rename to crates/wasm/.cargo/config.toml diff --git a/wasm/lib/Cargo.toml b/crates/wasm/Cargo.toml similarity index 100% rename from wasm/lib/Cargo.toml rename to crates/wasm/Cargo.toml diff --git a/wasm/lib/Lib/_microdistlib.py b/crates/wasm/Lib/_microdistlib.py similarity index 100% rename from wasm/lib/Lib/_microdistlib.py rename to crates/wasm/Lib/_microdistlib.py diff --git a/wasm/lib/Lib/asyncweb.py b/crates/wasm/Lib/asyncweb.py similarity index 100% rename from wasm/lib/Lib/asyncweb.py rename to crates/wasm/Lib/asyncweb.py diff --git a/wasm/lib/Lib/browser.py b/crates/wasm/Lib/browser.py similarity index 100% rename from wasm/lib/Lib/browser.py rename to crates/wasm/Lib/browser.py diff --git a/wasm/lib/Lib/whlimport.py b/crates/wasm/Lib/whlimport.py similarity index 100% rename from wasm/lib/Lib/whlimport.py rename to crates/wasm/Lib/whlimport.py diff --git a/wasm/lib/README.md b/crates/wasm/README.md similarity index 100% rename from wasm/lib/README.md rename to crates/wasm/README.md diff --git a/wasm/lib/src/browser_module.rs b/crates/wasm/src/browser_module.rs similarity index 100% rename from wasm/lib/src/browser_module.rs rename to crates/wasm/src/browser_module.rs diff --git a/wasm/lib/src/convert.rs b/crates/wasm/src/convert.rs similarity index 100% rename from wasm/lib/src/convert.rs rename to crates/wasm/src/convert.rs diff --git a/wasm/lib/src/js_module.rs b/crates/wasm/src/js_module.rs similarity index 100% rename from wasm/lib/src/js_module.rs rename to crates/wasm/src/js_module.rs diff --git a/wasm/lib/src/lib.rs b/crates/wasm/src/lib.rs similarity index 100% rename from wasm/lib/src/lib.rs rename to crates/wasm/src/lib.rs diff --git a/wasm/lib/src/vm_class.rs b/crates/wasm/src/vm_class.rs similarity index 100% rename from wasm/lib/src/vm_class.rs rename to crates/wasm/src/vm_class.rs diff --git a/wasm/lib/src/wasm_builtins.rs b/crates/wasm/src/wasm_builtins.rs similarity index 100% rename from wasm/lib/src/wasm_builtins.rs rename to crates/wasm/src/wasm_builtins.rs diff --git a/wasm/demo/webpack.config.js b/wasm/demo/webpack.config.js index 75fb6786e66..6c798d2ca4f 100644 --- a/wasm/demo/webpack.config.js +++ b/wasm/demo/webpack.config.js @@ -18,7 +18,7 @@ module.exports = (env = {}) => { alias: { rustpython: path.resolve( __dirname, - env.rustpythonPkg || '../lib/pkg', + env.rustpythonPkg || '../../crates/wasm/pkg', ), }, }, @@ -65,7 +65,7 @@ module.exports = (env = {}) => { if (!env.noWasmPack) { config.plugins.push( new WasmPackPlugin({ - crateDirectory: path.join(__dirname, '../lib'), + crateDirectory: path.join(__dirname, '../../crates/wasm'), }), ); } diff --git a/wasm/notebook/webpack.config.js b/wasm/notebook/webpack.config.js index ca19f3384aa..a3e3f8ef713 100644 --- a/wasm/notebook/webpack.config.js +++ b/wasm/notebook/webpack.config.js @@ -18,7 +18,7 @@ module.exports = (env = {}) => { alias: { rustpython: path.resolve( __dirname, - env.rustpythonPkg || '../lib/pkg', + env.rustpythonPkg || '../../crates/wasm/pkg', ), }, }, From 5fb5db961764b2043cb6fcd77feebe4ebcf42c61 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 17 Nov 2025 18:58:07 +0900 Subject: [PATCH 0036/1459] relocate wasm test crate under example_projects --- .../wasm32_without_js/rustpython-without-js}/.cargo/config.toml | 0 .../wasm32_without_js/rustpython-without-js}/Cargo.toml | 2 +- .../wasm32_without_js/rustpython-without-js}/README.md | 0 .../wasm32_without_js/rustpython-without-js}/src/lib.rs | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {wasm/wasm-unknown-test => example_projects/wasm32_without_js/rustpython-without-js}/.cargo/config.toml (100%) rename {wasm/wasm-unknown-test => example_projects/wasm32_without_js/rustpython-without-js}/Cargo.toml (88%) rename {wasm/wasm-unknown-test => example_projects/wasm32_without_js/rustpython-without-js}/README.md (100%) rename {wasm/wasm-unknown-test => example_projects/wasm32_without_js/rustpython-without-js}/src/lib.rs (100%) diff --git a/wasm/wasm-unknown-test/.cargo/config.toml b/example_projects/wasm32_without_js/rustpython-without-js/.cargo/config.toml similarity index 100% rename from wasm/wasm-unknown-test/.cargo/config.toml rename to example_projects/wasm32_without_js/rustpython-without-js/.cargo/config.toml diff --git a/wasm/wasm-unknown-test/Cargo.toml b/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml similarity index 88% rename from wasm/wasm-unknown-test/Cargo.toml rename to example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml index 277a9810b99..78c64eabe63 100644 --- a/wasm/wasm-unknown-test/Cargo.toml +++ b/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wasm-unknown-test" +name = "rustpython-without-js" version = "0.1.0" edition = "2021" diff --git a/wasm/wasm-unknown-test/README.md b/example_projects/wasm32_without_js/rustpython-without-js/README.md similarity index 100% rename from wasm/wasm-unknown-test/README.md rename to example_projects/wasm32_without_js/rustpython-without-js/README.md diff --git a/wasm/wasm-unknown-test/src/lib.rs b/example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs similarity index 100% rename from wasm/wasm-unknown-test/src/lib.rs rename to example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs From eac8968f84c5709f92fd9057b559241f7f79a330 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 17 Nov 2025 20:30:43 +0900 Subject: [PATCH 0037/1459] Add wasm runtime and fix the example code to actually run Co-Authored-By: Valentyn Faychuk Co-Authored-By: Lee Dogeon --- .cspell.dict/rust-more.txt | 1 + example_projects/wasm32_without_js/.gitignore | 2 + example_projects/wasm32_without_js/README.md | 18 +++ .../rustpython-without-js/Cargo.toml | 4 +- .../rustpython-without-js/src/lib.rs | 58 ++++++-- .../wasm32_without_js/wasm-runtime/.gitignore | 4 + .../wasm32_without_js/wasm-runtime/Cargo.toml | 9 ++ .../wasm32_without_js/wasm-runtime/README.md | 19 +++ .../wasm-runtime/src/main.rs | 133 ++++++++++++++++++ 9 files changed, 237 insertions(+), 11 deletions(-) create mode 100644 example_projects/wasm32_without_js/.gitignore create mode 100644 example_projects/wasm32_without_js/README.md create mode 100644 example_projects/wasm32_without_js/wasm-runtime/.gitignore create mode 100644 example_projects/wasm32_without_js/wasm-runtime/Cargo.toml create mode 100644 example_projects/wasm32_without_js/wasm-runtime/README.md create mode 100644 example_projects/wasm32_without_js/wasm-runtime/src/main.rs diff --git a/.cspell.dict/rust-more.txt b/.cspell.dict/rust-more.txt index f27e53bd6ed..ff2013e81a7 100644 --- a/.cspell.dict/rust-more.txt +++ b/.cspell.dict/rust-more.txt @@ -82,6 +82,7 @@ unsync wasip1 wasip2 wasmbind +wasmer wasmtime widestring winapi diff --git a/example_projects/wasm32_without_js/.gitignore b/example_projects/wasm32_without_js/.gitignore new file mode 100644 index 00000000000..50a623b5daa --- /dev/null +++ b/example_projects/wasm32_without_js/.gitignore @@ -0,0 +1,2 @@ +*/target/ +*/Cargo.lock diff --git a/example_projects/wasm32_without_js/README.md b/example_projects/wasm32_without_js/README.md new file mode 100644 index 00000000000..67fef3fba47 --- /dev/null +++ b/example_projects/wasm32_without_js/README.md @@ -0,0 +1,18 @@ +# RustPython wasm32 build without JS + +To test, build rustpython to wasm32-unknown-unknown target first. + +```shell +cd rustpython-without-js # due to `.cargo/config.toml` +cargo build +cd .. +``` + +Then there will be `rustpython-without-js/target/wasm32-unknown-unknown/debug/rustpython_without_js.wasm` file. + +Now we can run the wasm file with wasm runtime: + +```shell +cargo run --release --manifest-path wasm-runtime/Cargo.toml rustpython-without-js/target/wasm32-unknown-unknown/debug/rustpython_without_js.wasm +``` + diff --git a/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml b/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml index 78c64eabe63..f987fe94a24 100644 --- a/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml +++ b/example_projects/wasm32_without_js/rustpython-without-js/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "rustpython-without-js" version = "0.1.0" -edition = "2021" +edition = "2024" [lib] crate-type = ["cdylib"] [dependencies] getrandom = "0.3" -rustpython-vm = { path = "../../crates/vm", default-features = false, features = ["compiler"] } +rustpython-vm = { path = "../../../crates/vm", default-features = false, features = ["compiler"] } [workspace] diff --git a/example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs b/example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs index aae922864dd..0a8695fd7fb 100644 --- a/example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs +++ b/example_projects/wasm32_without_js/rustpython-without-js/src/lib.rs @@ -1,13 +1,50 @@ -use rustpython_vm::{Interpreter, eval}; +use rustpython_vm::{Interpreter}; + +unsafe extern "C" { + fn kv_get(kp: i32, kl: i32, vp: i32, vl: i32) -> i32; + + /// kp and kl are the key pointer and length in wasm memory, vp and vl are for the value + fn kv_put(kp: i32, kl: i32, vp: i32, vl: i32) -> i32; + + fn print(p: i32, l: i32) -> i32; +} #[unsafe(no_mangle)] -pub unsafe extern "C" fn eval(s: *const u8, l: usize) -> u32 { - let src = std::slice::from_raw_parts(s, l); - let src = std::str::from_utf8(src).unwrap(); - Interpreter::without_stdlib(Default::default()).enter(|vm| { - let res = eval::eval(vm, src, vm.new_scope_with_builtins(), "").unwrap(); - res.try_into_value(vm).unwrap() - }) +pub unsafe extern "C" fn eval(s: *const u8, l: usize) -> i32 { + // let src = unsafe { std::slice::from_raw_parts(s, l) }; + // let src = std::str::from_utf8(src).unwrap(); + // TODO: use src + let src = "1 + 3"; + + // 2. Execute Python code + let interpreter = Interpreter::without_stdlib(Default::default()); + let result = interpreter.enter(|vm| { + let scope = vm.new_scope_with_builtins(); + let res = match vm.run_block_expr(scope, src) { + Ok(val) => val, + Err(_) => return Err(-1), // Python execution error + }; + let repr_str = match res.repr(vm) { + Ok(repr) => repr.as_str().to_string(), + Err(_) => return Err(-1), // Failed to get string representation + }; + Ok(repr_str) + }); + let result = match result { + Ok(r) => r, + Err(code) => return code, + }; + + let msg = format!("eval result: {result}"); + + unsafe { + print( + msg.as_str().as_ptr() as usize as i32, + msg.len() as i32, + ) + }; + + 0 } #[unsafe(no_mangle)] @@ -15,5 +52,8 @@ unsafe extern "Rust" fn __getrandom_v03_custom( _dest: *mut u8, _len: usize, ) -> Result<(), getrandom::Error> { - Err(getrandom::Error::UNSUPPORTED) + // Err(getrandom::Error::UNSUPPORTED) + + // WARNING: This function **MUST** perform proper getrandom + Ok(()) } diff --git a/example_projects/wasm32_without_js/wasm-runtime/.gitignore b/example_projects/wasm32_without_js/wasm-runtime/.gitignore new file mode 100644 index 00000000000..2e2101b5066 --- /dev/null +++ b/example_projects/wasm32_without_js/wasm-runtime/.gitignore @@ -0,0 +1,4 @@ +*.wasm +target +Cargo.lock +!wasm/rustpython.wasm diff --git a/example_projects/wasm32_without_js/wasm-runtime/Cargo.toml b/example_projects/wasm32_without_js/wasm-runtime/Cargo.toml new file mode 100644 index 00000000000..a1d0de51719 --- /dev/null +++ b/example_projects/wasm32_without_js/wasm-runtime/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "wasm-runtime" +version = "0.1.0" +edition = "2024" + +[dependencies] +wasmer = "6.1.0" + +[workspace] \ No newline at end of file diff --git a/example_projects/wasm32_without_js/wasm-runtime/README.md b/example_projects/wasm32_without_js/wasm-runtime/README.md new file mode 100644 index 00000000000..2fa2f9e119a --- /dev/null +++ b/example_projects/wasm32_without_js/wasm-runtime/README.md @@ -0,0 +1,19 @@ +# Simple WASM Runtime + +WebAssembly runtime POC with wasmer with HashMap-based KV store. +First make sure to install wat2wasm and rust. + +```bash +# following command installs rust +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +cargo run --release +``` + +## WASM binary requirements + +Entry point is `eval(code_ptr: i32, code_len: i32) -> i32`, following are exported functions, on error return -1: + +- `kv_put(key_ptr: i32, key_len: i32, val_ptr: i32, val_len: i32) -> i32` +- `kv_get(key_ptr: i32, key_len: i32, val_ptr: i32, val_len: i32) -> i32` +- `print(msg_ptr: i32, msg_len: i32) -> i32` diff --git a/example_projects/wasm32_without_js/wasm-runtime/src/main.rs b/example_projects/wasm32_without_js/wasm-runtime/src/main.rs new file mode 100644 index 00000000000..8ca7d581ce4 --- /dev/null +++ b/example_projects/wasm32_without_js/wasm-runtime/src/main.rs @@ -0,0 +1,133 @@ +use std::collections::HashMap; +use wasmer::{ + Function, FunctionEnv, FunctionEnvMut, Instance, Memory, Module, Store, Value, imports, +}; + +struct Ctx { + kv: HashMap, Vec>, + mem: Option, +} + +/// kp and kl are the key pointer and length in wasm memory, vp and vl are for the return value +/// if read value is bigger than vl then it will be truncated to vl, returns read bytes +fn kv_get(mut ctx: FunctionEnvMut, kp: i32, kl: i32, vp: i32, vl: i32) -> i32 { + let (c, s) = ctx.data_and_store_mut(); + let mut key = vec![0u8; kl as usize]; + if c.mem + .as_ref() + .unwrap() + .view(&s) + .read(kp as u64, &mut key) + .is_err() + { + return -1; + } + match c.kv.get(&key) { + Some(val) => { + let len = val.len().min(vl as usize); + if c.mem + .as_ref() + .unwrap() + .view(&s) + .write(vp as u64, &val[..len]) + .is_err() + { + return -1; + } + len as i32 + } + None => 0, + } +} + +/// kp and kl are the key pointer and length in wasm memory, vp and vl are for the value +fn kv_put(mut ctx: FunctionEnvMut, kp: i32, kl: i32, vp: i32, vl: i32) -> i32 { + let (c, s) = ctx.data_and_store_mut(); + let mut key = vec![0u8; kl as usize]; + let mut val = vec![0u8; vl as usize]; + let m = c.mem.as_ref().unwrap().view(&s); + if m.read(kp as u64, &mut key).is_err() || m.read(vp as u64, &mut val).is_err() { + return -1; + } + c.kv.insert(key, val); + 0 +} + +// // p and l are the buffer pointer and length in wasm memory. +// fn get_code(mut ctx:FunctionEnvMut, p: i32, l: i32) -> i32 { +// let file_name = std::env::args().nth(2).expect("file_name is not given"); +// let code : String = std::fs::read_to_string(file_name).expect("file read failed"); +// if code.len() > l as usize { +// eprintln!("code is too long"); +// return -1; +// } + +// let (c, s) = ctx.data_and_store_mut(); +// let m = c.mem.as_ref().unwrap().view(&s); +// if m.write(p as u64, code.as_bytes()).is_err() { +// return -2; +// } + +// 0 +// } + +// p and l are the message pointer and length in wasm memory. +fn print(mut ctx: FunctionEnvMut, p: i32, l: i32) -> i32 { + let (c, s) = ctx.data_and_store_mut(); + let mut msg = vec![0u8; l as usize]; + let m = c.mem.as_ref().unwrap().view(&s); + if m.read(p as u64, &mut msg).is_err() { + return -1; + } + let s = std::str::from_utf8(&msg).expect("print got non-utf8 str"); + println!("{s}"); + 0 +} + +fn main() { + let mut store = Store::default(); + let module = Module::new( + &store, + &std::fs::read(&std::env::args().nth(1).unwrap()).unwrap(), + ) + .unwrap(); + + // Prepare initial KV store with Python code + let mut initial_kv = HashMap::new(); + initial_kv.insert( + b"code".to_vec(), + b"a=10;b='str';f'{a}{b}'".to_vec(), // Python code to execute + ); + + let env = FunctionEnv::new( + &mut store, + Ctx { + kv: initial_kv, + mem: None, + }, + ); + let imports = imports! { + "env" => { + "kv_get" => Function::new_typed_with_env(&mut store, &env, kv_get), + "kv_put" => Function::new_typed_with_env(&mut store, &env, kv_put), + // "get_code" => Function::new_typed_with_env(&mut store, &env, get_code), + "print" => Function::new_typed_with_env(&mut store, &env, print), + } + }; + let inst = Instance::new(&mut store, &module, &imports).unwrap(); + env.as_mut(&mut store).mem = inst.exports.get_memory("memory").ok().cloned(); + let res = inst + .exports + .get_function("eval") + .unwrap() + // TODO: actually pass source code + .call(&mut store, &[wasmer::Value::I32(0), wasmer::Value::I32(0)]) + .unwrap(); + println!( + "Result: {}", + match res[0] { + Value::I32(v) => v, + _ => -1, + } + ); +} From 9134cca17b8f8059bcbd7de6cbed93544146060c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 17 Nov 2025 20:38:19 +0900 Subject: [PATCH 0038/1459] Make CI to run rustpython-without-js test --- .github/workflows/ci.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fe45a1d71bb..95a1ac98d62 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -404,11 +404,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: | From f7ddcd2795ee5997d3ee9ab2cbfdb11385e9802d Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Tue, 18 Nov 2025 18:00:41 +0200 Subject: [PATCH 0039/1459] Break after annotation future found (#6284) --- crates/codegen/src/symboltable.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/codegen/src/symboltable.rs b/crates/codegen/src/symboltable.rs index 0464e09c138..3c8454b9e22 100644 --- a/crates/codegen/src/symboltable.rs +++ b/crates/codegen/src/symboltable.rs @@ -736,12 +736,10 @@ impl SymbolTableBuilder { if let Stmt::ImportFrom(StmtImportFrom { module, names, .. }) = &statement && module.as_ref().map(|id| id.as_str()) == Some("__future__") { - for feature in names { - if &feature.name == "annotations" { - self.future_annotations = true; - } - } + self.future_annotations = + self.future_annotations || names.iter().any(|future| &future.name == "annotations"); } + match &statement { Stmt::Global(StmtGlobal { names, .. }) => { for name in names { From 567fb4dec05fa15b73af26c1cd203039912e0437 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sat, 22 Nov 2025 20:42:24 +0900 Subject: [PATCH 0040/1459] fix(sqlite): raise `ProgrammingError` when operating on a blob with a closed connection, (#6286) Fixed #6285 --- Lib/test/test_sqlite3/test_dbapi.py | 2 -- crates/stdlib/src/sqlite.rs | 11 +++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 49c9764a1b7..a2530a03e2f 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -1469,8 +1469,6 @@ def test_blob_closed(self): with self.assertRaisesRegex(sqlite.ProgrammingError, msg): blob[0] = b"" - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_blob_closed_db_read(self): with memory_database() as cx: cx.execute("create table test(b blob)") diff --git a/crates/stdlib/src/sqlite.rs b/crates/stdlib/src/sqlite.rs index e19aca1f6d5..b875c609449 100644 --- a/crates/stdlib/src/sqlite.rs +++ b/crates/stdlib/src/sqlite.rs @@ -1007,6 +1007,10 @@ mod _sqlite { Ok(()) } + fn is_closed(&self) -> bool { + self.db.lock().is_none() + } + #[pymethod] fn commit(&self, vm: &VirtualMachine) -> PyResult<()> { self.db_lock(vm)?.implicit_commit(vm) @@ -2169,6 +2173,13 @@ mod _sqlite { length: OptionalArg, vm: &VirtualMachine, ) -> PyResult> { + if self.connection.is_closed() { + return Err(new_programming_error( + vm, + "Cannot operate on a closed database".to_owned(), + )); + } + let mut length = length.unwrap_or(-1); let mut inner = self.inner(vm)?; let blob_len = inner.blob.bytes(); From a9469a20d57d809fb1ee04e8d0259f4374b10533 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sat, 22 Nov 2025 22:13:06 +0900 Subject: [PATCH 0041/1459] 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 --- Lib/test/test_sqlite3/test_dbapi.py | 2 - crates/stdlib/src/sqlite.rs | 88 +++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index a2530a03e2f..d8772dfffba 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -573,8 +573,6 @@ def test_connection_reinit(self): self.assertTrue(all(isinstance(r, sqlite.Row) for r in rows)) self.assertEqual([r[0] for r in rows], ["2", "3"]) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_connection_bad_reinit(self): cx = sqlite.connect(":memory:") with cx: diff --git a/crates/stdlib/src/sqlite.rs b/crates/stdlib/src/sqlite.rs index b875c609449..6851462e525 100644 --- a/crates/stdlib/src/sqlite.rs +++ b/crates/stdlib/src/sqlite.rs @@ -833,10 +833,11 @@ mod _sqlite { #[derive(PyPayload)] struct Connection { db: PyMutex>, - detect_types: c_int, + initialized: PyAtomic, + detect_types: PyAtomic, isolation_level: PyAtomicRef>, - check_same_thread: bool, - thread_ident: ThreadId, + check_same_thread: PyAtomic, + thread_ident: PyMutex, // TODO: Use atomic row_factory: PyAtomicRef>, text_factory: PyAtomicRef, } @@ -865,12 +866,15 @@ mod _sqlite { None }; + let initialized = db.is_some(); + let conn = Self { db: PyMutex::new(db), - detect_types: args.detect_types, + initialized: Radium::new(initialized), + detect_types: Radium::new(args.detect_types), isolation_level: PyAtomicRef::from(args.isolation_level), - check_same_thread: args.check_same_thread, - thread_ident: std::thread::current().id(), + check_same_thread: Radium::new(args.check_same_thread), + thread_ident: PyMutex::new(std::thread::current().id()), row_factory: PyAtomicRef::from(None), text_factory: PyAtomicRef::from(text_factory), }; @@ -899,20 +903,51 @@ mod _sqlite { type Args = ConnectArgs; fn init(zelf: PyRef, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> { - let mut guard = zelf.db.lock(); - if guard.is_some() { - // Already initialized - return Ok(()); + let was_initialized = Radium::swap(&zelf.initialized, false, Ordering::AcqRel); + + // Reset factories to their defaults, matching CPython's behavior. + zelf.reset_factories(vm); + + if was_initialized { + zelf.drop_db(); } + // Attempt to open the new database before mutating other state so failures leave + // the connection uninitialized (and subsequent operations raise ProgrammingError). let db = Self::initialize_db(&args, vm)?; + + let ConnectArgs { + detect_types, + isolation_level, + check_same_thread, + .. + } = args; + + zelf.detect_types.store(detect_types, Ordering::Relaxed); + zelf.check_same_thread + .store(check_same_thread, Ordering::Relaxed); + *zelf.thread_ident.lock() = std::thread::current().id(); + let _ = unsafe { zelf.isolation_level.swap(isolation_level) }; + + let mut guard = zelf.db.lock(); *guard = Some(db); + Radium::store(&zelf.initialized, true, Ordering::Release); Ok(()) } } #[pyclass(with(Constructor, Callable, Initializer), flags(BASETYPE))] impl Connection { + fn drop_db(&self) { + self.db.lock().take(); + } + + fn reset_factories(&self, vm: &VirtualMachine) { + let default_text_factory = PyStr::class(&vm.ctx).to_owned().into_object(); + let _ = unsafe { self.row_factory.swap(None) }; + let _ = unsafe { self.text_factory.swap(default_text_factory) }; + } + fn initialize_db(args: &ConnectArgs, vm: &VirtualMachine) -> PyResult { let path = args.database.to_cstring(vm)?; let db = Sqlite::from(SqliteRaw::open(path.as_ptr(), args.uri, vm)?); @@ -1003,7 +1038,7 @@ mod _sqlite { #[pymethod] fn close(&self, vm: &VirtualMachine) -> PyResult<()> { self.check_thread(vm)?; - self.db.lock().take(); + self.drop_db(); Ok(()) } @@ -1450,15 +1485,17 @@ mod _sqlite { } fn check_thread(&self, vm: &VirtualMachine) -> PyResult<()> { - if self.check_same_thread && (std::thread::current().id() != self.thread_ident) { - Err(new_programming_error( - vm, - "SQLite objects created in a thread can only be used in that same thread." - .to_owned(), - )) - } else { - Ok(()) + if self.check_same_thread.load(Ordering::Relaxed) { + let creator_id = *self.thread_ident.lock(); + if std::thread::current().id() != creator_id { + return Err(new_programming_error( + vm, + "SQLite objects created in a thread can only be used in that same thread." + .to_owned(), + )); + } } + Ok(()) } #[pygetset] @@ -1632,7 +1669,8 @@ mod _sqlite { inner.row_cast_map = zelf.build_row_cast_map(&st, vm)?; - inner.description = st.columns_description(zelf.connection.detect_types, vm)?; + let detect_types = zelf.connection.detect_types.load(Ordering::Relaxed); + inner.description = st.columns_description(detect_types, vm)?; if ret == SQLITE_ROW { drop(st); @@ -1680,7 +1718,8 @@ mod _sqlite { )); } - inner.description = st.columns_description(zelf.connection.detect_types, vm)?; + let detect_types = zelf.connection.detect_types.load(Ordering::Relaxed); + inner.description = st.columns_description(detect_types, vm)?; inner.rowcount = if stmt.is_dml { 0 } else { -1 }; @@ -1845,7 +1884,8 @@ mod _sqlite { st: &SqliteStatementRaw, vm: &VirtualMachine, ) -> PyResult>> { - if self.connection.detect_types == 0 { + let detect_types = self.connection.detect_types.load(Ordering::Relaxed); + if detect_types == 0 { return Ok(vec![]); } @@ -1853,7 +1893,7 @@ mod _sqlite { let num_cols = st.column_count(); for i in 0..num_cols { - if self.connection.detect_types & PARSE_COLNAMES != 0 { + if detect_types & PARSE_COLNAMES != 0 { let col_name = st.column_name(i); let col_name = ptr_to_str(col_name, vm)?; let col_name = col_name @@ -1868,7 +1908,7 @@ mod _sqlite { continue; } } - if self.connection.detect_types & PARSE_DECLTYPES != 0 { + if detect_types & PARSE_DECLTYPES != 0 { let decltype = st.column_decltype(i); let decltype = ptr_to_str(decltype, vm)?; if let Some(decltype) = decltype.split_terminator(&[' ', '(']).next() { From f61b62e5a2e126a8cf16793f94e2801a6a47da02 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Mon, 24 Nov 2025 00:04:43 +0900 Subject: [PATCH 0042/1459] Ensure sqlite blob methods respect closed connections (#6290) --- crates/stdlib/src/sqlite.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/stdlib/src/sqlite.rs b/crates/stdlib/src/sqlite.rs index 6851462e525..9fdb1716ac6 100644 --- a/crates/stdlib/src/sqlite.rs +++ b/crates/stdlib/src/sqlite.rs @@ -2207,18 +2207,24 @@ mod _sqlite { self.inner.lock().take(); } + fn ensure_connection_open(&self, vm: &VirtualMachine) -> PyResult<()> { + if self.connection.is_closed() { + Err(new_programming_error( + vm, + "Cannot operate on a closed database".to_owned(), + )) + } else { + Ok(()) + } + } + #[pymethod] fn read( &self, length: OptionalArg, vm: &VirtualMachine, ) -> PyResult> { - if self.connection.is_closed() { - return Err(new_programming_error( - vm, - "Cannot operate on a closed database".to_owned(), - )); - } + self.ensure_connection_open(vm)?; let mut length = length.unwrap_or(-1); let mut inner = self.inner(vm)?; @@ -2245,6 +2251,7 @@ mod _sqlite { #[pymethod] fn write(&self, data: PyBuffer, vm: &VirtualMachine) -> PyResult<()> { + self.ensure_connection_open(vm)?; let mut inner = self.inner(vm)?; let blob_len = inner.blob.bytes(); let length = Self::expect_write(blob_len, data.desc.len, inner.offset, vm)?; @@ -2260,6 +2267,7 @@ mod _sqlite { #[pymethod] fn tell(&self, vm: &VirtualMachine) -> PyResult { + self.ensure_connection_open(vm)?; self.inner(vm).map(|x| x.offset) } @@ -2270,6 +2278,7 @@ mod _sqlite { origin: OptionalArg, vm: &VirtualMachine, ) -> PyResult<()> { + self.ensure_connection_open(vm)?; let origin = origin.unwrap_or(libc::SEEK_SET); let mut inner = self.inner(vm)?; let blob_len = inner.blob.bytes(); @@ -2299,12 +2308,14 @@ mod _sqlite { #[pymethod] fn __enter__(zelf: PyRef, vm: &VirtualMachine) -> PyResult> { + zelf.ensure_connection_open(vm)?; let _ = zelf.inner(vm)?; Ok(zelf) } #[pymethod] fn __exit__(&self, _args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> { + self.ensure_connection_open(vm)?; let _ = self.inner(vm)?; self.close(); Ok(()) @@ -2351,6 +2362,7 @@ mod _sqlite { } fn subscript(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { + self.ensure_connection_open(vm)?; let inner = self.inner(vm)?; if let Some(index) = needle.try_index_opt(vm) { let blob_len = inner.blob.bytes(); @@ -2396,6 +2408,7 @@ mod _sqlite { let Some(value) = value else { return Err(vm.new_type_error("Blob doesn't support slice deletion")); }; + self.ensure_connection_open(vm)?; let inner = self.inner(vm)?; if let Some(index) = needle.try_index_opt(vm) { From 817f91b5bfd314aa44a5caebc2c689c6da53812d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 05:29:53 +0900 Subject: [PATCH 0043/1459] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- crates/stdlib/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f749bc49b5..718631a8d69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4756,9 +4756,9 @@ dependencies = [ [[package]] name = "xml" -version = "1.0.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a4274c410d957424a1502b21126915b45d9956b2f80a88d4f6f906af29facc" +checksum = "2df5825faced2427b2da74d9100f1e2e93c533fff063506a81ede1cf517b2e7e" [[package]] name = "xz2" diff --git a/crates/stdlib/Cargo.toml b/crates/stdlib/Cargo.toml index e62872324ea..dbff752b99c 100644 --- a/crates/stdlib/Cargo.toml +++ b/crates/stdlib/Cargo.toml @@ -51,7 +51,7 @@ base64 = "0.22" csv-core = "0.1.11" dyn-clone = "1.0.10" pymath = { workspace = true } -xml = "1.0" +xml = "1.2" # random rand_core = { workspace = true } From ea3eb2a9efac84018f390ecf3af29d5ab7ec0bd3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 05:30:13 +0900 Subject: [PATCH 0044/1459] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 14 +++++++------- .github/workflows/cron-ci.yaml | 8 ++++---- .github/workflows/pr-auto-commit.yaml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/update-doc-db.yml | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 95a1ac98d62..23d16ffadb9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -120,7 +120,7 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -179,7 +179,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: target: i686-unknown-linux-gnu @@ -246,7 +246,7 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] fail-fast: false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: actions/setup-python@v6 @@ -311,7 +311,7 @@ jobs: name: Check Rust code with clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy @@ -350,7 +350,7 @@ jobs: env: NIGHTLY_CHANNEL: nightly steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: @@ -372,7 +372,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 @@ -435,7 +435,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: target: wasm32-wasip1 diff --git a/.github/workflows/cron-ci.yaml b/.github/workflows/cron-ci.yaml index a94c8df9e23..e10c5ac0d1f 100644 --- a/.github/workflows/cron-ci.yaml +++ b/.github/workflows/cron-ci.yaml @@ -21,7 +21,7 @@ jobs: # 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@v6 - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@cargo-llvm-cov - uses: actions/setup-python@v6 @@ -49,7 +49,7 @@ jobs: # 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@v6 - uses: dtolnay/rust-toolchain@stable - name: build rustpython run: cargo build --release --verbose @@ -80,7 +80,7 @@ jobs: # 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@v6 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v6 with: @@ -137,7 +137,7 @@ jobs: # 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@v6 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v6 with: diff --git a/.github/workflows/pr-auto-commit.yaml b/.github/workflows/pr-auto-commit.yaml index 8546c2abe51..62f9eb9c332 100644 --- a/.github/workflows/pr-auto-commit.yaml +++ b/.github/workflows/pr-auto-commit.yaml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout PR branch - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ github.event.pull_request.head.ref }} repository: ${{ github.event.pull_request.head.repo.full_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 43286780ef7..a69169c3234 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: # target: aarch64-pc-windows-msvc fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: cargo-bins/cargo-binstall@main @@ -93,7 +93,7 @@ jobs: # 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@v6 - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-wasip1 diff --git a/.github/workflows/update-doc-db.yml b/.github/workflows/update-doc-db.yml index dcef94f20d2..0f84b8d23a9 100644 --- a/.github/workflows/update-doc-db.yml +++ b/.github/workflows/update-doc-db.yml @@ -26,7 +26,7 @@ jobs: - windows-latest - macos-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false sparse-checkout: | @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-latest needs: generate steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false sparse-checkout: | From 1e7a49036a9e6f22867511b553ffcb76be62ac5f Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:04:30 +0900 Subject: [PATCH 0045/1459] try auto-format again (#6295) --- .github/workflows/pr-auto-commit.yaml | 60 ++++++++------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/.github/workflows/pr-auto-commit.yaml b/.github/workflows/pr-auto-commit.yaml index 62f9eb9c332..118f7bcd4d4 100644 --- a/.github/workflows/pr-auto-commit.yaml +++ b/.github/workflows/pr-auto-commit.yaml @@ -1,4 +1,4 @@ -name: PR Auto-format +name: Auto-format PR # This workflow triggers when a PR is opened/updated on: @@ -9,42 +9,26 @@ on: - release concurrency: - group: pr-fmt-${{ github.event.pull_request.number }} + group: auto-format-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: auto_format: - if: | - !contains(github.event.pull_request.labels.*.name, 'skip:ci') && - !contains(github.event.pull_request.head.sha, '[skip ci]') + if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip:ci') }} permissions: contents: write pull-requests: write - checks: read runs-on: ubuntu-latest timeout-minutes: 60 - steps: - name: Checkout PR branch uses: actions/checkout@v6 with: - ref: ${{ github.event.pull_request.head.ref }} + ref: ${{ github.event.pull_request.head.sha }} repository: ${{ github.event.pull_request.head.repo.full_name }} - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.AUTO_COMMIT_PAT }} fetch-depth: 0 - # Wait for all PR check runs to complete - - name: Wait for all checks to complete - uses: poseidon/wait-for-status-checks@v0.6.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - delay: 60 - interval: 30 - timeout: 7200 - - - name: CI completed successfully - run: echo "CI workflow completed successfully - proceeding with auto-format" - - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: @@ -55,8 +39,13 @@ jobs: echo "Running cargo fmt --all on PR #${{ github.event.pull_request.number }}" cargo fmt --all - - name: Check for formatting changes - id: check_changes + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Check for changes + id: check-changes run: | if [ -n "$(git status --porcelain)" ]; then echo "has_changes=true" >> $GITHUB_OUTPUT @@ -65,35 +54,22 @@ jobs: fi - name: Commit and push formatting changes - if: steps.check_changes.outputs.has_changes == 'true' + if: steps.check-changes.outputs.has_changes == 'true' run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add -u - git commit -m "Auto-format code [skip ci]" - + git commit -m "Auto-format: cargo fmt --all" git push origin HEAD:${{ github.event.pull_request.head.ref }} - name: Comment on PR - if: steps.check_changes.outputs.has_changes == 'true' + if: steps.check-changes.outputs.has_changes == 'true' uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ github.event.pull_request.number }} message: | **Code has been automatically formatted** - - The code in this PR has been formatted using `cargo fmt`. - The changes have been committed with `[skip ci]` to avoid triggering another CI run. - - **Triggered by commit:** `${{ github.event.pull_request.head.sha }}` - **Last formatted:** ${{ github.event.pull_request.updated_at }} - - You may need to pull the latest changes before pushing again: + + The code in this PR has been formatted using `cargo fmt --all`. + Please pull the latest changes before pushing again: ```bash git pull origin ${{ github.event.pull_request.head.ref }} ``` - - - name: No formatting needed - if: steps.check_changes.outputs.has_changes == 'false' - run: echo "Code is already properly formatted" From e733b7ecf99a2d3f255bc775d0f1df213c0db2fe Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 27 Nov 2025 17:15:02 +0200 Subject: [PATCH 0046/1459] Update `libc` to 0.2.177 --- Cargo.lock | 303 +++++++++++++++++++++++++---------------------------- Cargo.toml | 2 +- 2 files changed, 144 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 718631a8d69..f96b527dfe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -100,22 +100,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -231,9 +231,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-fips-sys" -version = "0.13.9" +version = "0.13.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede71ad84efb06d748d9af3bc500b14957a96282a69a6833b1420dcacb411cc3" +checksum = "57900537c00a0565a35b63c4c281b372edfc9744b072fd4a3b414350a8f5ed48" dependencies = [ "bindgen 0.72.1", "cc", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +checksum = "6b5ce75405893cd713f9ab8e297d8e438f624dde7d706108285f7e17a25a180f" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -257,11 +257,10 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.32.3" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" +checksum = "179c3777a8b5e70e90ea426114ffc565b2c1a9f82f6c4a0c5a34aa6ef5e781b6" dependencies = [ - "bindgen 0.72.1", "cc", "cmake", "dunce", @@ -286,7 +285,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -306,7 +305,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -328,9 +327,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "blake2" @@ -361,9 +360,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", @@ -435,9 +434,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" dependencies = [ "find-msvc-tools", "jobserver", @@ -535,18 +534,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.49" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.49" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstyle", "clap_lex", @@ -926,9 +925,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1052,9 +1051,9 @@ dependencies = [ [[package]] name = "dns-lookup" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853d5bcf0b73bd5e6d945b976288621825c7166e9f06c5a035ae1aaf42d1b64f" +checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d" dependencies = [ "cfg-if", "libc", @@ -1174,9 +1173,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flagset" @@ -1222,9 +1221,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "libz-rs-sys", @@ -1260,9 +1259,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1270,9 +1269,9 @@ dependencies = [ [[package]] name = "get-size-derive2" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3814abc7da8ab18d2fd820f5b540b5e39b6af0a32de1bdd7c47576693074843" +checksum = "ff47daa61505c85af126e9dd64af6a342a33dc0cccfe1be74ceadc7d352e6efd" dependencies = [ "attribute-derive", "quote", @@ -1281,13 +1280,13 @@ dependencies = [ [[package]] name = "get-size2" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfe2cec5b5ce8fb94dcdb16a1708baa4d0609cc3ce305ca5d3f6f2ffb59baed" +checksum = "ac7bb8710e1f09672102be7ddf39f764d8440ae74a9f4e30aaa4820dcdffa4af" dependencies = [ "compact_str", "get-size-derive2", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "smallvec", ] @@ -1374,9 +1373,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1413,11 +1412,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1446,19 +1445,22 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inout" @@ -1472,9 +1474,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.43.2" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0" +checksum = "dfd3461e1f00283105bdf97c3a1aca2b3f8456eb809a96938d2b190cd4dbc6d2" dependencies = [ "console", "once_cell", @@ -1501,9 +1503,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -1531,22 +1533,22 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -1587,9 +1589,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -1710,7 +1712,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", ] @@ -1951,7 +1953,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -1964,7 +1966,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -2036,9 +2038,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -2046,9 +2048,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro2", "quote", @@ -2072,9 +2074,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -2084,11 +2086,11 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" -version = "0.10.74" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2116,18 +2118,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.3+3.5.4" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.110" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2419,9 +2421,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -2498,9 +2500,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -2643,7 +2645,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] @@ -2753,7 +2755,7 @@ version = "0.0.0" source = "git+https://github.com/astral-sh/ruff.git?tag=0.14.1#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ "aho-corasick", - "bitflags 2.9.4", + "bitflags 2.10.0", "compact_str", "get-size2", "is-macro", @@ -2771,7 +2773,7 @@ name = "ruff_python_parser" version = "0.0.0" source = "git+https://github.com/astral-sh/ruff.git?tag=0.14.1#2bffef59665ce7d2630dfd72ee99846663660db8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bstr", "compact_str", "get-size2", @@ -2835,7 +2837,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", @@ -2953,7 +2955,7 @@ name = "rustpython-codegen" version = "0.4.0" dependencies = [ "ahash", - "bitflags 2.9.4", + "bitflags 2.10.0", "indexmap", "insta", "itertools 0.14.0", @@ -2977,7 +2979,7 @@ name = "rustpython-common" version = "0.4.0" dependencies = [ "ascii", - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "getrandom 0.3.4", "itertools 0.14.0", @@ -3016,7 +3018,7 @@ dependencies = [ name = "rustpython-compiler-core" version = "0.4.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "itertools 0.14.0", "lz4_flex", "malachite-bigint", @@ -3105,7 +3107,7 @@ dependencies = [ name = "rustpython-sre_engine" version = "0.4.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "criterion", "num_enum", "optional", @@ -3208,7 +3210,7 @@ version = "0.4.0" dependencies = [ "ahash", "ascii", - "bitflags 2.9.4", + "bitflags 2.10.0", "bstr", "caseless", "cfg-if", @@ -3319,7 +3321,7 @@ version = "17.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "clipboard-win", "fd-lock", @@ -3406,7 +3408,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -3479,11 +3481,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -3634,9 +3636,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.107" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -3671,7 +3673,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3870,44 +3872,42 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_writer" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "twox-hash" @@ -4055,15 +4055,15 @@ checksum = "061dbb8cc7f108532b6087a0065eff575e892a4bcb503dc57323a197457cc202" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] @@ -4196,9 +4196,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -4207,25 +4207,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -4236,9 +4222,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4246,22 +4232,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -4280,9 +4266,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -4684,12 +4670,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" [[package]] name = "winreg" @@ -4703,9 +4686,9 @@ dependencies = [ [[package]] name = "winresource" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcacf11b6f48dd21b9ba002f991bdd5de29b2da8cc2800412f4b80f677e4957" +checksum = "f1ef04dd590e94ff7431a8eda99d5ca659e688d60e930bd0a330062acea4608f" dependencies = [ "toml", "version_check", @@ -4771,18 +4754,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 53d1eb6ea2d..8993ce145ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,7 +175,7 @@ insta = "1.42" itertools = "0.14.0" is-macro = "0.3.7" junction = "1.3.0" -libc = "0.2.169" +libc = "0.2.177" libffi = "4.1" log = "0.4.28" nix = { version = "0.30", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] } From 081a8f0451317f776388c5160d67db51fd2fa1bd Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 27 Nov 2025 17:16:42 +0200 Subject: [PATCH 0047/1459] Regenerate libc constatnts --- crates/vm/src/stdlib/posix.rs | 6 +++--- scripts/libc_posix.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/vm/src/stdlib/posix.rs b/crates/vm/src/stdlib/posix.rs index e8a01d0e687..071f93d7ee0 100644 --- a/crates/vm/src/stdlib/posix.rs +++ b/crates/vm/src/stdlib/posix.rs @@ -202,8 +202,7 @@ pub mod module { #[pyattr] use libc::{ CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED, F_LOCK, - F_TEST, F_TLOCK, F_ULOCK, O_NDELAY, O_NOCTTY, O_SYNC, P_ALL, P_PGID, P_PID, SCHED_FIFO, - SCHED_RR, + F_TEST, F_TLOCK, F_ULOCK, O_SYNC, P_ALL, P_PGID, P_PID, SCHED_FIFO, SCHED_RR, }; #[cfg(any( @@ -217,7 +216,8 @@ pub mod module { target_os = "redox" ))] #[pyattr] - use libc::{O_ASYNC, WEXITED, WNOWAIT, WSTOPPED}; + use libc::{O_ASYNC, O_NDELAY, O_NOCTTY, WEXITED, WNOWAIT, WSTOPPED}; + #[pyattr] const EX_OK: i8 = exitcode::OK as i8; diff --git a/scripts/libc_posix.py b/scripts/libc_posix.py index 9ed6890e70f..73f082a0658 100644 --- a/scripts/libc_posix.py +++ b/scripts/libc_posix.py @@ -13,7 +13,7 @@ ) # TODO: Exclude matches if they have `(` after (those are functions) -LIBC_VERSION = "0.2.175" +LIBC_VERSION = "0.2.177" EXCLUDE = frozenset( { From 1b3261a090b4331cfcc60e8bad2ced92f0f8fb28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 08:39:46 +0900 Subject: [PATCH 0048/1459] 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] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- wasm/demo/package-lock.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/wasm/demo/package-lock.json b/wasm/demo/package-lock.json index e3cf79298ac..3acc105e3c3 100644 --- a/wasm/demo/package-lock.json +++ b/wasm/demo/package-lock.json @@ -775,7 +775,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", @@ -831,6 +832,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -1149,6 +1151,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -3495,9 +3498,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -3824,6 +3827,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -5082,7 +5086,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/type-fest": { "version": "2.19.0", @@ -5266,6 +5271,7 @@ "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", @@ -5313,6 +5319,7 @@ "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.6.1", "@webpack-cli/configtest": "^3.0.1", From 0e6e256f8e1448411d7c86d5fa23ec3fe72f4296 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Fri, 28 Nov 2025 06:41:22 +0700 Subject: [PATCH 0049/1459] Fix redox compilation in stdlib (#6298) --- crates/stdlib/src/posixsubprocess.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/stdlib/src/posixsubprocess.rs b/crates/stdlib/src/posixsubprocess.rs index 7f418c89931..4da6a6858dd 100644 --- a/crates/stdlib/src/posixsubprocess.rs +++ b/crates/stdlib/src/posixsubprocess.rs @@ -441,15 +441,14 @@ fn close_dir_fds(keep: KeepFds<'_>) -> nix::Result<()> { fn close_filetable_fds(keep: KeepFds<'_>) -> nix::Result<()> { use nix::fcntl; use std::os::fd::{FromRawFd, OwnedFd}; - let fd = fcntl::open( + let filetable = fcntl::open( c"/scheme/thisproc/current/filetable", fcntl::OFlag::O_RDONLY, nix::sys::stat::Mode::empty(), )?; - let filetable = unsafe { OwnedFd::from_raw_fd(fd) }; let read_one = || -> nix::Result<_> { let mut byte = 0; - let n = nix::unistd::read(filetable.as_raw_fd(), std::slice::from_mut(&mut byte))?; + let n = nix::unistd::read(&filetable, std::slice::from_mut(&mut byte))?; Ok((n > 0).then_some(byte)) }; while let Some(c) = read_one()? { From f37ea525650d975a8a87fa8052e1785612684ab1 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Mon, 31 Mar 2025 21:42:54 -0700 Subject: [PATCH 0050/1459] 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 --- crates/vm/src/stdlib/ctypes.rs | 11 +- crates/vm/src/stdlib/ctypes/array.rs | 1 + crates/vm/src/stdlib/ctypes/base.rs | 22 +- crates/vm/src/stdlib/ctypes/field.rs | 134 ++++++++ crates/vm/src/stdlib/ctypes/function.rs | 386 +++++++++++++++--------- crates/vm/src/stdlib/ctypes/pointer.rs | 41 ++- crates/vm/src/stdlib/ctypes/thunk.rs | 22 ++ crates/vm/src/stdlib/ctypes/util.rs | 24 ++ extra_tests/snippets/stdlib_ctypes.py | 11 +- 9 files changed, 491 insertions(+), 161 deletions(-) create mode 100644 crates/vm/src/stdlib/ctypes/field.rs create mode 100644 crates/vm/src/stdlib/ctypes/thunk.rs create mode 100644 crates/vm/src/stdlib/ctypes/util.rs diff --git a/crates/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs index 8ea4dd165eb..ac74418354e 100644 --- a/crates/vm/src/stdlib/ctypes.rs +++ b/crates/vm/src/stdlib/ctypes.rs @@ -2,11 +2,14 @@ pub(crate) mod array; pub(crate) mod base; +pub(crate) mod field; pub(crate) mod function; pub(crate) mod library; pub(crate) mod pointer; pub(crate) mod structure; +pub(crate) mod thunk; pub(crate) mod union; +pub(crate) mod util; use crate::builtins::PyModule; use crate::class::PyClassImpl; @@ -17,14 +20,18 @@ pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py) { let ctx = &vm.ctx; PyCSimpleType::make_class(ctx); array::PyCArrayType::make_class(ctx); + field::PyCFieldType::make_class(ctx); + pointer::PyCPointerType::make_class(ctx); extend_module!(vm, module, { "_CData" => PyCData::make_class(ctx), "_SimpleCData" => PyCSimple::make_class(ctx), "Array" => array::PyCArray::make_class(ctx), + "CField" => field::PyCField::make_class(ctx), "CFuncPtr" => function::PyCFuncPtr::make_class(ctx), "_Pointer" => pointer::PyCPointer::make_class(ctx), "_pointer_type_cache" => ctx.new_dict(), "Structure" => structure::PyCStructure::make_class(ctx), + "CThunkObject" => thunk::PyCThunk::make_class(ctx), "Union" => union::PyCUnion::make_class(ctx), }) } @@ -207,7 +214,9 @@ pub(crate) mod _ctypes { // TODO: load_flags let cache = library::libcache(); let mut cache_write = cache.write(); - let (id, _) = cache_write.get_or_insert_lib(&name, vm).unwrap(); + let (id, _) = cache_write + .get_or_insert_lib(&name, vm) + .map_err(|e| vm.new_os_error(e.to_string()))?; Ok(id) } diff --git a/crates/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs index 5290ec42f37..a1adf847a99 100644 --- a/crates/vm/src/stdlib/ctypes/array.rs +++ b/crates/vm/src/stdlib/ctypes/array.rs @@ -106,6 +106,7 @@ impl PyCArray { } impl PyCArray { + #[allow(unused)] pub fn to_arg(&self, _vm: &VirtualMachine) -> PyResult { let value = self.value.read(); let py_bytes = value.downcast_ref::().unwrap(); diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index 23cb505adaf..f5a25ad740b 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -276,25 +276,25 @@ impl PyCSimple { let value = unsafe { (*self.value.as_ptr()).clone() }; if let Ok(i) = value.try_int(vm) { let i = i.as_bigint(); - if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u8().as_raw_ptr()) { - return i.to_u8().map(|r: u8| libffi::middle::Arg::new(&r)); + return if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u8().as_raw_ptr()) { + i.to_u8().map(|r: u8| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i8().as_raw_ptr()) { - return i.to_i8().map(|r: i8| libffi::middle::Arg::new(&r)); + i.to_i8().map(|r: i8| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u16().as_raw_ptr()) { - return i.to_u16().map(|r: u16| libffi::middle::Arg::new(&r)); + i.to_u16().map(|r: u16| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i16().as_raw_ptr()) { - return i.to_i16().map(|r: i16| libffi::middle::Arg::new(&r)); + i.to_i16().map(|r: i16| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u32().as_raw_ptr()) { - return i.to_u32().map(|r: u32| libffi::middle::Arg::new(&r)); + i.to_u32().map(|r: u32| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i32().as_raw_ptr()) { - return i.to_i32().map(|r: i32| libffi::middle::Arg::new(&r)); + i.to_i32().map(|r: i32| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::u64().as_raw_ptr()) { - return i.to_u64().map(|r: u64| libffi::middle::Arg::new(&r)); + i.to_u64().map(|r: u64| libffi::middle::Arg::new(&r)) } else if std::ptr::eq(ty.as_raw_ptr(), libffi::middle::Type::i64().as_raw_ptr()) { - return i.to_i64().map(|r: i64| libffi::middle::Arg::new(&r)); + i.to_i64().map(|r: i64| libffi::middle::Arg::new(&r)) } else { - return None; - } + None + }; } if let Ok(_f) = value.try_float(vm) { todo!(); diff --git a/crates/vm/src/stdlib/ctypes/field.rs b/crates/vm/src/stdlib/ctypes/field.rs new file mode 100644 index 00000000000..b50b8b54ace --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/field.rs @@ -0,0 +1,134 @@ +use crate::builtins::PyType; +use crate::builtins::PyTypeRef; +use crate::stdlib::ctypes::PyCData; +use crate::types::Constructor; +use crate::types::Representable; +use crate::{Py, PyResult, VirtualMachine}; + +#[pyclass(name = "PyCFieldType", base = PyType, module = "_ctypes")] +#[derive(PyPayload, Debug)] +pub struct PyCFieldType { + pub(super) inner: PyCField, +} + +#[pyclass] +impl PyCFieldType {} + +#[pyclass( + name = "CField", + base = PyCData, + metaclass = "PyCFieldType", + module = "_ctypes" +)] +#[derive(Debug, PyPayload)] +pub struct PyCField { + byte_offset: usize, + byte_size: usize, + #[allow(unused)] + index: usize, + proto: PyTypeRef, + anonymous: bool, + bitfield_size: bool, + bit_offset: u8, + name: String, +} + +impl Representable for PyCField { + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + let tp_name = zelf.proto.name().to_string(); + if zelf.bitfield_size != false { + Ok(format!( + "<{} type={}, ofs={byte_offset}, bit_size={bitfield_size}, bit_offset={bit_offset}", + zelf.name, + tp_name, + byte_offset = zelf.byte_offset, + bitfield_size = zelf.bitfield_size, + bit_offset = zelf.bit_offset + )) + } else { + Ok(format!( + "<{} type={tp_name}, ofs={}, size={}", + zelf.name, zelf.byte_offset, zelf.byte_size + )) + } + } +} + +#[derive(Debug, FromArgs)] +pub struct PyCFieldConstructorArgs { + // PyObject *name, PyObject *proto, + // Py_ssize_t byte_size, Py_ssize_t byte_offset, + // Py_ssize_t index, int _internal_use, + // PyObject *bit_size_obj, PyObject *bit_offset_obj +} + +impl Constructor for PyCField { + type Args = PyCFieldConstructorArgs; + + fn py_new(_cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot instantiate a PyCField".to_string())) + } +} + +#[pyclass(flags(BASETYPE, IMMUTABLETYPE), with(Constructor, Representable))] +impl PyCField { + #[pygetset] + fn size(&self) -> usize { + self.byte_size + } + + #[pygetset] + fn bit_size(&self) -> bool { + self.bitfield_size + } + + #[pygetset] + fn is_bitfield(&self) -> bool { + self.bitfield_size + } + + #[pygetset] + fn is_anonymous(&self) -> bool { + self.anonymous + } + + #[pygetset] + fn name(&self) -> String { + self.name.clone() + } + + #[pygetset(name = "type")] + fn type_(&self) -> PyTypeRef { + self.proto.clone() + } + + #[pygetset] + fn offset(&self) -> usize { + self.byte_offset + } + + #[pygetset] + fn byte_offset(&self) -> usize { + self.byte_offset + } + + #[pygetset] + fn byte_size(&self) -> usize { + self.byte_size + } + + #[pygetset] + fn bit_offset(&self) -> u8 { + self.bit_offset + } +} + +#[inline(always)] +pub const fn low_bit(offset: usize) -> usize { + offset & 0xFFFF +} + +#[inline(always)] +pub const fn high_bit(offset: usize) -> usize { + offset >> 16 +} diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 6703dcc0f52..88d0fbb35ea 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -1,142 +1,117 @@ // spell-checker:disable -use crate::builtins::{PyStr, PyTupleRef, PyTypeRef}; +use crate::builtins::{PyNone, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef}; use crate::convert::ToPyObject; use crate::function::FuncArgs; use crate::stdlib::ctypes::PyCData; -use crate::stdlib::ctypes::array::PyCArray; use crate::stdlib::ctypes::base::{PyCSimple, ffi_type_from_str}; +use crate::types::Representable; use crate::types::{Callable, Constructor}; -use crate::{Py, PyObjectRef, PyResult, VirtualMachine}; +use crate::{AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use libffi::middle::{Arg, Cif, CodePtr, Type}; use libloading::Symbol; use num_traits::ToPrimitive; use rustpython_common::lock::PyRwLock; -use std::ffi::CString; +use std::ffi::{self, CString, c_void}; use std::fmt::Debug; -// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 +// See also: https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 -#[derive(Debug)] -pub struct Function { - args: Vec, - // TODO: no protection from use-after-free - pointer: CodePtr, - cif: Cif, +type FP = unsafe extern "C" fn(); + +pub trait ArgumentType { + fn to_ffi_type(&self, vm: &VirtualMachine) -> PyResult; + fn convert_object(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult; } -unsafe impl Send for Function {} -unsafe impl Sync for Function {} +impl ArgumentType for PyTypeRef { + fn to_ffi_type(&self, vm: &VirtualMachine) -> PyResult { + let typ = self + .get_class_attr(vm.ctx.intern_str("_type_")) + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + let typ = typ + .downcast_ref::() + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + let typ = typ.to_string(); + let typ = typ.as_str(); + let converted_typ = ffi_type_from_str(typ); + if let Some(typ) = converted_typ { + Ok(typ) + } else { + Err(vm.new_type_error(format!("Unsupported argument type: {}", typ))) + } + } -type FP = unsafe extern "C" fn(); + fn convert_object(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // if self.fast_isinstance::(vm) { + // let array = value.downcast::()?; + // return Ok(Arg::from(array.as_ptr())); + // } + if let Ok(simple) = value.downcast::() { + let typ = ArgumentType::to_ffi_type(self, vm)?; + let arg = simple + .to_arg(typ, vm) + .ok_or(vm.new_type_error("Unsupported argument type".to_string()))?; + return Ok(arg); + } + Err(vm.new_type_error("Unsupported argument type".to_string())) + } +} -impl Function { - pub unsafe fn load( - library: &libloading::Library, - function: &str, - args: &[PyObjectRef], - ret_type: &Option, +pub trait ReturnType { + fn to_ffi_type(&self) -> Option; + fn from_ffi_type( + &self, + value: *mut ffi::c_void, vm: &VirtualMachine, - ) -> PyResult { - // map each arg to a PyCSimple - let args = args - .iter() - .map(|arg| { - if let Some(arg) = arg.downcast_ref::() { - let converted = ffi_type_from_str(&arg._type_); - return match converted { - Some(t) => Ok(t), - None => Err(vm.new_type_error("Invalid type")), // TODO: add type name - }; - } - if let Some(arg) = arg.downcast_ref::() { - let t = arg.typ.read(); - let ty_attributes = t.attributes.read(); - let ty_pystr = ty_attributes - .get(vm.ctx.intern_str("_type_")) - .ok_or_else(|| vm.new_type_error("Expected a ctypes simple type"))?; - let ty_str = ty_pystr - .downcast_ref::() - .ok_or_else(|| vm.new_type_error("Expected a ctypes simple type"))? - .to_string(); - let converted = ffi_type_from_str(&ty_str); - match converted { - Some(_t) => { - // TODO: Use - Ok(Type::void()) - } - None => Err(vm.new_type_error("Invalid type")), // TODO: add type name - } - } else { - Err(vm.new_type_error("Expected a ctypes simple type")) - } - }) - .collect::>>()?; - let c_function_name = CString::new(function) - .map_err(|_| vm.new_value_error("Function name contains null bytes"))?; - let pointer: Symbol<'_, FP> = unsafe { - library - .get(c_function_name.as_bytes()) - .map_err(|err| err.to_string()) - .map_err(|err| vm.new_attribute_error(err))? - }; - let code_ptr = CodePtr(*pointer as *mut _); - let return_type = match ret_type { - // TODO: Fix this - Some(_t) => { - return Err(vm.new_not_implemented_error("Return type not implemented")); - } - None => Type::c_int(), - }; - let cif = Cif::new(args.clone(), return_type); - Ok(Function { - args, - cif, - pointer: code_ptr, - }) + ) -> PyResult>; +} + +impl ReturnType for PyTypeRef { + fn to_ffi_type(&self) -> Option { + ffi_type_from_str(self.name().to_string().as_str()) } - pub unsafe fn call( + fn from_ffi_type( &self, - args: Vec, - vm: &VirtualMachine, - ) -> PyResult { - let args = args - .into_iter() - .enumerate() - .map(|(count, arg)| { - // none type check - if let Some(d) = arg.downcast_ref::() { - return Ok(d.to_arg(self.args[count].clone(), vm).unwrap()); - } - if let Some(d) = arg.downcast_ref::() { - return Ok(d.to_arg(vm).unwrap()); - } - Err(vm.new_type_error("Expected a ctypes simple type")) - }) - .collect::>>()?; - // TODO: FIX return - let result: i32 = unsafe { self.cif.call(self.pointer, &args) }; - Ok(vm.ctx.new_int(result).into()) + _value: *mut ffi::c_void, + _vm: &VirtualMachine, + ) -> PyResult> { + todo!() + } +} + +impl ReturnType for PyNone { + fn to_ffi_type(&self) -> Option { + ffi_type_from_str("void") + } + + fn from_ffi_type( + &self, + _value: *mut ffi::c_void, + _vm: &VirtualMachine, + ) -> PyResult> { + Ok(None) } } #[pyclass(module = "_ctypes", name = "CFuncPtr", base = PyCData)] #[derive(PyPayload)] pub struct PyCFuncPtr { - pub name: PyRwLock, - pub _flags_: AtomicCell, - // FIXME(arihant2math): This shouldn't be an option, setting the default as the none type should work - // This is a workaround for now and I'll fix it later - pub _restype_: PyRwLock>, + pub name: PyRwLock>, + pub ptr: PyRwLock>, + pub needs_free: AtomicCell, + pub arg_types: PyRwLock>>, + pub res_type: PyRwLock>, + pub _flags_: AtomicCell, pub handler: PyObjectRef, } impl Debug for PyCFuncPtr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PyCFuncPtr") - .field("name", &self.name) + .field("flags", &self._flags_) .finish() } } @@ -156,10 +131,43 @@ impl Constructor for PyCFuncPtr { .nth(1) .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? .clone(); + let handle = handler.try_int(vm); + let handle = match handle { + Ok(handle) => handle.as_bigint().clone(), + Err(_) => handler + .get_attr("_handle", vm)? + .try_int(vm)? + .as_bigint() + .clone(), + }; + let library_cache = crate::stdlib::ctypes::library::libcache().read(); + let library = library_cache + .get_lib( + handle + .to_usize() + .ok_or(vm.new_value_error("Invalid handle".to_string()))?, + ) + .ok_or_else(|| vm.new_value_error("Library not found".to_string()))?; + let inner_lib = library.lib.lock(); + + let terminated = format!("{}\0", &name); + let code_ptr = if let Some(lib) = &*inner_lib { + let pointer: Symbol<'_, FP> = unsafe { + lib.get(terminated.as_bytes()) + .map_err(|err| err.to_string()) + .map_err(|err| vm.new_attribute_error(err))? + }; + Some(CodePtr(*pointer as *mut _)) + } else { + None + }; Ok(Self { + ptr: PyRwLock::new(code_ptr), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), _flags_: AtomicCell::new(0), - name: PyRwLock::new(name), - _restype_: PyRwLock::new(None), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(name)), handler, } .to_pyobject(vm)) @@ -169,53 +177,145 @@ impl Constructor for PyCFuncPtr { impl Callable for PyCFuncPtr { type Args = FuncArgs; fn call(zelf: &Py, args: Self::Args, vm: &VirtualMachine) -> PyResult { - unsafe { - let handle = zelf.handler.get_attr("_handle", vm)?; - let handle = handle.try_int(vm)?.as_bigint().clone(); - let library_cache = crate::stdlib::ctypes::library::libcache().read(); - let library = library_cache - .get_lib( - handle - .to_usize() - .ok_or(vm.new_value_error("Invalid handle"))?, - ) - .ok_or_else(|| vm.new_value_error("Library not found"))?; - let inner_lib = library.lib.lock(); - let name = zelf.name.read(); - let res_type = zelf._restype_.read(); - let func = Function::load( - inner_lib - .as_ref() - .ok_or_else(|| vm.new_value_error("Library not found"))?, - &name, - &args.args, - &res_type, - vm, - )?; - func.call(args.args, vm) + // This is completely seperate from the C python implementation + + // Cif init + let arg_types: Vec<_> = match zelf.arg_types.read().clone() { + Some(tys) => tys, + None => args + .args + .clone() + .into_iter() + .map(|a| a.class().as_object().to_pyobject(vm).downcast().unwrap()) + .collect(), + }; + let ffi_arg_types = arg_types + .clone() + .iter() + .map(|t| ArgumentType::to_ffi_type(t, vm)) + .collect::>>()?; + let return_type = zelf.res_type.read(); + let ffi_return_type = return_type + .as_ref() + .map(|t| ReturnType::to_ffi_type(&t.clone().downcast::().unwrap())) + .flatten() + .unwrap_or_else(|| Type::i32()); + let cif = Cif::new(ffi_arg_types, ffi_return_type); + + // Call the function + let ffi_args = args + .args + .into_iter() + .enumerate() + .map(|(n, arg)| { + let arg_type = arg_types + .get(n) + .ok_or_else(|| vm.new_type_error("argument amount mismatch".to_string()))?; + arg_type.convert_object(arg, vm) + }) + .collect::, _>>()?; + let pointer = zelf.ptr.read(); + let code_ptr = pointer + .as_ref() + .ok_or_else(|| vm.new_type_error("Function pointer not set".to_string()))?; + let mut output: c_void = unsafe { cif.call(*code_ptr, &ffi_args) }; + let return_type = return_type + .as_ref() + .map(|f| { + f.clone() + .downcast::() + .unwrap() + .from_ffi_type(&mut output, vm) + .ok() + .flatten() + }) + .unwrap_or_else(|| Some(vm.ctx.new_int(output as i32).as_object().to_pyobject(vm))); + if let Some(return_type) = return_type { + Ok(return_type) + } else { + Ok(vm.ctx.none()) } } } -#[pyclass(flags(BASETYPE), with(Callable, Constructor))] -impl PyCFuncPtr { - #[pygetset] - fn __name__(&self) -> String { - self.name.read().clone() +impl Representable for PyCFuncPtr { + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + let index = zelf.ptr.read(); + let index = index.map(|ptr| ptr.0 as usize).unwrap_or(0); + let type_name = zelf.class().name(); + #[cfg(windows)] + { + let index = index - 0x1000; + return Ok(format!("")); + } + Ok(format!("<{type_name} object at {index:#x}>")) } +} - #[pygetset(setter)] - fn set___name__(&self, name: String) { - *self.name.write() = name; - } +// TODO: fix +unsafe impl Send for PyCFuncPtr {} +unsafe impl Sync for PyCFuncPtr {} +#[pyclass(flags(BASETYPE), with(Callable, Constructor, Representable))] +impl PyCFuncPtr { #[pygetset(name = "_restype_")] - fn restype(&self) -> Option { - self._restype_.read().as_ref().cloned() + fn restype(&self) -> Option { + self.res_type.read().as_ref().cloned() } #[pygetset(name = "_restype_", setter)] - fn set_restype(&self, restype: PyTypeRef) { - *self._restype_.write() = Some(restype); + fn set_restype(&self, restype: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + // has to be type, callable, or none + // TODO: Callable support + if vm.is_none(&restype) || restype.downcast_ref::().is_some() { + *self.res_type.write() = Some(restype); + Ok(()) + } else { + Err(vm.new_type_error("restype must be a type, a callable, or None".to_string())) + } + } + + #[pygetset(name = "argtypes")] + fn argtypes(&self, vm: &VirtualMachine) -> PyTupleRef { + PyTuple::new_ref( + self.arg_types + .read() + .clone() + .unwrap_or_default() + .into_iter() + .map(|t| t.to_pyobject(vm)) + .collect(), + &vm.ctx, + ) + } + + #[pygetset(name = "argtypes", setter)] + fn set_argtypes(&self, argtypes: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let none = vm.is_none(&argtypes); + if none { + *self.arg_types.write() = None; + Ok(()) + } else { + let tuple = argtypes.downcast::().unwrap(); + *self.arg_types.write() = Some( + tuple + .iter() + .map(|obj| obj.clone().downcast::().unwrap()) + .collect::>(), + ); + Ok(()) + } + } + + #[pygetset] + fn __name__(&self) -> Option { + self.name.read().clone() + } + + #[pygetset(setter)] + fn set___name__(&self, name: String) -> PyResult<()> { + *self.name.write() = Some(name); + // TODO: update handle and stuff + Ok(()) } } diff --git a/crates/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs index d1360f9862e..e0723125628 100644 --- a/crates/vm/src/stdlib/ctypes/pointer.rs +++ b/crates/vm/src/stdlib/ctypes/pointer.rs @@ -1,5 +1,40 @@ -#[pyclass(name = "Pointer", module = "_ctypes")] -pub struct PyCPointer {} +use rustpython_common::lock::PyRwLock; + +use crate::builtins::PyType; +use crate::stdlib::ctypes::PyCData; +use crate::{PyObjectRef, PyResult}; + +#[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")] +#[derive(PyPayload, Debug)] +pub struct PyCPointerType { + pub inner: PyCPointer, +} + +#[pyclass] +impl PyCPointerType {} + +#[pyclass( + name = "_Pointer", + base = PyCData, + metaclass = "PyCPointerType", + module = "_ctypes" +)] +#[derive(Debug, PyPayload)] +pub struct PyCPointer { + contents: PyRwLock, +} #[pyclass(flags(BASETYPE, IMMUTABLETYPE))] -impl PyCPointer {} +impl PyCPointer { + // TODO: not correct + #[pygetset] + fn contents(&self) -> PyResult { + let contents = self.contents.read().clone(); + Ok(contents) + } + #[pygetset(setter)] + fn set_contents(&self, contents: PyObjectRef) -> PyResult<()> { + *self.contents.write() = contents; + Ok(()) + } +} diff --git a/crates/vm/src/stdlib/ctypes/thunk.rs b/crates/vm/src/stdlib/ctypes/thunk.rs new file mode 100644 index 00000000000..a65b04684b0 --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/thunk.rs @@ -0,0 +1,22 @@ +//! Yes, really, this is not a typo. + +// typedef struct { +// PyObject_VAR_HEAD +// ffi_closure *pcl_write; /* the C callable, writeable */ +// void *pcl_exec; /* the C callable, executable */ +// ffi_cif cif; +// int flags; +// PyObject *converters; +// PyObject *callable; +// PyObject *restype; +// SETFUNC setfunc; +// ffi_type *ffi_restype; +// ffi_type *atypes[1]; +// } CThunkObject; + +#[pyclass(name = "CThunkObject", module = "_ctypes")] +#[derive(Debug, PyPayload)] +pub struct PyCThunk {} + +#[pyclass] +impl PyCThunk {} diff --git a/crates/vm/src/stdlib/ctypes/util.rs b/crates/vm/src/stdlib/ctypes/util.rs new file mode 100644 index 00000000000..df5d3186682 --- /dev/null +++ b/crates/vm/src/stdlib/ctypes/util.rs @@ -0,0 +1,24 @@ +use crate::PyObjectRef; + +#[pyclass(name, module = "_ctypes")] +#[derive(Debug, PyPayload)] +pub struct StgInfo { + pub initialized: i32, + pub size: usize, // number of bytes + pub align: usize, // alignment requirements + pub length: usize, // number of fields + // ffi_type_pointer: ffi::ffi_type, + pub proto: PyObjectRef, // Only for Pointer/ArrayObject + pub setfunc: Option, // Only for simple objects + pub getfunc: Option, // Only for simple objects + pub paramfunc: Option, + + /* Following fields only used by PyCFuncPtrType_Type instances */ + pub argtypes: Option, // tuple of CDataObjects + pub converters: Option, // tuple([t.from_param for t in argtypes]) + pub restype: Option, // CDataObject or NULL + pub checker: Option, + pub module: Option, + pub flags: i32, // calling convention and such + pub dict_final: u8, +} diff --git a/extra_tests/snippets/stdlib_ctypes.py b/extra_tests/snippets/stdlib_ctypes.py index 32ed17d19f6..3ef6df689fa 100644 --- a/extra_tests/snippets/stdlib_ctypes.py +++ b/extra_tests/snippets/stdlib_ctypes.py @@ -40,7 +40,6 @@ def create_string_buffer(init, size=None): size = len(init) + 1 _sys.audit("ctypes.create_string_buffer", init, size) buftype = c_char.__mul__(size) - print(type(c_char.__mul__(size))) # buftype = c_char * size buf = buftype() buf.value = init @@ -334,8 +333,14 @@ def LoadLibrary(self, name): test_byte_array = create_string_buffer(b"Hello, World!\n") assert test_byte_array._length_ == 15 -if _os.name == "posix" or _sys.platform == "darwin": - pass +if _os.name == "posix": + if _sys.platform == "darwin": + libc = cdll.LoadLibrary("libc.dylib") + libc.rand() + i = c_int(1) + print("start srand") + print(libc.srand(i)) + print(test_byte_array) else: import os From 14cf4e32d04b234549fae3ae0dddb0b34a09f76a Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 28 Nov 2025 23:41:42 +0900 Subject: [PATCH 0051/1459] Fix PyCSimple --- crates/vm/src/stdlib/ctypes/base.rs | 5 +++-- crates/vm/src/stdlib/ctypes/field.rs | 2 +- crates/vm/src/stdlib/ctypes/function.rs | 16 ++++++++-------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index f5a25ad740b..1b07d73f8d2 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -218,11 +218,12 @@ impl Constructor for PyCSimple { _ => vm.ctx.none(), // "z" | "Z" | "P" } }; - Ok(PyCSimple { + PyCSimple { _type_, value: AtomicCell::new(value), } - .to_pyobject(vm)) + .into_ref_with_type(vm, cls) + .map(Into::into) } } diff --git a/crates/vm/src/stdlib/ctypes/field.rs b/crates/vm/src/stdlib/ctypes/field.rs index b50b8b54ace..20aded85a7e 100644 --- a/crates/vm/src/stdlib/ctypes/field.rs +++ b/crates/vm/src/stdlib/ctypes/field.rs @@ -36,7 +36,7 @@ pub struct PyCField { impl Representable for PyCField { fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { let tp_name = zelf.proto.name().to_string(); - if zelf.bitfield_size != false { + if zelf.bitfield_size { Ok(format!( "<{} type={}, ofs={byte_offset}, bit_size={bitfield_size}, bit_offset={bit_offset}", zelf.name, diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 88d0fbb35ea..bb4cc65212e 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -13,7 +13,7 @@ use libffi::middle::{Arg, Cif, CodePtr, Type}; use libloading::Symbol; use num_traits::ToPrimitive; use rustpython_common::lock::PyRwLock; -use std::ffi::{self, CString, c_void}; +use std::ffi::{self, c_void}; use std::fmt::Debug; // See also: https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15 @@ -61,6 +61,7 @@ impl ArgumentType for PyTypeRef { pub trait ReturnType { fn to_ffi_type(&self) -> Option; + #[allow(clippy::wrong_self_convention)] fn from_ffi_type( &self, value: *mut ffi::c_void, @@ -197,9 +198,8 @@ impl Callable for PyCFuncPtr { let return_type = zelf.res_type.read(); let ffi_return_type = return_type .as_ref() - .map(|t| ReturnType::to_ffi_type(&t.clone().downcast::().unwrap())) - .flatten() - .unwrap_or_else(|| Type::i32()); + .and_then(|t| ReturnType::to_ffi_type(&t.clone().downcast::().unwrap())) + .unwrap_or_else(Type::i32); let cif = Cif::new(ffi_arg_types, ffi_return_type); // Call the function @@ -243,12 +243,12 @@ impl Representable for PyCFuncPtr { let index = zelf.ptr.read(); let index = index.map(|ptr| ptr.0 as usize).unwrap_or(0); let type_name = zelf.class().name(); - #[cfg(windows)] - { + if cfg!(windows) { let index = index - 0x1000; - return Ok(format!("")); + Ok(format!("")) + } else { + Ok(format!("<{type_name} object at {index:#x}>")) } - Ok(format!("<{type_name} object at {index:#x}>")) } } From 8af105fc4ff5f5eb8e9a534b826b27ec47a6a576 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 28 Nov 2025 23:46:14 +0900 Subject: [PATCH 0052/1459] Make mergeable --- crates/vm/src/stdlib/ctypes.rs | 1 - crates/vm/src/stdlib/ctypes/field.rs | 11 +---------- crates/vm/src/stdlib/ctypes/function.rs | 2 ++ crates/vm/src/stdlib/ctypes/pointer.rs | 3 ++- crates/vm/src/stdlib/ctypes/util.rs | 24 ------------------------ extra_tests/snippets/stdlib_ctypes.py | 12 ++++++------ 6 files changed, 11 insertions(+), 42 deletions(-) delete mode 100644 crates/vm/src/stdlib/ctypes/util.rs diff --git a/crates/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs index ac74418354e..92629dcadb8 100644 --- a/crates/vm/src/stdlib/ctypes.rs +++ b/crates/vm/src/stdlib/ctypes.rs @@ -9,7 +9,6 @@ pub(crate) mod pointer; pub(crate) mod structure; pub(crate) mod thunk; pub(crate) mod union; -pub(crate) mod util; use crate::builtins::PyModule; use crate::class::PyClassImpl; diff --git a/crates/vm/src/stdlib/ctypes/field.rs b/crates/vm/src/stdlib/ctypes/field.rs index 20aded85a7e..e4e3bd6a09e 100644 --- a/crates/vm/src/stdlib/ctypes/field.rs +++ b/crates/vm/src/stdlib/ctypes/field.rs @@ -8,6 +8,7 @@ use crate::{Py, PyResult, VirtualMachine}; #[pyclass(name = "PyCFieldType", base = PyType, module = "_ctypes")] #[derive(PyPayload, Debug)] pub struct PyCFieldType { + #[allow(dead_code)] pub(super) inner: PyCField, } @@ -122,13 +123,3 @@ impl PyCField { self.bit_offset } } - -#[inline(always)] -pub const fn low_bit(offset: usize) -> usize { - offset & 0xFFFF -} - -#[inline(always)] -pub const fn high_bit(offset: usize) -> usize { - offset >> 16 -} diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index bb4cc65212e..64a230ad568 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -102,10 +102,12 @@ impl ReturnType for PyNone { pub struct PyCFuncPtr { pub name: PyRwLock>, pub ptr: PyRwLock>, + #[allow(dead_code)] pub needs_free: AtomicCell, pub arg_types: PyRwLock>>, pub res_type: PyRwLock>, pub _flags_: AtomicCell, + #[allow(dead_code)] pub handler: PyObjectRef, } diff --git a/crates/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs index e0723125628..b60280c73c8 100644 --- a/crates/vm/src/stdlib/ctypes/pointer.rs +++ b/crates/vm/src/stdlib/ctypes/pointer.rs @@ -7,7 +7,8 @@ use crate::{PyObjectRef, PyResult}; #[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")] #[derive(PyPayload, Debug)] pub struct PyCPointerType { - pub inner: PyCPointer, + #[allow(dead_code)] + pub(crate) inner: PyCPointer, } #[pyclass] diff --git a/crates/vm/src/stdlib/ctypes/util.rs b/crates/vm/src/stdlib/ctypes/util.rs deleted file mode 100644 index df5d3186682..00000000000 --- a/crates/vm/src/stdlib/ctypes/util.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::PyObjectRef; - -#[pyclass(name, module = "_ctypes")] -#[derive(Debug, PyPayload)] -pub struct StgInfo { - pub initialized: i32, - pub size: usize, // number of bytes - pub align: usize, // alignment requirements - pub length: usize, // number of fields - // ffi_type_pointer: ffi::ffi_type, - pub proto: PyObjectRef, // Only for Pointer/ArrayObject - pub setfunc: Option, // Only for simple objects - pub getfunc: Option, // Only for simple objects - pub paramfunc: Option, - - /* Following fields only used by PyCFuncPtrType_Type instances */ - pub argtypes: Option, // tuple of CDataObjects - pub converters: Option, // tuple([t.from_param for t in argtypes]) - pub restype: Option, // CDataObject or NULL - pub checker: Option, - pub module: Option, - pub flags: i32, // calling convention and such - pub dict_final: u8, -} diff --git a/extra_tests/snippets/stdlib_ctypes.py b/extra_tests/snippets/stdlib_ctypes.py index 3ef6df689fa..b4bc05dcb4c 100644 --- a/extra_tests/snippets/stdlib_ctypes.py +++ b/extra_tests/snippets/stdlib_ctypes.py @@ -338,9 +338,9 @@ def LoadLibrary(self, name): libc = cdll.LoadLibrary("libc.dylib") libc.rand() i = c_int(1) - print("start srand") - print(libc.srand(i)) - print(test_byte_array) + # print("start srand") + # print(libc.srand(i)) + # print(test_byte_array) else: import os @@ -348,9 +348,9 @@ def LoadLibrary(self, name): libc.rand() i = c_int(1) print("start srand") - print(libc.srand(i)) - print(test_byte_array) - print(test_byte_array._type_) + # print(libc.srand(i)) + # print(test_byte_array) + # print(test_byte_array._type_) # print("start printf") # libc.printf(test_byte_array) From fef9de22c4d31eb062073ba4752aa5a8ed18ad45 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Fri, 28 Nov 2025 17:46:27 +0200 Subject: [PATCH 0053/1459] Remove `Rotate*` & `Duplicate*` instructions (#6303) * Remove `Instruction::Duplicate2?` * Remove `Instruction::Rotate*` instructions * Fix jit * Update snapshot --- crates/codegen/src/compile.rs | 38 +++++++++-------- ...pile__tests__nested_double_async_with.snap | 2 +- crates/compiler-core/src/bytecode.rs | 11 ----- crates/jit/tests/common.rs | 12 ------ crates/vm/src/frame.rs | 41 ++++--------------- 5 files changed, 30 insertions(+), 74 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index 5f8caebf272..b2dc10cda6c 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -1063,7 +1063,7 @@ impl Compiler { if let Stmt::Expr(StmtExpr { value, .. }) = &last { self.compile_expression(value)?; - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); emit!(self, Instruction::PrintExpr); } else { self.compile_statement(last)?; @@ -1094,7 +1094,7 @@ impl Compiler { Stmt::FunctionDef(_) | Stmt::ClassDef(_) => { let pop_instructions = self.current_block().instructions.pop(); let store_inst = compiler_unwrap_option(self, pop_instructions); // pop Instruction::Store - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.current_block().instructions.push(store_inst); } _ => self.emit_load_const(ConstantData::None), @@ -1656,7 +1656,7 @@ impl Compiler { for (i, target) in targets.iter().enumerate() { if i + 1 != targets.len() { - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); } self.compile_store(target)?; } @@ -1917,7 +1917,7 @@ impl Compiler { ); } - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.store_name(name.as_ref())?; } TypeParam::ParamSpec(TypeParamParamSpec { name, default, .. }) => { @@ -1943,7 +1943,7 @@ impl Compiler { ); } - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.store_name(name.as_ref())?; } TypeParam::TypeVarTuple(TypeParamTypeVarTuple { name, default, .. }) => { @@ -1970,7 +1970,7 @@ impl Compiler { ); } - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.store_name(name.as_ref())?; } }; @@ -2030,7 +2030,7 @@ impl Compiler { // check if this handler can handle the exception: if let Some(exc_type) = type_ { // Duplicate exception for test: - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); // Check exception type: self.compile_expression(exc_type)?; @@ -2251,11 +2251,11 @@ impl Compiler { // Handle docstring if present if let Some(doc) = doc_str { - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.emit_load_const(ConstantData::Str { value: doc.to_string().into(), }); - emit!(self, Instruction::Rotate2); + emit!(self, Instruction::Swap { index: 2 }); let doc_attr = self.name("__doc__"); emit!(self, Instruction::StoreAttr { idx: doc_attr }); } @@ -2726,7 +2726,7 @@ impl Compiler { if let Some(classcell_idx) = classcell_idx { emit!(self, Instruction::LoadClosure(classcell_idx.to_u32())); - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); let classcell = self.name("__classcell__"); emit!(self, Instruction::StoreLocal(classcell)); } else { @@ -4121,8 +4121,8 @@ impl Compiler { for (op, val) in mid_ops.iter().zip(mid_exprs) { self.compile_expression(val)?; // store rhs for the next comparison in chain - emit!(self, Instruction::Duplicate); - emit!(self, Instruction::Rotate3); + emit!(self, Instruction::Swap { index: 2 }); + emit!(self, Instruction::CopyItem { index: 2_u32 }); compile_cmpop(self, op); @@ -4151,7 +4151,7 @@ impl Compiler { // early exit left us with stack: `rhs, comparison_result`. We need to clean up rhs. self.switch_to_block(break_block); - emit!(self, Instruction::Rotate2); + emit!(self, Instruction::Swap { index: 2 }); emit!(self, Instruction::Pop); self.switch_to_block(after_block); @@ -4322,7 +4322,8 @@ impl Compiler { // But we can't use compile_subscript directly because we need DUP_TOP2 self.compile_expression(value)?; self.compile_expression(slice)?; - emit!(self, Instruction::Duplicate2); + emit!(self, Instruction::CopyItem { index: 2_u32 }); + emit!(self, Instruction::CopyItem { index: 2_u32 }); emit!(self, Instruction::Subscript); AugAssignKind::Subscript } @@ -4330,7 +4331,7 @@ impl Compiler { let attr = attr.as_str(); self.check_forbidden_name(attr, NameUsage::Store)?; self.compile_expression(value)?; - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); let idx = self.name(attr); emit!(self, Instruction::LoadAttr { idx }); AugAssignKind::Attr { idx } @@ -4350,12 +4351,13 @@ impl Compiler { } AugAssignKind::Subscript => { // stack: CONTAINER SLICE RESULT - emit!(self, Instruction::Rotate3); + emit!(self, Instruction::Swap { index: 3 }); + emit!(self, Instruction::Swap { index: 2 }); emit!(self, Instruction::StoreSubscript); } AugAssignKind::Attr { idx } => { // stack: CONTAINER RESULT - emit!(self, Instruction::Rotate2); + emit!(self, Instruction::Swap { index: 2 }); emit!(self, Instruction::StoreAttr { idx }); } } @@ -4896,7 +4898,7 @@ impl Compiler { range: _, }) => { self.compile_expression(value)?; - emit!(self, Instruction::Duplicate); + emit!(self, Instruction::CopyItem { index: 1_u32 }); self.compile_store(target)?; } Expr::FString(fstring) => { diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap index 82c02d5ea34..dc624c7b6a1 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -49,7 +49,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 39 WithCleanupFinish 40 PopBlock 41 Jump (59) - >> 42 Duplicate + >> 42 CopyItem (1) 6 43 LoadNameAny (7, Exception) 44 TestOperation (ExceptionMatch) diff --git a/crates/compiler-core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs index 2cfb70e2782..fe28517cda8 100644 --- a/crates/compiler-core/src/bytecode.rs +++ b/crates/compiler-core/src/bytecode.rs @@ -614,10 +614,6 @@ pub enum Instruction { index: Arg, }, ToBool, - Rotate2, - Rotate3, - Duplicate, - Duplicate2, GetIter, GetLen, CallIntrinsic1 { @@ -1475,9 +1471,6 @@ impl Instruction { Pop => -1, Swap { .. } => 0, ToBool => 0, - Rotate2 | Rotate3 => 0, - Duplicate => 1, - Duplicate2 => 2, GetIter => 0, GetLen => 1, CallIntrinsic1 { .. } => 0, // Takes 1, pushes 1 @@ -1678,10 +1671,6 @@ impl Instruction { Pop => w!(Pop), Swap { index } => w!(Swap, index), ToBool => w!(ToBool), - Rotate2 => w!(Rotate2), - Rotate3 => w!(Rotate3), - Duplicate => w!(Duplicate), - Duplicate2 => w!(Duplicate2), GetIter => w!(GetIter), // GET_LEN GetLen => w!(GetLen), diff --git a/crates/jit/tests/common.rs b/crates/jit/tests/common.rs index a88d3207f2a..5dafeaeb807 100644 --- a/crates/jit/tests/common.rs +++ b/crates/jit/tests/common.rs @@ -164,18 +164,6 @@ impl StackMachine { self.stack.push(StackValue::Function(func)); } } - Instruction::Duplicate => { - let value = self.stack.last().unwrap().clone(); - self.stack.push(value); - } - Instruction::Rotate2 => { - let i = self.stack.len() - 2; - self.stack[i..].rotate_right(1); - } - Instruction::Rotate3 => { - let i = self.stack.len() - 3; - self.stack[i..].rotate_right(1); - } Instruction::ReturnConst { idx } => { let idx = idx.get(arg); self.stack.push(constants[idx as usize].clone().into()); diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs index ab90f52b2df..8deaada0827 100644 --- a/crates/vm/src/frame.rs +++ b/crates/vm/src/frame.rs @@ -719,32 +719,16 @@ impl ExecutingFrame<'_> { self.state.stack.swap(i, j); Ok(None) } - // bytecode::Instruction::ToBool => { - // dbg!("Shouldn't be called outside of match statements for now") - // let value = self.pop_value(); - // // call __bool__ - // let result = value.try_to_bool(vm)?; - // self.push_value(vm.ctx.new_bool(result).into()); - // Ok(None) - // } - bytecode::Instruction::Duplicate => { - // Duplicate top of stack - let value = self.top_value(); - self.push_value(value.to_owned()); - Ok(None) - } - bytecode::Instruction::Duplicate2 => { - // Duplicate top 2 of stack - let len = self.state.stack.len(); - self.push_value(self.state.stack[len - 2].clone()); - self.push_value(self.state.stack[len - 1].clone()); - Ok(None) + /* + bytecode::Instruction::ToBool => { + dbg!("Shouldn't be called outside of match statements for now") + let value = self.pop_value(); + // call __bool__ + let result = value.try_to_bool(vm)?; + self.push_value(vm.ctx.new_bool(result).into()); + Ok(None) } - // splitting the instructions like this offloads the cost of "dynamic" dispatch (on the - // amount to rotate) to the opcode dispatcher, and generates optimized code for the - // concrete cases we actually have - bytecode::Instruction::Rotate2 => self.execute_rotate(2), - bytecode::Instruction::Rotate3 => self.execute_rotate(3), + */ bytecode::Instruction::BuildString { size } => { let s = self .pop_multiple(size.get(arg) as usize) @@ -1685,13 +1669,6 @@ impl ExecutingFrame<'_> { } } - #[inline(always)] - fn execute_rotate(&mut self, amount: usize) -> FrameResult { - let i = self.state.stack.len() - amount; - self.state.stack[i..].rotate_right(1); - Ok(None) - } - fn execute_subscript(&mut self, vm: &VirtualMachine) -> FrameResult { let b_ref = self.pop_value(); let a_ref = self.pop_value(); From 23ec5a55adc7053a105235cd2dfe6ee078c9e712 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:32:52 +0900 Subject: [PATCH 0054/1459] 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 --- Lib/ctypes/__init__.py | 39 ++++--- crates/vm/src/stdlib/ctypes.rs | 79 +++++++++++-- crates/vm/src/stdlib/ctypes/array.rs | 7 +- crates/vm/src/stdlib/ctypes/base.rs | 15 +++ crates/vm/src/stdlib/ctypes/function.rs | 148 ++++++++++++++++-------- 5 files changed, 211 insertions(+), 77 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index a5d27daff0b..3599e13ed28 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -347,6 +347,19 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None): + if name: + name = _os.fspath(name) + + # If the filename that has been provided is an iOS/tvOS/watchOS + # .fwork file, dereference the location to the true origin of the + # binary. + if name.endswith(".fwork"): + with open(name) as f: + name = _os.path.join( + _os.path.dirname(_sys.executable), + f.read().strip() + ) + self._name = name flags = self._func_flags_ if use_errno: @@ -467,6 +480,8 @@ def LoadLibrary(self, name): if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) +elif _sys.platform == "android": + pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2]) elif _sys.platform == "cygwin": pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) else: @@ -498,15 +513,14 @@ def WinError(code=None, descr=None): c_ssize_t = c_longlong # functions + from _ctypes import _memmove_addr, _memset_addr, _string_at_addr, _cast_addr ## void *memmove(void *, const void *, size_t); -# XXX: RUSTPYTHON -# memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) +memmove = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_size_t)(_memmove_addr) ## void *memset(void *, int, size_t) -# XXX: RUSTPYTHON -# memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) +memset = CFUNCTYPE(c_void_p, c_void_p, c_int, c_size_t)(_memset_addr) def PYFUNCTYPE(restype, *argtypes): class CFunctionType(_CFuncPtr): @@ -515,17 +529,15 @@ class CFunctionType(_CFuncPtr): _flags_ = _FUNCFLAG_CDECL | _FUNCFLAG_PYTHONAPI return CFunctionType -# XXX: RUSTPYTHON -# _cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) +_cast = PYFUNCTYPE(py_object, c_void_p, py_object, py_object)(_cast_addr) def cast(obj, typ): return _cast(obj, obj, typ) -# XXX: RUSTPYTHON -# _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) +_string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr) def string_at(ptr, size=-1): - """string_at(addr[, size]) -> string + """string_at(ptr[, size]) -> string - Return the string at addr.""" + Return the byte string at void *ptr.""" return _string_at(ptr, size) try: @@ -533,12 +545,11 @@ def string_at(ptr, size=-1): except ImportError: pass else: - # XXX: RUSTPYTHON - # _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) + _wstring_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_wstring_at_addr) def wstring_at(ptr, size=-1): - """wstring_at(addr[, size]) -> string + """wstring_at(ptr[, size]) -> string - Return the string at addr.""" + Return the wide-character string at void *ptr.""" return _wstring_at(ptr, size) diff --git a/crates/vm/src/stdlib/ctypes.rs b/crates/vm/src/stdlib/ctypes.rs index 92629dcadb8..500ddbfb06b 100644 --- a/crates/vm/src/stdlib/ctypes.rs +++ b/crates/vm/src/stdlib/ctypes.rs @@ -189,6 +189,7 @@ pub(crate) mod _ctypes { } } + #[cfg(windows)] #[pyfunction(name = "LoadLibrary")] fn load_library_windows( name: String, @@ -203,20 +204,33 @@ pub(crate) mod _ctypes { Ok(id) } + #[cfg(not(windows))] #[pyfunction(name = "dlopen")] fn load_library_unix( - name: String, + name: Option, _load_flags: OptionalArg, vm: &VirtualMachine, ) -> PyResult { // TODO: audit functions first // TODO: load_flags - let cache = library::libcache(); - let mut cache_write = cache.write(); - let (id, _) = cache_write - .get_or_insert_lib(&name, vm) - .map_err(|e| vm.new_os_error(e.to_string()))?; - Ok(id) + match name { + Some(name) => { + let cache = library::libcache(); + let mut cache_write = cache.write(); + let (id, _) = cache_write + .get_or_insert_lib(&name, vm) + .map_err(|e| vm.new_os_error(e.to_string()))?; + Ok(id) + } + None => { + // If None, call libc::dlopen(null, mode) to get the current process handle + let handle = unsafe { libc::dlopen(std::ptr::null(), libc::RTLD_NOW) }; + if handle.is_null() { + return Err(vm.new_os_error("dlopen() error")); + } + Ok(handle as usize) + } + } } #[pyfunction(name = "FreeLibrary")] @@ -228,10 +242,57 @@ pub(crate) mod _ctypes { } #[pyfunction(name = "POINTER")] - pub fn pointer(_cls: PyTypeRef) {} + pub fn create_pointer_type(cls: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // Get the _pointer_type_cache + let ctypes_module = vm.import("_ctypes", 0)?; + let cache = ctypes_module.get_attr("_pointer_type_cache", vm)?; + + // Check if already in cache using __getitem__ + if let Ok(cached) = vm.call_method(&cache, "__getitem__", (cls.clone(),)) + && !vm.is_none(&cached) + { + return Ok(cached); + } + + // Get the _Pointer base class + let pointer_base = ctypes_module.get_attr("_Pointer", vm)?; + + // Create the name for the pointer type + let name = if let Ok(type_obj) = cls.get_attr("__name__", vm) { + format!("LP_{}", type_obj.str(vm)?) + } else if let Ok(s) = cls.str(vm) { + format!("LP_{}", s) + } else { + "LP_unknown".to_string() + }; + + // Create a new type that inherits from _Pointer + let type_type = &vm.ctx.types.type_type; + let bases = vm.ctx.new_tuple(vec![pointer_base]); + let dict = vm.ctx.new_dict(); + dict.set_item("_type_", cls.clone(), vm)?; + + let new_type = type_type + .as_object() + .call((vm.ctx.new_str(name), bases, dict), vm)?; + + // Store in cache using __setitem__ + vm.call_method(&cache, "__setitem__", (cls, new_type.clone()))?; + + Ok(new_type) + } #[pyfunction(name = "pointer")] - pub fn pointer_fn(_inst: PyObjectRef) {} + pub fn create_pointer_inst(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // Get the type of the object + let obj_type = obj.class().to_owned(); + + // Create pointer type for this object's type + let ptr_type = create_pointer_type(obj_type.into(), vm)?; + + // Create an instance of the pointer type with the object + ptr_type.call((obj,), vm) + } #[pyfunction] fn _pointer_type_cache() -> PyObjectRef { diff --git a/crates/vm/src/stdlib/ctypes/array.rs b/crates/vm/src/stdlib/ctypes/array.rs index a1adf847a99..a46322460a9 100644 --- a/crates/vm/src/stdlib/ctypes/array.rs +++ b/crates/vm/src/stdlib/ctypes/array.rs @@ -72,13 +72,14 @@ impl std::fmt::Debug for PyCArray { impl Constructor for PyCArray { type Args = (PyTypeRef, usize); - fn py_new(_cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { - Ok(Self { + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + Self { typ: PyRwLock::new(args.0), length: AtomicCell::new(args.1), value: PyRwLock::new(vm.ctx.none()), } - .into_pyobject(vm)) + .into_ref_with_type(vm, cls) + .map(Into::into) } } diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index 1b07d73f8d2..29f3b9da2ae 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -171,6 +171,21 @@ impl PyCSimpleType { .clone(), )) } + + #[pyclassmethod] + fn from_param(cls: PyTypeRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + // If the value is already an instance of the requested type, return it + if value.fast_isinstance(&cls) { + return Ok(value); + } + + // Check for _as_parameter_ attribute + let Ok(as_parameter) = value.get_attr("_as_parameter_", vm) else { + return Err(vm.new_type_error("wrong type")); + }; + + PyCSimpleType::from_param(cls, as_parameter, vm) + } } #[pyclass( diff --git a/crates/vm/src/stdlib/ctypes/function.rs b/crates/vm/src/stdlib/ctypes/function.rs index 64a230ad568..c1db4d58f8d 100644 --- a/crates/vm/src/stdlib/ctypes/function.rs +++ b/crates/vm/src/stdlib/ctypes/function.rs @@ -120,60 +120,106 @@ impl Debug for PyCFuncPtr { } impl Constructor for PyCFuncPtr { - type Args = (PyTupleRef, FuncArgs); + type Args = FuncArgs; - fn py_new(_cls: PyTypeRef, (tuple, _args): Self::Args, vm: &VirtualMachine) -> PyResult { - let name = tuple - .first() - .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? - .downcast_ref::() - .ok_or(vm.new_type_error("Expected a string"))? - .to_string(); - let handler = tuple - .into_iter() - .nth(1) - .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? - .clone(); - let handle = handler.try_int(vm); - let handle = match handle { - Ok(handle) => handle.as_bigint().clone(), - Err(_) => handler - .get_attr("_handle", vm)? - .try_int(vm)? - .as_bigint() - .clone(), - }; - let library_cache = crate::stdlib::ctypes::library::libcache().read(); - let library = library_cache - .get_lib( - handle - .to_usize() - .ok_or(vm.new_value_error("Invalid handle".to_string()))?, - ) - .ok_or_else(|| vm.new_value_error("Library not found".to_string()))?; - let inner_lib = library.lib.lock(); - - let terminated = format!("{}\0", &name); - let code_ptr = if let Some(lib) = &*inner_lib { - let pointer: Symbol<'_, FP> = unsafe { - lib.get(terminated.as_bytes()) - .map_err(|err| err.to_string()) - .map_err(|err| vm.new_attribute_error(err))? + fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult { + // Handle different argument forms like CPython: + // 1. Empty args: create uninitialized + // 2. One integer argument: function address + // 3. Tuple argument: (name, dll) form + + if args.args.is_empty() { + return Self { + ptr: PyRwLock::new(None), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(None), + handler: vm.ctx.none(), + } + .into_ref_with_type(vm, cls) + .map(Into::into); + } + + let first_arg = &args.args[0]; + + // Check if first argument is an integer (function address) + if let Ok(addr) = first_arg.try_int(vm) { + let ptr_val = addr.as_bigint().to_usize().unwrap_or(0); + return Self { + ptr: PyRwLock::new(Some(CodePtr(ptr_val as *mut _))), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(format!("CFuncPtr@{:#x}", ptr_val))), + handler: vm.ctx.new_int(ptr_val).into(), + } + .into_ref_with_type(vm, cls) + .map(Into::into); + } + + // Check if first argument is a tuple (name, dll) form + if let Some(tuple) = first_arg.downcast_ref::() { + let name = tuple + .first() + .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? + .downcast_ref::() + .ok_or(vm.new_type_error("Expected a string"))? + .to_string(); + let handler = tuple + .iter() + .nth(1) + .ok_or(vm.new_type_error("Expected a tuple with at least 2 elements"))? + .clone(); + + // Get library handle and load function + let handle = handler.try_int(vm); + let handle = match handle { + Ok(handle) => handle.as_bigint().clone(), + Err(_) => handler + .get_attr("_handle", vm)? + .try_int(vm)? + .as_bigint() + .clone(), }; - Some(CodePtr(*pointer as *mut _)) - } else { - None - }; - Ok(Self { - ptr: PyRwLock::new(code_ptr), - needs_free: AtomicCell::new(false), - arg_types: PyRwLock::new(None), - _flags_: AtomicCell::new(0), - res_type: PyRwLock::new(None), - name: PyRwLock::new(Some(name)), - handler, + let library_cache = crate::stdlib::ctypes::library::libcache().read(); + let library = library_cache + .get_lib( + handle + .to_usize() + .ok_or(vm.new_value_error("Invalid handle".to_string()))?, + ) + .ok_or_else(|| vm.new_value_error("Library not found".to_string()))?; + let inner_lib = library.lib.lock(); + + let terminated = format!("{}\0", &name); + let code_ptr = if let Some(lib) = &*inner_lib { + let pointer: Symbol<'_, FP> = unsafe { + lib.get(terminated.as_bytes()) + .map_err(|err| err.to_string()) + .map_err(|err| vm.new_attribute_error(err))? + }; + Some(CodePtr(*pointer as *mut _)) + } else { + None + }; + + return Self { + ptr: PyRwLock::new(code_ptr), + needs_free: AtomicCell::new(false), + arg_types: PyRwLock::new(None), + _flags_: AtomicCell::new(0), + res_type: PyRwLock::new(None), + name: PyRwLock::new(Some(name)), + handler, + } + .into_ref_with_type(vm, cls) + .map(Into::into); } - .to_pyobject(vm)) + + Err(vm.new_type_error("Expected an integer address or a tuple")) } } From a81912857d6151689ff8cb62dd7fec320cf33212 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:15:25 +0900 Subject: [PATCH 0055/1459] Ctypes __mul__ (#6305) --- crates/vm/src/stdlib/ctypes/base.rs | 52 +++++++++++++++++++++----- crates/vm/src/stdlib/ctypes/pointer.rs | 49 ++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/crates/vm/src/stdlib/ctypes/base.rs b/crates/vm/src/stdlib/ctypes/base.rs index 29f3b9da2ae..6fdb79e11e9 100644 --- a/crates/vm/src/stdlib/ctypes/base.rs +++ b/crates/vm/src/stdlib/ctypes/base.rs @@ -3,8 +3,9 @@ use crate::builtins::PyType; use crate::builtins::{PyBytes, PyFloat, PyInt, PyNone, PyStr, PyTypeRef}; use crate::convert::ToPyObject; use crate::function::{Either, OptionalArg}; +use crate::protocol::PyNumberMethods; use crate::stdlib::ctypes::_ctypes::new_simple_type; -use crate::types::Constructor; +use crate::types::{AsNumber, Constructor}; use crate::{AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine}; use crossbeam_utils::atomic::AtomicCell; use num_traits::ToPrimitive; @@ -158,9 +159,10 @@ pub struct PyCData { impl PyCData {} #[pyclass(module = "_ctypes", name = "PyCSimpleType", base = PyType)] +#[derive(Debug, PyPayload)] pub struct PyCSimpleType {} -#[pyclass(flags(BASETYPE))] +#[pyclass(flags(BASETYPE), with(AsNumber))] impl PyCSimpleType { #[allow(clippy::new_ret_no_self)] #[pymethod] @@ -186,6 +188,33 @@ impl PyCSimpleType { PyCSimpleType::from_param(cls, as_parameter, vm) } + + #[pymethod] + fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { + PyCSimple::repeat(cls, n, vm) + } +} + +impl AsNumber for PyCSimpleType { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + multiply: Some(|a, b, vm| { + // a is a PyCSimpleType instance (type object like c_char) + // b is int (array size) + let cls = a + .downcast_ref::() + .ok_or_else(|| vm.new_type_error("expected type".to_owned()))?; + let n = b + .try_index(vm)? + .as_bigint() + .to_isize() + .ok_or_else(|| vm.new_overflow_error("array size too large".to_owned()))?; + PyCSimple::repeat(cls.to_owned(), n, vm) + }), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + &AS_NUMBER + } } #[pyclass( @@ -215,8 +244,18 @@ impl Constructor for PyCSimple { let attributes = cls.get_attributes(); let _type_ = attributes .iter() - .find(|(k, _)| k.to_object().str(vm).unwrap().to_string() == *"_type_") - .unwrap() + .find(|(k, _)| { + k.to_object() + .str(vm) + .map(|s| s.to_string() == "_type_") + .unwrap_or(false) + }) + .ok_or_else(|| { + vm.new_type_error(format!( + "cannot create '{}' instances: no _type_ attribute", + cls.name() + )) + })? .1 .str(vm)? .to_string(); @@ -276,11 +315,6 @@ impl PyCSimple { } .to_pyobject(vm)) } - - #[pyclassmethod] - fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { - PyCSimple::repeat(cls, n, vm) - } } impl PyCSimple { diff --git a/crates/vm/src/stdlib/ctypes/pointer.rs b/crates/vm/src/stdlib/ctypes/pointer.rs index b60280c73c8..3eb6f68e6dd 100644 --- a/crates/vm/src/stdlib/ctypes/pointer.rs +++ b/crates/vm/src/stdlib/ctypes/pointer.rs @@ -1,8 +1,13 @@ +use crossbeam_utils::atomic::AtomicCell; +use num_traits::ToPrimitive; use rustpython_common::lock::PyRwLock; -use crate::builtins::PyType; +use crate::builtins::{PyType, PyTypeRef}; +use crate::convert::ToPyObject; +use crate::protocol::PyNumberMethods; use crate::stdlib::ctypes::PyCData; -use crate::{PyObjectRef, PyResult}; +use crate::types::AsNumber; +use crate::{PyObjectRef, PyResult, VirtualMachine}; #[pyclass(name = "PyCPointerType", base = PyType, module = "_ctypes")] #[derive(PyPayload, Debug)] @@ -11,8 +16,44 @@ pub struct PyCPointerType { pub(crate) inner: PyCPointer, } -#[pyclass] -impl PyCPointerType {} +#[pyclass(flags(IMMUTABLETYPE), with(AsNumber))] +impl PyCPointerType { + #[pymethod] + fn __mul__(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult { + use super::array::{PyCArray, PyCArrayType}; + if n < 0 { + return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}"))); + } + Ok(PyCArrayType { + inner: PyCArray { + typ: PyRwLock::new(cls), + length: AtomicCell::new(n as usize), + value: PyRwLock::new(vm.ctx.none()), + }, + } + .to_pyobject(vm)) + } +} + +impl AsNumber for PyCPointerType { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + multiply: Some(|a, b, vm| { + let cls = a + .downcast_ref::() + .ok_or_else(|| vm.new_type_error("expected type".to_owned()))?; + let n = b + .try_index(vm)? + .as_bigint() + .to_isize() + .ok_or_else(|| vm.new_overflow_error("array size too large".to_owned()))?; + PyCPointerType::__mul__(cls.to_owned(), n, vm) + }), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + &AS_NUMBER + } +} #[pyclass( name = "_Pointer", From 7d8f0b989c542ee856615ee7e569898081312479 Mon Sep 17 00:00:00 2001 From: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:02:52 +0200 Subject: [PATCH 0056/1459] Split `TestOperator` instruction (#6306) * Split `TestOperator` inatruction * Update snapshot * Set as have label --- crates/codegen/src/compile.rs | 48 +++-------- ...pile__tests__nested_double_async_with.snap | 69 ++++++++-------- crates/compiler-core/src/bytecode.rs | 54 ++++++++----- crates/vm/src/frame.rs | 81 +++++++++++-------- 4 files changed, 123 insertions(+), 129 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index b2dc10cda6c..4a4f17edcfa 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -36,7 +36,7 @@ use rustpython_compiler_core::{ Mode, OneIndexed, PositionEncoding, SourceFile, SourceLocation, bytecode::{ self, Arg as OpArgMarker, BinaryOperator, CodeObject, ComparisonOperator, ConstantData, - Instruction, OpArg, OpArgType, UnpackExArgs, + Instruction, Invert, OpArg, OpArgType, UnpackExArgs, }, }; use rustpython_wtf8::Wtf8Buf; @@ -2034,20 +2034,7 @@ impl Compiler { // Check exception type: self.compile_expression(exc_type)?; - emit!( - self, - Instruction::TestOperation { - op: bytecode::TestOperator::ExceptionMatch, - } - ); - - // We cannot handle this exception type: - emit!( - self, - Instruction::PopJumpIfFalse { - target: next_handler, - } - ); + emit!(self, Instruction::JumpIfNotExcMatch(next_handler)); // We have a match, store in name (except x as y) if let Some(alias) = name { @@ -3477,12 +3464,7 @@ impl Compiler { // 4. Load None. self.emit_load_const(ConstantData::None); // 5. Compare with IS_OP 1. - emit!( - self, - Instruction::TestOperation { - op: bytecode::TestOperator::IsNot - } - ); + emit!(self, Instruction::IsOp(Invert::Yes)); // At this point the TOS is a tuple of (nargs + n_attrs) attributes (or None). pc.on_top += 1; @@ -3648,12 +3630,8 @@ impl Compiler { // Check if copy is None (consumes the copy like POP_JUMP_IF_NONE) self.emit_load_const(ConstantData::None); - emit!( - self, - Instruction::TestOperation { - op: bytecode::TestOperator::IsNot - } - ); + emit!(self, Instruction::IsOp(Invert::Yes)); + // Stack: [subject, keys_tuple, values_tuple, bool] self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?; // Stack: [subject, keys_tuple, values_tuple] @@ -3948,12 +3926,7 @@ impl Compiler { Singleton::True => ConstantData::Boolean { value: true }, }); // Compare using the "Is" operator. - emit!( - self, - Instruction::TestOperation { - op: bytecode::TestOperator::Is - } - ); + emit!(self, Instruction::IsOp(Invert::No)); // Jump to the failure label if the comparison is false. self.jump_to_fail_pop(pc, JumpOp::PopJumpIfFalse)?; Ok(()) @@ -4082,7 +4055,6 @@ impl Compiler { let (last_val, mid_exprs) = exprs.split_last().unwrap(); use bytecode::ComparisonOperator::*; - use bytecode::TestOperator::*; let compile_cmpop = |c: &mut Self, op: &CmpOp| match op { CmpOp::Eq => emit!(c, Instruction::CompareOperation { op: Equal }), CmpOp::NotEq => emit!(c, Instruction::CompareOperation { op: NotEqual }), @@ -4092,10 +4064,10 @@ impl Compiler { CmpOp::GtE => { emit!(c, Instruction::CompareOperation { op: GreaterOrEqual }) } - CmpOp::In => emit!(c, Instruction::TestOperation { op: In }), - CmpOp::NotIn => emit!(c, Instruction::TestOperation { op: NotIn }), - CmpOp::Is => emit!(c, Instruction::TestOperation { op: Is }), - CmpOp::IsNot => emit!(c, Instruction::TestOperation { op: IsNot }), + CmpOp::In => emit!(c, Instruction::ContainsOp(Invert::No)), + CmpOp::NotIn => emit!(c, Instruction::ContainsOp(Invert::Yes)), + CmpOp::Is => emit!(c, Instruction::IsOp(Invert::No)), + CmpOp::IsNot => emit!(c, Instruction::IsOp(Invert::Yes)), }; // a == b == c == d diff --git a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap index dc624c7b6a1..3042e35f179 100644 --- a/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ b/crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -1,5 +1,5 @@ --- -source: compiler/codegen/src/compile.rs +source: crates/codegen/src/compile.rs expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")" --- 1 0 SetupLoop @@ -11,7 +11,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 6 CallFunctionPositional(1) 7 BuildTuple (2) 8 GetIter - >> 9 ForIter (73) + >> 9 ForIter (72) 10 StoreLocal (2, stop_exc) 2 11 LoadNameAny (3, self) @@ -21,7 +21,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 15 CallFunctionPositional(1) 16 LoadConst (("type")) 17 CallMethodKeyword (1) - 18 SetupWith (70) + 18 SetupWith (69) 19 Pop 3 20 SetupExcept (42) @@ -48,41 +48,40 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 38 Resume (3) 39 WithCleanupFinish 40 PopBlock - 41 Jump (59) + 41 Jump (58) >> 42 CopyItem (1) 6 43 LoadNameAny (7, Exception) - 44 TestOperation (ExceptionMatch) - 45 PopJumpIfFalse (58) - 46 StoreLocal (8, ex) + 44 JUMP_IF_NOT_EXC_MATCH(57) + 45 StoreLocal (8, ex) - 7 47 LoadNameAny (3, self) - 48 LoadMethod (9, assertIs) - 49 LoadNameAny (8, ex) - 50 LoadNameAny (2, stop_exc) - 51 CallMethodPositional (2) - 52 Pop - 53 PopException - 54 LoadConst (None) - 55 StoreLocal (8, ex) - 56 DeleteLocal (8, ex) - 57 Jump (68) - >> 58 Raise (Reraise) + 7 46 LoadNameAny (3, self) + 47 LoadMethod (9, assertIs) + 48 LoadNameAny (8, ex) + 49 LoadNameAny (2, stop_exc) + 50 CallMethodPositional (2) + 51 Pop + 52 PopException + 53 LoadConst (None) + 54 StoreLocal (8, ex) + 55 DeleteLocal (8, ex) + 56 Jump (67) + >> 57 Raise (Reraise) - 9 >> 59 LoadNameAny (3, self) - 60 LoadMethod (10, fail) - 61 LoadConst ("") - 62 LoadNameAny (2, stop_exc) - 63 FormatValue (None) - 64 LoadConst (" was suppressed") - 65 BuildString (2) - 66 CallMethodPositional (1) - 67 Pop + 9 >> 58 LoadNameAny (3, self) + 59 LoadMethod (10, fail) + 60 LoadConst ("") + 61 LoadNameAny (2, stop_exc) + 62 FormatValue (None) + 63 LoadConst (" was suppressed") + 64 BuildString (2) + 65 CallMethodPositional (1) + 66 Pop - 2 >> 68 PopBlock - 69 EnterFinally - >> 70 WithCleanupStart - 71 WithCleanupFinish - 72 Jump (9) - >> 73 PopBlock - 74 ReturnConst (None) + 2 >> 67 PopBlock + 68 EnterFinally + >> 69 WithCleanupStart + 70 WithCleanupFinish + 71 Jump (9) + >> 72 PopBlock + 73 ReturnConst (None) diff --git a/crates/compiler-core/src/bytecode.rs b/crates/compiler-core/src/bytecode.rs index fe28517cda8..144054860e4 100644 --- a/crates/compiler-core/src/bytecode.rs +++ b/crates/compiler-core/src/bytecode.rs @@ -577,6 +577,10 @@ pub enum Instruction { Subscript, StoreSubscript, DeleteSubscript, + /// Performs `is` comparison, or `is not` if `invert` is 1. + IsOp(Arg), + /// Performs `in` comparison, or `not in` if `invert` is 1. + ContainsOp(Arg), StoreAttr { idx: Arg, }, @@ -600,9 +604,6 @@ pub enum Instruction { LoadAttr { idx: Arg, }, - TestOperation { - op: Arg, - }, CompareOperation { op: Arg, }, @@ -628,6 +629,10 @@ pub enum Instruction { Break { target: Arg