Compare commits

..

7 Commits

Author SHA1 Message Date
20c053a3c0 Refactor PySetterValue handling in object and type modules
- Update object_set_dict to use new PySetterValue variants
- Modify getset.rs to handle PySetterValue::Delete
- Add delete_dict method to PyObject
- Adjust type.rs to use new PySetterValue semantics
2025-02-09 23:26:36 +09:00
Zion
6181bf4a7a Add delete for object_set_dict 2025-02-08 17:24:31 +09:00
b333ffa781 merge upstream 2025-02-08 16:33:07 +09:00
308b95ec13 Update .github/workflows/ai-review.yml 2025-01-11 01:53:46 +09:00
5f77ce5b4f Update .github/workflows/ai-review.yml 2025-01-11 00:44:09 +09:00
4da57d42de Update .github/workflows/ai-review.yml 2025-01-10 13:54:55 +09:00
2777588bb4 Add ci 2025-01-10 11:48:55 +09:00
44 changed files with 528 additions and 1295 deletions

23
.github/workflows/ai-review.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: PR Review
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- name: AI Code Review
uses: gitea-actions/ai-reviewer@v0.6
with:
access-token: ${{ secrets.ACCESS_TOKEN }}
full-context-model: "gpt-4o"
full-context-api-key: ${{ secrets.OPENAI_API_KEY }}
single-chunk-model: "claude-3-5-sonnet"
single-chunk-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
exclude-files: "*.md,*.yaml"

View File

@@ -105,7 +105,7 @@ env:
test_weakref
test_yield_from
# Python version targeted by the CI.
PYTHON_VERSION: "3.13.1"
PYTHON_VERSION: "3.12.3"
jobs:
rust_tests:

View File

@@ -7,7 +7,7 @@ name: Periodic checks/tasks
env:
CARGO_ARGS: --no-default-features --features stdlib,zlib,importlib,encodings,ssl,jit
PYTHON_VERSION: "3.13.1"
PYTHON_VERSION: "3.12.0"
jobs:
# codecov collects code coverage data from the rust tests, python snippets and python test suite.

