mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Compare commits
7 Commits
2025-02-17
...
impl_del_o
| Author | SHA1 | Date | |
|---|---|---|---|
| 20c053a3c0 | |||
|
|
6181bf4a7a | ||
| b333ffa781 | |||
| 308b95ec13 | |||
| 5f77ce5b4f | |||
| 4da57d42de | |||
| 2777588bb4 |
23
.github/workflows/ai-review.yml
vendored
Normal file
23
.github/workflows/ai-review.yml
vendored
Normal 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"
|
||||
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/cron-ci.yaml
vendored
2
.github/workflows/cron-ci.yaml
vendored
@@ -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
725
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
64
Cargo.toml
64
Cargo.toml
@@ -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
|
||||
|
||||
|
||||
@@ -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
67
Lib/_colorize.py
vendored
@@ -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
19
Lib/difflib.py
vendored
@@ -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)
|
||||
|
||||
135
Lib/test/test__colorize.py
vendored
135
Lib/test/test__colorize.py
vendored
@@ -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
30
Lib/test/test_enum.py
vendored
@@ -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()
|
||||
|
||||
2
Lib/test/test_random.py
vendored
2
Lib/test/test_random.py
vendored
@@ -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()
|
||||
|
||||
@@ -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:.
|
||||
|
||||
[](https://github.com/RustPython/RustPython/actions?query=workflow%3ACI)
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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 }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
27
extra_tests/snippets/stdlib_imghdr.py
Normal file
27
extra_tests/snippets/stdlib_imghdr.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
12
extra_tests/snippets/stdlib_xdrlib.py
Normal file
12
extra_tests/snippets/stdlib_xdrlib.py
Normal 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'
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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'
|
||||
@@ -44,7 +44,7 @@ def f3():
|
||||
"""
|
||||
pass
|
||||
|
||||
assert f3.__doc__ == "\ntest3\n"
|
||||
assert f3.__doc__ == "\n test3\n "
|
||||
|
||||
def f4():
|
||||
"test4"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
})
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user