725
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -127,11 +127,11 @@ rustpython-doc = { git = "https://github.com/RustPython/__doc__", tag = "0.3.0",
# rustpython-parser = { version = "0.4.0" }
# rustpython-ast = { version = "0.4.0" }
# rustpython-format= { version = "0.4.0" }
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
rustpython-format = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "d2f137b372ec08ce4a243564a80f8f9153c45a23" }
rustpython-literal = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-parser-core = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-parser = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-ast = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
rustpython-format = { git = "https://github.com/RustPython/Parser.git", version = "0.4.0", rev = "4588ea5c3e6327009640e7c9c89eb6fa9220358e" }
# rustpython-literal = { path = "../RustPython-parser/literal" }
# rustpython-parser-core = { path = "../RustPython-parser/core" }
# rustpython-parser = { path = "../RustPython-parser/parser" }
@@ -139,50 +139,50 @@ rustpython-format = { git = "https://github.com/RustPython/Parser.git", version
# rustpython-format = { path = "../RustPython-parser/format" }
ahash = "0.8.11"
ascii = "1.1"
bitflags = "2.4.2"
ascii = "1.0"
bitflags = "2.4.1"
bstr = "1"
cfg-if = "1.0"
chrono = "0.4.39"
crossbeam-utils = "0.8.21"
chrono = "0.4.37"
crossbeam-utils = "0.8.19"
flame = "0.2.2"
getrandom = "0.2.15"
getrandom = "0.2.12"
glob = "0.3"
hex = "0.4.3"
indexmap = { version = "2.2.6", features = ["std"] }
insta = "1.38.0"
itertools = "0.14.0"
is-macro = "0.3.7"
junction = "1.2.0"
libc = "0.2.169"
log = "0.4.25"
itertools = "0.11.0"
is-macro = "0.3.0"
junction = "1.0.0"
libc = "0.2.153"
log = "0.4.16"
nix = { version = "0.29", features = ["fs", "user", "process", "term", "time", "signal", "ioctl", "socket", "sched", "zerocopy", "dir", "hostname", "net", "poll"] }
malachite-bigint = "0.2.3"
malachite-q = "0.4.22"
malachite-base = "0.4.22"
memchr = "2.7.4"
num-complex = "0.4.6"
num-integer = "0.1.46"
malachite-bigint = "0.2.2"
malachite-q = "<=0.4.18"
malachite-base = "<=0.4.18"
memchr = "2.7.2"
num-complex = "0.4.0"
num-integer = "0.1.44"
num-traits = "0.2"
num_enum = { version = "0.7", default-features = false }
once_cell = "1.20.3"
parking_lot = "0.12.3"
paste = "1.0.15"
once_cell = "1.19.0"
parking_lot = "0.12.1"
paste = "1.0.7"
rand = "0.8.5"
rustix = { version = "0.38", features = ["event"] }
rustyline = "15.0.0"
rustyline = "14.0.0"
serde = { version = "1.0.133", default-features = false }
schannel = "0.1.27"
schannel = "0.1.22"
static_assertions = "1.1"
strum = "0.27"
strum_macros = "0.27"
strum = "0.26"
strum_macros = "0.26"
syn = "1.0.109"
thiserror = "2.0"
thread_local = "1.1.8"
unicode_names2 = "1.3.0"
thiserror = "1.0"
thread_local = "1.1.4"
unicode_names2 = "1.2.0"
widestring = "1.1.0"
windows-sys = "0.52.0"
wasm-bindgen = "0.2.100"
wasm-bindgen = "0.2.92"
# Lints

View File

@@ -25,7 +25,7 @@ RustPython requires the following:
stable version: `rustup update stable`
- If you do not have Rust installed, use [rustup](https://rustup.rs/) to
do so.
- CPython version 3.13 or higher
- CPython version 3.12 or higher
- CPython can be installed by your operating system's package manager,
from the [Python website](https://www.python.org/downloads/), or
using a third-party distribution, such as

67
Lib/_colorize.py vendored
View File

@@ -1,67 +0,0 @@
import io
import os
import sys
COLORIZE = True
class ANSIColors:
BOLD_GREEN = "\x1b[1;32m"
BOLD_MAGENTA = "\x1b[1;35m"
BOLD_RED = "\x1b[1;31m"
GREEN = "\x1b[32m"
GREY = "\x1b[90m"
MAGENTA = "\x1b[35m"
RED = "\x1b[31m"
RESET = "\x1b[0m"
YELLOW = "\x1b[33m"
NoColors = ANSIColors()
for attr in dir(NoColors):
if not attr.startswith("__"):
setattr(NoColors, attr, "")
def get_colors(colorize: bool = False, *, file=None) -> ANSIColors:
if colorize or can_colorize(file=file):
return ANSIColors()
else:
return NoColors
def can_colorize(*, file=None) -> bool:
if file is None:
file = sys.stdout
if not sys.flags.ignore_environment:
if os.environ.get("PYTHON_COLORS") == "0":
return False
if os.environ.get("PYTHON_COLORS") == "1":
return True
if os.environ.get("NO_COLOR"):
return False
if not COLORIZE:
return False
if os.environ.get("FORCE_COLOR"):
return True
if os.environ.get("TERM") == "dumb":
return False
if not hasattr(file, "fileno"):
return False
if sys.platform == "win32":
try:
import nt
if not nt._supports_virtual_terminal():
return False
except (ImportError, AttributeError):
return False
try:
return os.isatty(file.fileno())
except io.UnsupportedOperation:
return file.isatty()

19
Lib/difflib.py vendored
View File

@@ -1200,6 +1200,25 @@ def context_diff(a, b, fromfile='', tofile='',
strings for 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
The modification times are normally expressed in the ISO 8601 format.
If not specified, the strings default to blanks.
Example:
>>> print(''.join(context_diff('one\ntwo\nthree\nfour\n'.splitlines(True),
... 'zero\none\ntree\nfour\n'.splitlines(True), 'Original', 'Current')),
... end="")
*** Original
--- Current
***************
*** 1,4 ****
one
! two
! three
four
--- 1,4 ----
+ zero
one
! tree
four
"""
_check_types(a, b, fromfile, tofile, fromfiledate, tofiledate, lineterm)

View File

@@ -1,135 +0,0 @@
import contextlib
import io
import sys
import unittest
import unittest.mock
import _colorize
from test.support.os_helper import EnvironmentVarGuard
@contextlib.contextmanager
def clear_env():
with EnvironmentVarGuard() as mock_env:
for var in "FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS":
mock_env.unset(var)
yield mock_env
def supports_virtual_terminal():
if sys.platform == "win32":
return unittest.mock.patch("nt._supports_virtual_terminal", return_value=True)
else:
return contextlib.nullcontext()
class TestColorizeFunction(unittest.TestCase):
def test_colorized_detection_checks_for_environment_variables(self):
def check(env, fallback, expected):
with (self.subTest(env=env, fallback=fallback),
clear_env() as mock_env):
mock_env.update(env)
isatty_mock.return_value = fallback
stdout_mock.isatty.return_value = fallback
self.assertEqual(_colorize.can_colorize(), expected)
with (unittest.mock.patch("os.isatty") as isatty_mock,
unittest.mock.patch("sys.stdout") as stdout_mock,
supports_virtual_terminal()):
stdout_mock.fileno.return_value = 1
for fallback in False, True:
check({}, fallback, fallback)
check({'TERM': 'dumb'}, fallback, False)
check({'TERM': 'xterm'}, fallback, fallback)
check({'TERM': ''}, fallback, fallback)
check({'FORCE_COLOR': '1'}, fallback, True)
check({'FORCE_COLOR': '0'}, fallback, True)
check({'FORCE_COLOR': ''}, fallback, fallback)
check({'NO_COLOR': '1'}, fallback, False)
check({'NO_COLOR': '0'}, fallback, False)
check({'NO_COLOR': ''}, fallback, fallback)
check({'TERM': 'dumb', 'FORCE_COLOR': '1'}, False, True)
check({'FORCE_COLOR': '1', 'NO_COLOR': '1'}, True, False)
for ignore_environment in False, True:
# Simulate running with or without `-E`.
flags = unittest.mock.MagicMock(ignore_environment=ignore_environment)
with unittest.mock.patch("sys.flags", flags):
check({'PYTHON_COLORS': '1'}, True, True)
check({'PYTHON_COLORS': '1'}, False, not ignore_environment)
check({'PYTHON_COLORS': '0'}, True, ignore_environment)
check({'PYTHON_COLORS': '0'}, False, False)
for fallback in False, True:
check({'PYTHON_COLORS': 'x'}, fallback, fallback)
check({'PYTHON_COLORS': ''}, fallback, fallback)
check({'TERM': 'dumb', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
check({'NO_COLOR': '1', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
check({'FORCE_COLOR': '1', 'PYTHON_COLORS': '0'}, True, ignore_environment)
@unittest.skipUnless(sys.platform == "win32", "requires Windows")
def test_colorized_detection_checks_on_windows(self):
with (clear_env(),
unittest.mock.patch("os.isatty") as isatty_mock,
unittest.mock.patch("sys.stdout") as stdout_mock,
supports_virtual_terminal() as vt_mock):
stdout_mock.fileno.return_value = 1
isatty_mock.return_value = True
stdout_mock.isatty.return_value = True
vt_mock.return_value = True
self.assertEqual(_colorize.can_colorize(), True)
vt_mock.return_value = False
self.assertEqual(_colorize.can_colorize(), False)
import nt
del nt._supports_virtual_terminal
self.assertEqual(_colorize.can_colorize(), False)
def test_colorized_detection_checks_for_std_streams(self):
with (clear_env(),
unittest.mock.patch("os.isatty") as isatty_mock,
unittest.mock.patch("sys.stdout") as stdout_mock,
unittest.mock.patch("sys.stderr") as stderr_mock,
supports_virtual_terminal()):
stdout_mock.fileno.return_value = 1
stderr_mock.fileno.side_effect = ZeroDivisionError
stderr_mock.isatty.side_effect = ZeroDivisionError
isatty_mock.return_value = True
stdout_mock.isatty.return_value = True
self.assertEqual(_colorize.can_colorize(), True)
isatty_mock.return_value = False
stdout_mock.isatty.return_value = False
self.assertEqual(_colorize.can_colorize(), False)
def test_colorized_detection_checks_for_file(self):
with clear_env(), supports_virtual_terminal():
with unittest.mock.patch("os.isatty") as isatty_mock:
file = unittest.mock.MagicMock()
file.fileno.return_value = 1
isatty_mock.return_value = True
self.assertEqual(_colorize.can_colorize(file=file), True)
isatty_mock.return_value = False
self.assertEqual(_colorize.can_colorize(file=file), False)
# No file.fileno.
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
file = unittest.mock.MagicMock(spec=['isatty'])
file.isatty.return_value = True
self.assertEqual(_colorize.can_colorize(file=file), False)
# file.fileno() raises io.UnsupportedOperation.
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
file = unittest.mock.MagicMock()
file.fileno.side_effect = io.UnsupportedOperation
file.isatty.return_value = True
self.assertEqual(_colorize.can_colorize(file=file), True)
file.isatty.return_value = False
self.assertEqual(_colorize.can_colorize(file=file), False)
if __name__ == "__main__":
unittest.main()

30
Lib/test/test_enum.py vendored
View File

@@ -1369,12 +1369,10 @@ class TestSpecial(unittest.TestCase):
[Outer.a, Outer.b, Outer.Inner],
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(
python_version < (3, 13),
'inner classes are still members',
)
python_version < (3, 13),
'inner classes are still members',
)
def test_nested_classes_in_enum_are_not_members(self):
"""Support locally-defined nested classes."""
class Outer(Enum):
@@ -4557,24 +4555,20 @@ class TestInternals(unittest.TestCase):
self.assertEqual(Color.green.value, 3)
self.assertEqual(Color.yellow.value, 4)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(
python_version < (3, 13),
'inner classes are still members',
)
python_version < (3, 13),
'mixed types with auto() will raise in 3.13',
)
def test_auto_garbage_fail(self):
with self.assertRaisesRegex(TypeError, 'will require all values to be sortable'):
class Color(Enum):
red = 'red'
blue = auto()
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipIf(
python_version < (3, 13),
'inner classes are still members',
)
python_version < (3, 13),
'mixed types with auto() will raise in 3.13',
)
def test_auto_garbage_corrected_fail(self):
with self.assertRaisesRegex(TypeError, 'will require all values to be sortable'):
class Color(Enum):
@@ -4604,9 +4598,9 @@ class TestInternals(unittest.TestCase):
self.assertEqual(Color.blue.value, 'blue')
@unittest.skipIf(
python_version < (3, 13),
'inner classes are still members',
)
python_version < (3, 13),
'auto() will return highest value + 1 in 3.13',
)
def test_auto_with_aliases(self):
class Color(Enum):
red = auto()

View File

@@ -1012,6 +1012,8 @@ def gamma(z, sqrt2pi=(2.0*pi)**0.5):
])
class TestDistributions(unittest.TestCase):
# TODO: RUSTPYTHON ValueError: math domain error
@unittest.expectedFailure
def test_zeroinputs(self):
# Verify that distributions can handle a series of zero inputs'
g = random.Random()

View File

@@ -2,7 +2,7 @@
# [RustPython](https://rustpython.github.io/)
A Python-3 (CPython >= 3.13.0) Interpreter written in Rust :snake: :scream:
A Python-3 (CPython >= 3.12.0) Interpreter written in Rust :snake: :scream:
:metal:.
[![Build Status](https://github.com/RustPython/RustPython/workflows/CI/badge.svg)](https://github.com/RustPython/RustPython/actions?query=workflow%3ACI)

View File

@@ -322,37 +322,6 @@ pub mod levenshtein {
}
}
/// Replace all tabs in a string with spaces, using the given tab size.
pub fn expandtabs(input: &str, tab_size: usize) -> String {
let tab_stop = tab_size;
let mut expanded_str = String::with_capacity(input.len());
let mut tab_size = tab_stop;
let mut col_count = 0usize;
for ch in input.chars() {
match ch {
'\t' => {
let num_spaces = tab_size - col_count;
col_count += num_spaces;
let expand = " ".repeat(num_spaces);
expanded_str.push_str(&expand);
}
'\r' | '\n' => {
expanded_str.push(ch);
col_count = 0;
tab_size = 0;
}
_ => {
expanded_str.push(ch);
col_count += 1;
}
}
if col_count >= tab_size {
tab_size += tab_stop;
}
}
expanded_str
}
/// Creates an [`AsciiStr`][ascii::AsciiStr] from a string literal, throwing a compile error if the
/// literal isn't actually ascii.
///

View File

@@ -11,7 +11,6 @@ license.workspace = true
[dependencies]
rustpython-ast = { workspace = true, features=["unparse", "constant-optimization"] }
rustpython-common = { workspace = true }
rustpython-parser-core = { workspace = true }
rustpython-compiler-core = { workspace = true }

View File

@@ -1095,27 +1095,8 @@ impl Compiler {
self.store_name(name.as_ref())?;
}
}
located_ast::TypeParam::ParamSpec(located_ast::TypeParamParamSpec {
name, ..
}) => {
self.emit_load_const(ConstantData::Str {
value: name.to_string(),
});
emit!(self, Instruction::ParamSpec);
emit!(self, Instruction::Duplicate);
self.store_name(name.as_ref())?;
}
located_ast::TypeParam::TypeVarTuple(located_ast::TypeParamTypeVarTuple {
name,
..
}) => {
self.emit_load_const(ConstantData::Str {
value: name.to_string(),
});
emit!(self, Instruction::TypeVarTuple);
emit!(self, Instruction::Duplicate);
self.store_name(name.as_ref())?;
}
located_ast::TypeParam::ParamSpec(_) => todo!(),
located_ast::TypeParam::TypeVarTuple(_) => todo!(),
};
}
emit!(
@@ -2723,7 +2704,7 @@ impl Compiler {
fn compile_keywords(&mut self, keywords: &[located_ast::Keyword]) -> CompileResult<()> {
let mut size = 0;
let groupby = keywords.iter().chunk_by(|e| e.arg.is_none());
let groupby = keywords.iter().group_by(|e| e.arg.is_none());
for (is_unpacking, sub_keywords) in &groupby {
if is_unpacking {
for keyword in sub_keywords {
@@ -2886,7 +2867,7 @@ impl Compiler {
(false, element)
}
})
.chunk_by(|(starred, _)| *starred);
.group_by(|(starred, _)| *starred);
for (starred, run) in &groups {
let mut run_size = 0;
@@ -3368,51 +3349,17 @@ impl EmitArg<bytecode::Label> for ir::BlockIdx {
}
}
/// Strips leading whitespace from a docstring.
///
/// The code has been ported from `_PyCompile_CleanDoc` in cpython.
/// `inspect.cleandoc` is also a good reference, but has a few incompatibilities.
fn clean_doc(doc: &str) -> String {
let doc = rustpython_common::str::expandtabs(doc, 8);
// First pass: find minimum indentation of any non-blank lines
// after first line.
let margin = doc
.lines()
// Find the non-blank lines
.filter(|line| !line.trim().is_empty())
// get the one with the least indentation
.map(|line| line.chars().take_while(|c| c == &' ').count())
.min();
if let Some(margin) = margin {
let mut cleaned = String::with_capacity(doc.len());
// copy first line without leading whitespace
if let Some(first_line) = doc.lines().next() {
cleaned.push_str(first_line.trim_start());
}
// copy subsequent lines without margin.
for line in doc.split('\n').skip(1) {
cleaned.push('\n');
let cleaned_line = line.chars().skip(margin).collect::<String>();
cleaned.push_str(&cleaned_line);
}
cleaned
} else {
doc.to_owned()
}
}
fn split_doc<'a>(
body: &'a [located_ast::Stmt],
opts: &CompileOpts,
) -> (Option<String>, &'a [located_ast::Stmt]) {
if let Some((located_ast::Stmt::Expr(expr), body_rest)) = body.split_first() {
if let Some(doc) = try_get_constant_string(std::slice::from_ref(&expr.value)) {
return if opts.optimize < 2 {
(Some(clean_doc(&doc)), body_rest)
if opts.optimize < 2 {
return (Some(doc), body_rest);
} else {
(None, body_rest)
};
return (None, body_rest);
}
}
}
(None, body)

View File

@@ -1257,18 +1257,8 @@ impl SymbolTableBuilder {
self.scan_expression(binding, ExpressionContext::Load)?;
}
}
ast::located::TypeParam::ParamSpec(ast::TypeParamParamSpec {
name,
range: param_spec_range,
}) => {
self.register_name(name, SymbolUsage::Assigned, param_spec_range.start)?;
}
ast::located::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple {
name,
range: type_var_tuple_range,
}) => {
self.register_name(name, SymbolUsage::Assigned, type_var_tuple_range.start)?;
}
ast::located::TypeParam::ParamSpec(_) => todo!(),
ast::located::TypeParam::TypeVarTuple(_) => todo!(),
}
}
Ok(())

View File

@@ -15,7 +15,7 @@ bitflags = { workspace = true }
itertools = { workspace = true }
malachite-bigint = { workspace = true }
num-complex = { workspace = true }
serde = { version = "1.0.217", optional = true, default-features = false, features = ["derive"] }
serde = { version = "1.0.133", optional = true, default-features = false, features = ["derive"] }
lz4_flex = "0.11"

View File

@@ -595,12 +595,10 @@ pub enum Instruction {
TypeVarWithBound,
TypeVarWithConstraint,
TypeAlias,
TypeVarTuple,
ParamSpec,
// If you add a new instruction here, be sure to keep LAST_INSTRUCTION updated
}
// This must be kept up to date to avoid marshaling errors
const LAST_INSTRUCTION: Instruction = Instruction::ParamSpec;
const LAST_INSTRUCTION: Instruction = Instruction::TypeAlias;
const _: () = assert!(mem::size_of::<Instruction>() == 1);
impl From<Instruction> for u8 {
@@ -1293,8 +1291,6 @@ impl Instruction {
TypeVarWithBound => -1,
TypeVarWithConstraint => -1,
TypeAlias => -2,
ParamSpec => 0,
TypeVarTuple => 0,
}
}
@@ -1464,8 +1460,6 @@ impl Instruction {
TypeVarWithBound => w!(TypeVarWithBound),
TypeVarWithConstraint => w!(TypeVarWithConstraint),
TypeAlias => w!(TypeAlias),
ParamSpec => w!(ParamSpec),
TypeVarTuple => w!(TypeVarTuple),
}
}
}

View File

@@ -18,10 +18,10 @@ once_cell = { workspace = true }
syn = { workspace = true, features = ["full", "extra-traits"] }
maplit = "1.0.2"
proc-macro2 = "1.0.93"
quote = "1.0.38"
proc-macro2 = "1.0.79"
quote = "1.0.18"
syn-ext = { version = "0.4.0", features = ["full"] }
textwrap = { version = "0.16.1", default-features = false }
textwrap = { version = "0.15.0", default-features = false }
[lints]
workspace = true

View File

@@ -7,7 +7,9 @@ myobj = MyObject()
assert myobj == myobj
assert not myobj != myobj
object.__subclasshook__() == NotImplemented
object.__subclasshook__(1) == NotImplemented
object.__subclasshook__(1, 2) == NotImplemented
assert MyObject().__eq__(MyObject()) == NotImplemented
assert MyObject().__ne__(MyObject()) == NotImplemented

View File

@@ -1,105 +0,0 @@
from _ctypes import sizeof
from _ctypes import _SimpleCData
from struct import calcsize as _calcsize
def _check_size(typ, typecode=None):
# Check if sizeof(ctypes_type) against struct.calcsize. This
# should protect somewhat against a misconfigured libffi.
from struct import calcsize
if typecode is None:
# Most _type_ codes are the same as used in struct
typecode = typ._type_
actual, required = sizeof(typ), calcsize(typecode)
if actual != required:
raise SystemError("sizeof(%s) wrong: %d instead of %d" % \
(typ, actual, required))
class c_short(_SimpleCData):
_type_ = "h"
_check_size(c_short)
class c_ushort(_SimpleCData):
_type_ = "H"
_check_size(c_ushort)
class c_long(_SimpleCData):
_type_ = "l"
_check_size(c_long)
class c_ulong(_SimpleCData):
_type_ = "L"
_check_size(c_ulong)
if _calcsize("i") == _calcsize("l"):
# if int and long have the same size, make c_int an alias for c_long
c_int = c_long
c_uint = c_ulong
else:
class c_int(_SimpleCData):
_type_ = "i"
_check_size(c_int)
class c_uint(_SimpleCData):
_type_ = "I"
_check_size(c_uint)
class c_float(_SimpleCData):
_type_ = "f"
_check_size(c_float)
class c_double(_SimpleCData):
_type_ = "d"
_check_size(c_double)
class c_longdouble(_SimpleCData):
_type_ = "g"
if sizeof(c_longdouble) == sizeof(c_double):
c_longdouble = c_double
if _calcsize("l") == _calcsize("q"):
# if long and long long have the same size, make c_longlong an alias for c_long
c_longlong = c_long
c_ulonglong = c_ulong
else:
class c_longlong(_SimpleCData):
_type_ = "q"
_check_size(c_longlong)
class c_ulonglong(_SimpleCData):
_type_ = "Q"
## def from_param(cls, val):
## return ('d', float(val), val)
## from_param = classmethod(from_param)
_check_size(c_ulonglong)
class c_ubyte(_SimpleCData):
_type_ = "B"
c_ubyte.__ctype_le__ = c_ubyte.__ctype_be__ = c_ubyte
# backward compatibility:
##c_uchar = c_ubyte
_check_size(c_ubyte)
class c_byte(_SimpleCData):
_type_ = "b"
c_byte.__ctype_le__ = c_byte.__ctype_be__ = c_byte
_check_size(c_byte)
class c_char(_SimpleCData):
_type_ = "c"
c_char.__ctype_le__ = c_char.__ctype_be__ = c_char
_check_size(c_char)
class c_char_p(_SimpleCData):
_type_ = "z"
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, c_void_p.from_buffer(self).value)
_check_size(c_char_p, "P")
class c_void_p(_SimpleCData):
_type_ = "P"
c_voidp = c_void_p # backwards compatibility (to a bug)
_check_size(c_void_p)
class c_bool(_SimpleCData):
_type_ = "?"
_check_size(c_bool)

View File

@@ -0,0 +1,27 @@
# unittest for modified imghdr.py
# Should be replace it into https://github.com/python/cpython/blob/main/Lib/test/test_imghdr.py
import os
import imghdr
TEST_FILES = (
#('python.png', 'png'),
('python.gif', 'gif'),
('python.bmp', 'bmp'),
('python.ppm', 'ppm'),
('python.pgm', 'pgm'),
('python.pbm', 'pbm'),
('python.jpg', 'jpeg'),
('python.ras', 'rast'),
#('python.sgi', 'rgb'),
('python.tiff', 'tiff'),
('python.xbm', 'xbm'),
('python.webp', 'webp'),
('python.exr', 'exr'),
)
resource_dir = os.path.join(os.path.dirname(__file__), 'imghdrdata')
for fname, expected in TEST_FILES:
res = imghdr.what(os.path.join(resource_dir, fname))
assert res == expected

View File

@@ -32,7 +32,7 @@ p = subprocess.Popen(sleep(2))
assert p.poll() is None
with assert_raises(subprocess.TimeoutExpired):
assert p.wait(1)
assert p.wait(1)
p.wait()
@@ -48,17 +48,17 @@ p = subprocess.Popen(sleep(2))
p.terminate()
p.wait()
if is_unix:
assert p.returncode == -signal.SIGTERM
assert p.returncode == -signal.SIGTERM
else:
assert p.returncode == 1
assert p.returncode == 1
p = subprocess.Popen(sleep(2))
p.kill()
p.wait()
if is_unix:
assert p.returncode == -signal.SIGKILL
assert p.returncode == -signal.SIGKILL
else:
assert p.returncode == 1
assert p.returncode == 1
p = subprocess.Popen(echo("test"), stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate()
@@ -66,4 +66,4 @@ assert stdout.strip() == b"test"
p = subprocess.Popen(sleep(5), stdout=subprocess.PIPE)
with assert_raises(subprocess.TimeoutExpired):
p.communicate(timeout=1)
p.communicate(timeout=1)

View File

@@ -0,0 +1,12 @@
# This probably will be superceeded by the python unittests when that works.
import xdrlib
p = xdrlib.Packer()
p.pack_int(1337)
d = p.get_buffer()
print(d)
# assert d == b'\x00\x00\x059'

View File

@@ -50,7 +50,7 @@ class Bar:
assert x == 3
assert Bar.__doc__ == "W00t "
assert Bar.__doc__ == " W00t "
bar = Bar(42)
assert bar.get_x.__doc__ == None
@@ -147,7 +147,7 @@ class T3:
test3
"""
assert T3.__doc__ == "\ntest3\n"
assert T3.__doc__ == "\n test3\n "
class T4:

View File

@@ -1,15 +0,0 @@
def f1():
"""
x
\ty
"""
assert f1.__doc__ == '\nx\ny\n'
def f2():
"""
\t x
\t\ty
"""
assert f2.__doc__ == '\nx\n y\n'

View File

@@ -44,7 +44,7 @@ def f3():
"""
pass
assert f3.__doc__ == "\ntest3\n"
assert f3.__doc__ == "\n test3\n "
def f4():
"test4"

View File

@@ -46,15 +46,15 @@ thread_local = { workspace = true }
memchr = { workspace = true }
base64 = "0.13.0"
csv-core = "0.1.11"
csv-core = "0.1.10"
dyn-clone = "1.0.10"
libz-sys = { version = "1.1", default-features = false, optional = true }
puruspe = "0.3.0"
puruspe = "0.2.4"
xml-rs = "0.8.14"
# random
rand = { workspace = true }
rand_core = "0.6.4"
rand_core = "0.6.3"
mt19937 = "2.0.1"
# Crypto:
@@ -92,8 +92,8 @@ uuid = { version = "1.1.2", features = ["v1", "fast-rng"] }
# mmap
[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies]
memmap2 = "0.5.10"
page_size = "0.6"
memmap2 = "0.5.4"
page_size = "0.4"
[target.'cfg(all(unix, not(target_os = "redox"), not(target_os = "ios")))'.dependencies]
termios = "0.3.3"
@@ -102,8 +102,8 @@ termios = "0.3.3"
rustix = { workspace = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
gethostname = "1.0.0"
socket2 = { version = "0.5.8", features = ["all"] }
gethostname = "0.2.3"
socket2 = { version = "0.5.6", features = ["all"] }
dns-lookup = "2"
openssl = { version = "0.10.66", optional = true }
openssl-sys = { version = "0.9.80", optional = true }
@@ -136,7 +136,7 @@ features = [
]
[target.'cfg(target_os = "macos")'.dependencies]
system-configuration = "0.5.1"
system-configuration = "0.5.0"
[lints]
workspace = true

View File

@@ -132,9 +132,6 @@ mod math {
#[pyfunction]
fn log(x: PyObjectRef, base: OptionalArg<ArgIntoFloat>, vm: &VirtualMachine) -> PyResult<f64> {
let base = base.map(|b| *b).unwrap_or(std::f64::consts::E);
if base.is_sign_negative() {
return Err(vm.new_value_error("math domain error".to_owned()));
}
log2(x, vm).map(|logx| logx / base.log2())
}
@@ -195,18 +192,16 @@ mod math {
let x = *x;
let y = *y;
if x < 0.0 && x.is_finite() && y.fract() != 0.0 && y.is_finite()
|| x == 0.0 && y < 0.0 && y != f64::NEG_INFINITY
{
if x < 0.0 && x.is_finite() && y.fract() != 0.0 && y.is_finite() {
return Err(vm.new_value_error("math domain error".to_owned()));
}
if x == 0.0 && y < 0.0 && y != f64::NEG_INFINITY {
return Err(vm.new_value_error("math domain error".to_owned()));
}
let value = x.powf(y);
if x.is_finite() && y.is_finite() && value.is_infinite() {
return Err(vm.new_overflow_error("math range error".to_string()));
}
Ok(value)
}
@@ -217,9 +212,6 @@ mod math {
return Ok(value);
}
if value.is_sign_negative() {
if value.is_zero() {
return Ok(-0.0f64);
}
return Err(vm.new_value_error("math domain error".to_owned()));
}
Ok(value.sqrt())
@@ -268,9 +260,6 @@ mod math {
#[pyfunction]
fn cos(x: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<f64> {
if x.is_infinite() {
return Err(vm.new_value_error("math domain error".to_owned()));
}
call_math_func!(cos, x, vm)
}
@@ -356,7 +345,7 @@ mod math {
.map(|x| (x / scale).powi(2))
.chain(std::iter::once(-norm * norm))
// Pairwise summation of floats gives less rounding error than a naive sum.
.tree_reduce(std::ops::Add::add)
.tree_fold1(std::ops::Add::add)
.expect("expected at least 1 element");
norm = norm + correction / (2.0 * norm);
}
@@ -405,17 +394,11 @@ mod math {
#[pyfunction]
fn sin(x: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<f64> {
if x.is_infinite() {
return Err(vm.new_value_error("math domain error".to_owned()));
}
call_math_func!(sin, x, vm)
}
#[pyfunction]
fn tan(x: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<f64> {
if x.is_infinite() {
return Err(vm.new_value_error("math domain error".to_owned()));
}
call_math_func!(tan, x, vm)
}

View File

@@ -612,11 +612,7 @@ mod _ssl {
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()])
ssl::select_next_proto(&server, client).ok_or(ssl::AlpnError::NOACK)
});
Ok(())
}

View File

@@ -99,8 +99,6 @@ uname = "0.1.1"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rustyline = { workspace = true }
which = "6"
errno = "0.3"
widestring = { workspace = true }
[target.'cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))'.dependencies]
num_cpus = "1.13.1"
@@ -108,6 +106,7 @@ num_cpus = "1.13.1"
[target.'cfg(windows)'.dependencies]
junction = { workspace = true }
schannel = { workspace = true }
widestring = { workspace = true }
winreg = "0.52"
[target.'cfg(windows)'.dependencies.windows]

View File

@@ -495,9 +495,18 @@ pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyDict
obj.dict()
.ok_or_else(|| vm.new_attribute_error("This object has no __dict__".to_owned()))
}
pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -> PyResult<()> {
obj.set_dict(dict)
.map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned()))
pub fn object_set_dict(obj: PyObjectRef, dict: PySetterValue<PyDictRef>, vm: &VirtualMachine) -> PyResult<()> {
match dict {
PySetterValue::Assign(dict) => {
obj.set_dict(dict)
.map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned()))
}
PySetterValue::Delete => {
obj.delete_dict()
.map_err(|_| vm.new_attribute_error("This object has no deletable __dict__".to_owned()))
}
}
}
pub fn init(ctx: &Context) {

View File

@@ -1125,7 +1125,33 @@ impl PyStr {
#[pymethod]
fn expandtabs(&self, args: anystr::ExpandTabsArgs) -> String {
rustpython_common::str::expandtabs(self.as_str(), args.tabsize())
let tab_stop = args.tabsize();
let mut expanded_str = String::with_capacity(self.byte_len());
let mut tab_size = tab_stop;
let mut col_count = 0usize;
for ch in self.as_str().chars() {
match ch {
'\t' => {
let num_spaces = tab_size - col_count;
col_count += num_spaces;
let expand = " ".repeat(num_spaces);
expanded_str.push_str(&expand);
}
'\r' | '\n' => {
expanded_str.push(ch);
col_count = 0;
tab_size = 0;
}
_ => {
expanded_str.push(ch);
col_count += 1;
}
}
if col_count >= tab_size {
tab_size += tab_stop;
}
}
expanded_str
}
#[pymethod]

View File

@@ -1288,7 +1288,7 @@ fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -
descr_set(&descr, obj, PySetterValue::Assign(value), vm)
}
None => {
object::object_set_dict(obj, value.try_into_value(vm)?, vm)?;
object::object_set_dict(obj, PySetterValue::Delete, vm)?;
Ok(())
}
}

View File

@@ -1202,23 +1202,6 @@ impl ExecutingFrame<'_> {
self.push_value(type_alias.into_ref(&vm.ctx).into());
Ok(None)
}
bytecode::Instruction::ParamSpec => {
let param_spec_name = self.pop_value();
let param_spec: PyObjectRef = _typing::make_paramspec(param_spec_name.clone())
.into_ref(&vm.ctx)
.into();
self.push_value(param_spec);
Ok(None)
}
bytecode::Instruction::TypeVarTuple => {
let type_var_tuple_name = self.pop_value();
let type_var_tuple: PyObjectRef =
_typing::make_typevartuple(type_var_tuple_name.clone())
.into_ref(&vm.ctx)
.into();
self.push_value(type_var_tuple);
Ok(None)
}
}
}

View File

@@ -36,8 +36,10 @@ where
{
#[inline]
fn from_setter_value(vm: &VirtualMachine, obj: PySetterValue) -> PyResult<Self> {
let obj = obj.ok_or_else(|| vm.new_type_error("can't delete attribute".to_owned()))?;
T::try_from_object(vm, obj)
match obj {
PySetterValue::Assign(obj) => T::try_from_object(vm, obj),
PySetterValue::Delete => T::try_from_object(vm, vm.ctx.none()),
}
}
}

View File

@@ -717,6 +717,19 @@ impl PyObject {
}
}
pub fn delete_dict(&self) -> Result<(), ()> {
match self.instance_dict() {
Some(_) => {
unsafe {
let ptr = self as *const _ as *mut PyObject;
(*ptr).0.dict = None;
}
Ok(())
}
None => Err(()),
}
}
#[inline(always)]
pub fn payload_if_subclass<T: crate::PyPayload>(&self, vm: &VirtualMachine) -> Option<&T> {
if self.class().fast_issubclass(T::class(&vm.ctx)) {

View File

@@ -1,107 +0,0 @@
pub(crate) mod base;
use crate::builtins::PyModule;
use crate::{PyRef, VirtualMachine};
pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
let module = _ctypes::make_module(vm);
base::extend_module_nodes(vm, &module);
module
}
#[pymodule]
pub(crate) mod _ctypes {
use super::base::PyCSimple;
use crate::builtins::PyTypeRef;
use crate::class::StaticType;
use crate::function::Either;
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use std::ffi::{
c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, c_ulong,
c_ulonglong,
};
use std::mem;
use widestring::WideChar;
pub fn get_size(ty: &str) -> usize {
match ty {
"u" => mem::size_of::<WideChar>(),
"c" | "b" => mem::size_of::<c_schar>(),
"h" => mem::size_of::<c_short>(),
"H" => mem::size_of::<c_short>(),
"i" => mem::size_of::<c_int>(),
"I" => mem::size_of::<c_uint>(),
"l" => mem::size_of::<c_long>(),
"q" => mem::size_of::<c_longlong>(),
"L" => mem::size_of::<c_ulong>(),
"Q" => mem::size_of::<c_ulonglong>(),
"f" => mem::size_of::<c_float>(),
"d" | "g" => mem::size_of::<c_double>(),
"?" | "B" => mem::size_of::<c_uchar>(),
"P" | "z" | "Z" => mem::size_of::<usize>(),
_ => unreachable!(),
}
}
const SIMPLE_TYPE_CHARS: &str = "cbBhHiIlLdfguzZPqQ?";
pub fn new_simple_type(
cls: Either<&PyObjectRef, &PyTypeRef>,
vm: &VirtualMachine,
) -> PyResult<PyCSimple> {
let cls = match cls {
Either::A(obj) => obj,
Either::B(typ) => typ.as_object(),
};
if let Ok(_type_) = cls.get_attr("_type_", vm) {
if _type_.is_instance((&vm.ctx.types.str_type).as_ref(), vm)? {
let tp_str = _type_.str(vm)?.to_string();
if tp_str.len() != 1 {
Err(vm.new_value_error(
format!("class must define a '_type_' attribute which must be a string of length 1, str: {tp_str}"),
))
} else if !SIMPLE_TYPE_CHARS.contains(tp_str.as_str()) {
Err(vm.new_attribute_error(format!("class must define a '_type_' attribute which must be\n a single character string containing one of {SIMPLE_TYPE_CHARS}, currently it is {tp_str}.")))
} else {
Ok(PyCSimple {
_type_: tp_str,
_value: AtomicCell::new(vm.ctx.none()),
})
}
} else {
Err(vm.new_type_error("class must define a '_type_' string attribute".to_string()))
}
} else {
Err(vm.new_attribute_error("class must define a '_type_' attribute".to_string()))
}
}
#[pyfunction(name = "sizeof")]
pub fn size_of(tp: Either<PyTypeRef, PyObjectRef>, vm: &VirtualMachine) -> PyResult<usize> {
match tp {
Either::A(type_) if type_.fast_issubclass(PyCSimple::static_type()) => {
let zelf = new_simple_type(Either::B(&type_), vm)?;
Ok(get_size(zelf._type_.as_str()))
}
Either::B(obj) if obj.has_attr("size_of_instances", vm)? => {
let size_of_method = obj.get_attr("size_of_instances", vm)?;
let size_of_return = size_of_method.call(vec![], vm)?;
Ok(usize::try_from_object(vm, size_of_return)?)
}
_ => Err(vm.new_type_error("this type has no size".to_string())),
}
}
#[pyfunction]
fn get_errno() -> i32 {
errno::errno().0
}
#[pyfunction]
fn set_errno(value: i32) {
errno::set_errno(errno::Errno(value));
}
}

View File

@@ -1,161 +0,0 @@
use crate::builtins::{PyBytes, PyFloat, PyInt, PyModule, PyNone, PyStr};
use crate::class::PyClassImpl;
use crate::{Py, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::ToPrimitive;
use rustpython_common::lock::PyRwLock;
use std::fmt::Debug;
#[allow(dead_code)]
fn set_primitive(_type_: &str, value: &PyObjectRef, vm: &VirtualMachine) -> PyResult {
match _type_ {
"c" => {
if value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyBytes>(vm)
.is_ok_and(|v| v.len() == 1)
|| value
.clone()
.downcast_exact::<PyInt>(vm)
.map_or(Ok(false), |v| {
let n = v.as_bigint().to_i64();
if let Some(n) = n {
Ok((0..=255).contains(&n))
} else {
Ok(false)
}
})?
{
Ok(value.clone())
} else {
Err(vm.new_type_error(
"one character bytes, bytearray or integer expected".to_string(),
))
}
}
"u" => {
if let Ok(b) = value.str(vm).map(|v| v.to_string().chars().count() == 1) {
if b {
Ok(value.clone())
} else {
Err(vm.new_type_error("one character unicode string expected".to_string()))
}
} else {
Err(vm.new_type_error(format!(
"unicode string expected instead of {} instance",
value.class().name()
)))
}
}
"b" | "h" | "H" | "i" | "I" | "l" | "q" | "L" | "Q" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"an integer is required (got type {})",
value.class().name()
)))
}
}
"f" | "d" | "g" => {
if value.clone().downcast_exact::<PyFloat>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!("must be real number, not {}", value.class().name())))
}
}
"?" => Ok(PyObjectRef::from(
vm.ctx.new_bool(value.clone().try_to_bool(vm)?),
)),
"B" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok() {
Ok(vm.new_pyobj(u8::try_from_object(vm, value.clone())?))
} else {
Err(vm.new_type_error(format!("int expected instead of {}", value.class().name())))
}
}
"z" => {
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyBytes>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"bytes or integer address expected instead of {} instance",
value.class().name()
)))
}
}
"Z" => {
if value.clone().downcast_exact::<PyStr>(vm).is_ok() {
Ok(value.clone())
} else {
Err(vm.new_type_error(format!(
"unicode string or integer address expected instead of {} instance",
value.class().name()
)))
}
}
_ => {
// "P"
if value.clone().downcast_exact::<PyInt>(vm).is_ok()
|| value.clone().downcast_exact::<PyNone>(vm).is_ok()
{
Ok(value.clone())
} else {
Err(vm.new_type_error("cannot be converted to pointer".to_string()))
}
}
}
}
pub struct RawBuffer {
#[allow(dead_code)]
pub inner: Box<[u8]>,
#[allow(dead_code)]
pub size: usize,
}
#[pyclass(name = "_CData", module = "_ctypes")]
pub struct PyCData {
_objects: AtomicCell<Vec<PyObjectRef>>,
_buffer: PyRwLock<RawBuffer>,
}
#[pyclass]
impl PyCData {}
#[pyclass(
name = "_SimpleCData",
base = "PyCData",
module = "_ctypes"
// TODO: metaclass
)]
#[derive(PyPayload)]
pub struct PyCSimple {
pub _type_: String,
pub _value: AtomicCell<PyObjectRef>,
}
impl Debug for PyCSimple {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PyCSimple")
.field("_type_", &self._type_)
.finish()
}
}
#[pyclass(flags(BASETYPE))]
impl PyCSimple {}
pub fn extend_module_nodes(vm: &VirtualMachine, module: &Py<PyModule>) {
let ctx = &vm.ctx;
extend_module!(vm, module, {
"_CData" => PyCData::make_class(ctx),
"_SimpleCData" => PyCSimple::make_class(ctx),
})
}

View File

@@ -37,8 +37,6 @@ pub mod posix;
#[path = "posix_compat.rs"]
pub mod posix;
#[cfg(any(target_family = "unix", target_family = "windows"))]
mod ctypes;
#[cfg(windows)]
pub(crate) mod msvcrt;
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
@@ -126,9 +124,5 @@ pub fn get_module_inits() -> StdlibMap {
"_winapi" => winapi::make_module,
"winreg" => winreg::make_module,
}
#[cfg(any(target_family = "unix", target_family = "windows"))]
{
"_ctypes" => ctypes::make_module,
}
}
}

View File

@@ -43,12 +43,6 @@ pub(crate) mod module {
|| attr & FileSystem::FILE_ATTRIBUTE_DIRECTORY != 0))
}
#[pyfunction]
pub(super) fn _supports_virtual_terminal() -> PyResult<bool> {
// TODO: implement this
Ok(true)
}
#[derive(FromArgs)]
pub(super) struct SymlinkArgs {
src: OsPath,

View File

@@ -75,31 +75,18 @@ pub(crate) mod _typing {
#[pyclass(name = "ParamSpec")]
#[derive(Debug, PyPayload)]
#[allow(dead_code)]
pub(crate) struct ParamSpec {
name: PyObjectRef,
}
struct ParamSpec {}
#[pyclass(flags(BASETYPE))]
impl ParamSpec {}
pub(crate) fn make_paramspec(name: PyObjectRef) -> ParamSpec {
ParamSpec { name }
}
#[pyattr]
#[pyclass(name = "TypeVarTuple")]
#[derive(Debug, PyPayload)]
#[allow(dead_code)]
pub(crate) struct TypeVarTuple {
name: PyObjectRef,
}
pub(crate) struct TypeVarTuple {}
#[pyclass(flags(BASETYPE))]
impl TypeVarTuple {}
pub(crate) fn make_typevartuple(name: PyObjectRef) -> TypeVarTuple {
TypeVarTuple { name }
}
#[pyattr]
#[pyclass(name = "ParamSpecArgs")]
#[derive(Debug, PyPayload)]

View File

@@ -4,9 +4,9 @@
use chrono::{prelude::DateTime, Local};
use std::time::{Duration, UNIX_EPOCH};
// = 3.13.0alpha
// = 3.12.0alpha
pub const MAJOR: usize = 3;
pub const MINOR: usize = 13;
pub const MINOR: usize = 12;
pub const MICRO: usize = 0;
pub const RELEASELEVEL: &str = "alpha";
pub const RELEASELEVEL_N: usize = 0xA;

View File

@@ -35,8 +35,8 @@ GENERATED_FILE = "extra_tests/not_impl.py"
implementation = platform.python_implementation()
if implementation != "CPython":
sys.exit(f"whats_left.py must be run under CPython, got {implementation} instead")
if sys.version_info[:2] < (3, 13):
sys.exit(f"whats_left.py must be run under CPython 3.13 or newer, got {implementation} {sys.version} instead")
if sys.version_info[:2] < (3, 12):
sys.exit(f"whats_left.py must be run under CPython 3.12 or newer, got {implementation} {sys.version} instead")
def parse_args():
parser = argparse.ArgumentParser(description="Process some integers.")