Merge branch 'main' of github.com:chrismoradi/RustPython into unimplemented-abstract-methods-raise-error

This commit is contained in:
Chris Moradi committed 2021-11-22 14:58:17 -08:00
commit a685b236f9
76 files changed
+2293 -1268

No files matched your search

Generated
+10 -2
View File
@@ -1315,6 +1315,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "optional"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978aa494585d3ca4ad74929863093e87cac9790d81fe7aba2b3dc2890643a0fc"
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -1524,9 +1530,9 @@ dependencies = [
[[package]]
name = "radium"
version = "0.6.2"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "radix_trie"
@@ -1954,6 +1960,7 @@ dependencies = [
"libc",
"log",
"memchr",
"memoffset",
"nix 0.23.0",
"num-bigint",
"num-complex",
@@ -1963,6 +1970,7 @@ dependencies = [
"num_cpus",
"num_enum",
"once_cell",
"optional",
"parking_lot",
"paste",
"rand",
+7 -3
View File
@@ -51,10 +51,14 @@ class WeakSet:
self.update(data)
def _commit_removals(self):
l = self._pending_removals
pop = self._pending_removals.pop
discard = self.data.discard
while l:
discard(l.pop())
while True:
try:
item = pop()
except IndexError:
return
discard(item)
def __iter__(self):
with _IterationGuard(self):
-2
View File
@@ -1753,8 +1753,6 @@ class AssortedBytesTest(unittest.TestCase):
self.assertEqual(bytes(b"abc") < b"ab", False)
self.assertEqual(bytes(b"abc") <= b"ab", False)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@test.support.requires_docstrings
def test_doc(self):
self.assertIsNotNone(bytearray.__doc__)
-8
View File
@@ -826,18 +826,12 @@ class TestCopy(unittest.TestCase):
v[x] = y
self.assertNotIn(x, u)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_copy_weakkeydict(self):
self._check_copy_weakdict(weakref.WeakKeyDictionary)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_copy_weakvaluedict(self):
self._check_copy_weakdict(weakref.WeakValueDictionary)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_deepcopy_weakkeydict(self):
class C(object):
def __init__(self, i):
@@ -857,8 +851,6 @@ class TestCopy(unittest.TestCase):
del c
self.assertEqual(len(v), 1)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_deepcopy_weakvaluedict(self):
class C(object):
def __init__(self, i):
-4
View File
@@ -90,8 +90,6 @@ class DictSetTest(unittest.TestCase):
self.assertTrue(r == "dict_values(['ABC', 10])" or
r == "dict_values([10, 'ABC'])")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_keys_set_operations(self):
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 2}
@@ -148,8 +146,6 @@ class DictSetTest(unittest.TestCase):
self.assertTrue(de.keys().isdisjoint(de.keys()))
self.assertTrue(de.keys().isdisjoint([1]))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_items_set_operations(self):
d1 = {'a': 1, 'b': 2}
d2 = {'a': 2, 'b': 2}
-2
View File
@@ -94,8 +94,6 @@ class EnumerateTestCase(unittest.TestCase, PickleTest):
self.assertEqual(list(self.enum(self.seq)), self.res)
self.enum.__doc__
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_pickle(self):
self.check_pickle(self.enum(self.seq), self.res)
-2
View File
@@ -500,8 +500,6 @@ class TestPartialMethod(unittest.TestCase):
a = A()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_arg_combinations(self):
self.assertEqual(self.a.nothing(), ((self.a,), {}))
self.assertEqual(self.a.nothing(5), ((self.a, 5), {}))
+93 -27
View File
@@ -1,4 +1,3 @@
# Python test set -- math module
# XXXX Should not do tests around zero only
@@ -19,7 +18,6 @@ eps = 1E-05
NAN = float('nan')
INF = float('inf')
NINF = float('-inf')
FLOAT_MAX = sys.float_info.max
FLOAT_MIN = sys.float_info.min
@@ -43,8 +41,10 @@ def to_ulps(x):
adjacent floats are converted to adjacent integers. Then
abs(ulps(x) - ulps(y)) gives the difference in ulps between two
floats.
The results from this function will only make sense on platforms
where native doubles are represented in IEEE 754 binary64 format.
Note: 0.0 and -0.0 are converted to 0 and -1, respectively.
"""
n = struct.unpack('<q', struct.pack('<d', x))[0]
@@ -81,6 +81,7 @@ def count_set_bits(n):
def partial_product(start, stop):
"""Product of integers in range(start, stop, 2), computed recursively.
start and stop should both be odd, with start <= stop.
"""
numfactors = (stop - start) >> 1
if not numfactors:
@@ -94,6 +95,7 @@ def partial_product(start, stop):
def py_factorial(n):
"""Factorial of nonnegative integer n, via "Binary Split Factorial Formula"
described at http://www.luschny.de/math/factorial/binarysplitfact.html
"""
inner = outer = 1
for i in reversed(range(n.bit_length())):
@@ -105,6 +107,7 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
"""Given finite floats `expected` and `got`, check that they're
approximately equal to within the given number of ulps or the
given absolute tolerance, whichever is bigger.
Returns None on success and an error message on failure.
"""
ulp_error = abs(to_ulps(expected) - to_ulps(got))
@@ -120,12 +123,14 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
def parse_mtestfile(fname):
"""Parse a file with test values
-- starts a comment
blank lines, or lines containing only a comment, are ignored
other lines are expected to have the form
id fn arg -> expected [flag]*
"""
with open(fname) as fp:
with open(fname, encoding="utf-8") as fp:
for line in fp:
# strip comments, and skip blank lines
if '--' in line:
@@ -144,10 +149,11 @@ def parse_mtestfile(fname):
def parse_testfile(fname):
"""Parse a file with test values
Empty lines or lines starting with -- are ignored
yields id, fn, arg_real, arg_imag, exp_real, exp_imag
"""
with open(fname) as fp:
with open(fname, encoding="utf-8") as fp:
for line in fp:
# skip comment lines and blank lines
if line.startswith('--') or not line.strip():
@@ -170,9 +176,11 @@ def result_check(expected, got, ulp_tol=5, abs_tol=0.0):
"""Compare arguments expected and got, as floats, if either
is a float, using a tolerance expressed in multiples of
ulp(expected) or absolutely (if given and greater).
As a convenience, when neither argument is a float, and for
non-finite floats, exact equality is demanded. Also, nan==nan
as far as this function is concerned.
Returns None on success and an error message on failure.
"""
@@ -232,6 +240,7 @@ class MathTests(unittest.TestCase):
"""Compare arguments expected and got, as floats, if either
is a float, using a tolerance expressed in multiples of
ulp(expected) or absolutely, whichever is greater.
As a convenience, when neither argument is a float, and for
non-finite floats, exact equality is demanded. Also, nan==nan
in this function.
@@ -492,17 +501,11 @@ class MathTests(unittest.TestCase):
self.assertRaises(ValueError, math.factorial, -1)
self.assertRaises(ValueError, math.factorial, -10**100)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def testFactorialNonIntegers(self):
with self.assertWarns(DeprecationWarning):
self.assertEqual(math.factorial(5.0), 120)
with self.assertWarns(DeprecationWarning):
self.assertRaises(ValueError, math.factorial, 5.2)
with self.assertWarns(DeprecationWarning):
self.assertRaises(ValueError, math.factorial, -1.0)
with self.assertWarns(DeprecationWarning):
self.assertRaises(ValueError, math.factorial, -1e100)
self.assertRaises(TypeError, math.factorial, 5.0)
self.assertRaises(TypeError, math.factorial, 5.2)
self.assertRaises(TypeError, math.factorial, -1.0)
self.assertRaises(TypeError, math.factorial, -1e100)
self.assertRaises(TypeError, math.factorial, decimal.Decimal('5'))
self.assertRaises(TypeError, math.factorial, decimal.Decimal('5.2'))
self.assertRaises(TypeError, math.factorial, "5")
@@ -513,8 +516,7 @@ class MathTests(unittest.TestCase):
# Currently raises OverflowError for inputs that are too large
# to fit into a C long.
self.assertRaises(OverflowError, math.factorial, 10**100)
with self.assertWarns(DeprecationWarning):
self.assertRaises(OverflowError, math.factorial, 1e100)
self.assertRaises(TypeError, math.factorial, 1e100)
def testFloor(self):
self.assertRaises(TypeError, math.floor)
@@ -587,7 +589,6 @@ class MathTests(unittest.TestCase):
self.assertEqual(math.frexp(NINF)[0], NINF)
self.assertTrue(math.isnan(math.frexp(NAN)[0]))
@requires_IEEE_754
@unittest.skipIf(HAVE_DOUBLE_ROUNDING,
"fsum is not exact on machines with double rounding")
@@ -611,6 +612,7 @@ class MathTests(unittest.TestCase):
"""Full precision summation. Compute sum(iterable) without any
intermediate accumulation of error. Based on the 'lsum' function
at http://code.activestate.com/recipes/393090/
"""
tmant, texp = 0, 0
for x in iterable:
@@ -684,8 +686,6 @@ class MathTests(unittest.TestCase):
s = msum(vals)
self.assertEqual(msum(vals), math.fsum(vals))
# Python 3.9
def testGcd(self):
gcd = math.gcd
self.assertEqual(gcd(0, 0), 0)
@@ -726,7 +726,7 @@ class MathTests(unittest.TestCase):
self.assertRaises(TypeError, gcd, 120.0, 84)
self.assertRaises(TypeError, gcd, 120, 84.0)
self.assertRaises(TypeError, gcd, 120, 1, 84.0)
#self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) # TODO: RUSTPYTHON
self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12)
def testHypot(self):
from decimal import Decimal
@@ -795,13 +795,79 @@ class MathTests(unittest.TestCase):
# Verify scaling for extremely large values
fourthmax = FLOAT_MAX / 4.0
for n in range(32):
self.assertEqual(hypot(*([fourthmax]*n)), fourthmax * math.sqrt(n))
self.assertTrue(math.isclose(hypot(*([fourthmax]*n)),
fourthmax * math.sqrt(n)))
# Verify scaling for extremely small values
for exp in range(32):
scale = FLOAT_MIN / 2.0 ** exp
self.assertEqual(math.hypot(4*scale, 3*scale), 5*scale)
@requires_IEEE_754
@unittest.skipIf(HAVE_DOUBLE_ROUNDING,
"hypot() loses accuracy on machines with double rounding")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def testHypotAccuracy(self):
# Verify improved accuracy in cases that were known to be inaccurate.
#
# The new algorithm's accuracy depends on IEEE 754 arithmetic
# guarantees, on having the usual ROUND HALF EVEN rounding mode, on
# the system not having double rounding due to extended precision,
# and on the compiler maintaining the specified order of operations.
#
# This test is known to succeed on most of our builds. If it fails
# some build, we either need to add another skipIf if the cause is
# identifiable; otherwise, we can remove this test entirely.
hypot = math.hypot
Decimal = decimal.Decimal
high_precision = decimal.Context(prec=500)
for hx, hy in [
# Cases with a 1 ulp error in Python 3.7 compiled with Clang
('0x1.10e89518dca48p+29', '0x1.1970f7565b7efp+30'),
('0x1.10106eb4b44a2p+29', '0x1.ef0596cdc97f8p+29'),
('0x1.459c058e20bb7p+30', '0x1.993ca009b9178p+29'),
('0x1.378371ae67c0cp+30', '0x1.fbe6619854b4cp+29'),
('0x1.f4cd0574fb97ap+29', '0x1.50fe31669340ep+30'),
('0x1.494b2cdd3d446p+29', '0x1.212a5367b4c7cp+29'),
('0x1.f84e649f1e46dp+29', '0x1.1fa56bef8eec4p+30'),
('0x1.2e817edd3d6fap+30', '0x1.eb0814f1e9602p+29'),
('0x1.0d3a6e3d04245p+29', '0x1.32a62fea52352p+30'),
('0x1.888e19611bfc5p+29', '0x1.52b8e70b24353p+29'),
# Cases with 2 ulp error in Python 3.8
('0x1.538816d48a13fp+29', '0x1.7967c5ca43e16p+29'),
('0x1.57b47b7234530p+29', '0x1.74e2c7040e772p+29'),
('0x1.821b685e9b168p+30', '0x1.677dc1c1e3dc6p+29'),
('0x1.9e8247f67097bp+29', '0x1.24bd2dc4f4baep+29'),
('0x1.b73b59e0cb5f9p+29', '0x1.da899ab784a97p+28'),
('0x1.94a8d2842a7cfp+30', '0x1.326a51d4d8d8ap+30'),
('0x1.e930b9cd99035p+29', '0x1.5a1030e18dff9p+30'),
('0x1.1592bbb0e4690p+29', '0x1.a9c337b33fb9ap+29'),
('0x1.1243a50751fd4p+29', '0x1.a5a10175622d9p+29'),
('0x1.57a8596e74722p+30', '0x1.42d1af9d04da9p+30'),
# Cases with 1 ulp error in version fff3c28052e6b0
('0x1.ee7dbd9565899p+29', '0x1.7ab4d6fc6e4b4p+29'),
('0x1.5c6bfbec5c4dcp+30', '0x1.02511184b4970p+30'),
('0x1.59dcebba995cap+30', '0x1.50ca7e7c38854p+29'),
('0x1.768cdd94cf5aap+29', '0x1.9cfdc5571d38ep+29'),
('0x1.dcf137d60262ep+29', '0x1.1101621990b3ep+30'),
('0x1.3a2d006e288b0p+30', '0x1.e9a240914326cp+29'),
('0x1.62a32f7f53c61p+29', '0x1.47eb6cd72684fp+29'),
('0x1.d3bcb60748ef2p+29', '0x1.3f13c4056312cp+30'),
('0x1.282bdb82f17f3p+30', '0x1.640ba4c4eed3ap+30'),
('0x1.89d8c423ea0c6p+29', '0x1.d35dcfe902bc3p+29'),
]:
x = float.fromhex(hx)
y = float.fromhex(hy)
with self.subTest(hx=hx, hy=hy, x=x, y=y):
with decimal.localcontext(high_precision):
z = float((Decimal(x)**2 + Decimal(y)**2).sqrt())
self.assertEqual(hypot(x, y), z)
def testDist(self):
from decimal import Decimal as D
from fractions import Fraction as F
@@ -904,8 +970,8 @@ class MathTests(unittest.TestCase):
for n in range(32):
p = (fourthmax,) * n
q = (0.0,) * n
self.assertEqual(dist(p, q), fourthmax * math.sqrt(n))
self.assertEqual(dist(q, p), fourthmax * math.sqrt(n))
self.assertTrue(math.isclose(dist(p, q), fourthmax * math.sqrt(n)))
self.assertTrue(math.isclose(dist(q, p), fourthmax * math.sqrt(n)))
# Verify scaling for extremely small values
for exp in range(32):
@@ -968,8 +1034,7 @@ class MathTests(unittest.TestCase):
with self.assertRaises(TypeError):
math.isqrt(value)
# Python 3.9
def testlcm(self):
def test_lcm(self):
lcm = math.lcm
self.assertEqual(lcm(0, 0), 0)
self.assertEqual(lcm(1, 0), 0)
@@ -1011,7 +1076,7 @@ class MathTests(unittest.TestCase):
self.assertRaises(TypeError, lcm, 120.0, 84)
self.assertRaises(TypeError, lcm, 120, 84.0)
self.assertRaises(TypeError, lcm, 120, 0, 84.0)
# self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) # TODO: RUSTPYTHON
self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840)
def testLdexp(self):
self.assertRaises(TypeError, math.ldexp)
@@ -1987,7 +2052,7 @@ class MathTests(unittest.TestCase):
# min and max
self.assertEqual(math.ulp(0.0),
sys.float_info.min * sys.float_info.epsilon)
sys.float_info.min * sys.float_info.epsilon)
self.assertEqual(math.ulp(FLOAT_MAX),
FLOAT_MAX - math.nextafter(FLOAT_MAX, -INF))
@@ -2024,6 +2089,7 @@ class MathTests(unittest.TestCase):
def assertEqualSign(self, x, y):
"""Similar to assertEqual(), but compare also the sign with copysign().
Function useful to compare signed zeros.
"""
self.assertEqual(x, y)
-2
View File
@@ -343,8 +343,6 @@ class AbstractMemoryTests:
m = self._view(b)
self.assertRaises(ValueError, hash, m)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weakref(self):
# Check memoryviews are weakrefable
for tp in self._types:
-2
View File
@@ -318,8 +318,6 @@ class PositionalOnlyTestCase(unittest.TestCase):
with self.assertRaisesRegex(TypeError, r"g\(\) takes 2 positional arguments but 3 were given"):
f(1,2)(3,4,5)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_same_keyword_as_positional_with_kwargs(self):
def f(something,/,**kwargs):
return (something, kwargs)
-2
View File
@@ -234,8 +234,6 @@ class SliceTest(unittest.TestCase):
x[1:2] = 42
self.assertEqual(tmp, [(slice(1, 2), 42)])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_pickle(self):
s = slice(10, 20, 3)
for protocol in (0,1,2):
-2
View File
@@ -1454,8 +1454,6 @@ class TestTemporaryDirectory(BaseTestCase):
self.assertNotIn("Exception ", err)
self.assertIn("ResourceWarning: Implicitly cleaning up", err)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_warnings_on_cleanup(self):
# ResourceWarning will be triggered by __del__
with self.do_create() as dir:
-2
View File
@@ -3731,8 +3731,6 @@ class TypedDictTests(BaseTestCase):
self.assertEqual(Emp.__annotations__, {'name': str, 'id': int})
self.assertEqual(Emp.__total__, True)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_typeddict_special_keyword_names(self):
TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, fields=list, _fields=dict)
self.assertEqual(TD.__name__, 'TD')
-2
View File
@@ -661,8 +661,6 @@ class UnicodeTest(string_tests.CommonTest,
self.assertFalse("\u20ac".isascii())
self.assertFalse("\U0010ffff".isascii())
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_isdecimal(self):
self.checkequalnofix(False, '', 'isdecimal')
self.checkequalnofix(False, 'a', 'isdecimal')
+4 -37
View File
@@ -113,8 +113,6 @@ class ReferencesTestCase(TestBase):
del o
repr(wr)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_callback(self):
self.check_basic_callback(C)
self.check_basic_callback(create_function)
@@ -132,8 +130,6 @@ class ReferencesTestCase(TestBase):
self.check_basic_ref(create_cfunction)
self.check_basic_callback(create_cfunction)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_multiple_callbacks(self):
o = C()
ref1 = weakref.ref(o, self.callback)
@@ -166,7 +162,8 @@ class ReferencesTestCase(TestBase):
c = C()
self.assertRaises(TypeError, weakref.ref, c, callback=None)
@unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy_ref(self):
o = C()
o.bar = 1
@@ -201,8 +198,6 @@ class ReferencesTestCase(TestBase):
self.assertIsNone(ref(),
"ref2 should be dead after deleting object reference")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_ref_reuse(self):
o = C()
ref1 = weakref.ref(o)
@@ -332,7 +327,8 @@ class ReferencesTestCase(TestBase):
def test_shared_ref_without_callback(self):
self.check_shared_without_callback(weakref.ref)
@unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_shared_proxy_without_callback(self):
self.check_shared_without_callback(weakref.proxy)
@@ -439,7 +435,6 @@ class ReferencesTestCase(TestBase):
# can be killed in the middle of the call
"blech" in p
@unittest.skip("TODO: RUSTPYTHON, thread 'main' panicked at 'Passed a callback to weakproxy, but weakproxy does not yet support proxies.'")
def test_getweakrefcount(self):
o = C()
ref1 = weakref.ref(o)
@@ -461,8 +456,6 @@ class ReferencesTestCase(TestBase):
self.assertEqual(weakref.getweakrefcount(1), 0,
"got wrong number of weak reference objects for int")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_getweakrefs(self):
o = C()
ref1 = weakref.ref(o, self.callback)
@@ -828,8 +821,6 @@ class ReferencesTestCase(TestBase):
# No exception should be raised here
gc.collect()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_classes(self):
# Check that classes are weakrefable.
class A(object):
@@ -1003,8 +994,6 @@ class SubclassableWeakrefTestCase(TestBase):
self.assertIn(r1, refs[1:])
self.assertIn(r3, refs[1:])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_subclass_refs_dont_conflate_callbacks(self):
class MyRef(weakref.ref):
pass
@@ -1107,8 +1096,6 @@ class WeakMethodTestCase(unittest.TestCase):
gc.collect()
self.assertIs(r(), None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_callback_when_object_dead(self):
# Test callback behaviour when object dies first.
C = self._subclass()
@@ -1125,8 +1112,6 @@ class WeakMethodTestCase(unittest.TestCase):
gc.collect()
self.assertEqual(calls, [r])
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_callback_when_method_dead(self):
# Test callback behaviour when method dies first.
C = self._subclass()
@@ -1154,8 +1139,6 @@ class WeakMethodTestCase(unittest.TestCase):
del r
self.assertIs(wr(), None)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_equality(self):
def _eq(a, b):
self.assertTrue(a == b)
@@ -1282,8 +1265,6 @@ class MappingTestCase(TestBase):
def test_weak_valued_len_race(self):
self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_values(self):
#
# This exercises d.copy(), d.items(), d[], del d[], len(d).
@@ -1313,8 +1294,6 @@ class MappingTestCase(TestBase):
dict[2] = C()
self.assertRaises(KeyError, dict.__getitem__, 2)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_keys(self):
#
# This exercises d.copy(), d.items(), d[] = v, d[], del d[],
@@ -1492,8 +1471,6 @@ class MappingTestCase(TestBase):
self.assertEqual(len(dict), 0)
self.assertEqual(len(dict), 0)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_keys_destroy_while_iterating(self):
# Issue #7105: iterators shouldn't crash when a key is implicitly removed
dict, objects = self.make_weak_keyed_dict()
@@ -1520,8 +1497,6 @@ class MappingTestCase(TestBase):
dict, objects = self.make_weak_keyed_dict()
self.check_weak_del_and_len_while_iterating(dict, testcontext)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_values_destroy_while_iterating(self):
# Issue #7105: iterators shouldn't crash when a key is implicitly removed
dict, objects = self.make_weak_valued_dict()
@@ -1724,8 +1699,6 @@ class MappingTestCase(TestBase):
self.assertRaises(TypeError, d.__getitem__, 13)
self.assertRaises(TypeError, d.__setitem__, 13, 13)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_keyed_cascading_deletes(self):
# SF bug 742860. For some reason, before 2.3 __delitem__ iterated
# over the keys via self.data.iterkeys(). If things vanished from
@@ -1921,8 +1894,6 @@ class FinalizeTestCase(unittest.TestCase):
if sys.implementation.name != 'cpython':
support.gc_collect()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_finalize(self):
def add(x,y,z):
res.append(x + y + z)
@@ -1995,8 +1966,6 @@ class FinalizeTestCase(unittest.TestCase):
self.assertRaises(TypeError, weakref.finalize, a)
self.assertRaises(TypeError, weakref.finalize)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_order(self):
a = self.A()
res = []
@@ -2036,8 +2005,6 @@ class FinalizeTestCase(unittest.TestCase):
expected = ['A', 'f3', 'B', 'C', 'f4', 'f2', 'f1', 'D']
self.assertEqual(res, expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_all_freed(self):
# we want a weakrefable subclass of weakref.finalize
class MyFinalizer(weakref.finalize):
-14
View File
@@ -43,8 +43,6 @@ class TestWeakSet(unittest.TestCase):
def test_new_or_init(self):
self.assertRaises(TypeError, WeakSet, [], 2)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_len(self):
self.assertEqual(len(self.s), len(self.d))
self.assertEqual(len(self.fs), 1)
@@ -60,8 +58,6 @@ class TestWeakSet(unittest.TestCase):
del self.obj
self.assertNotIn(ustr('F'), self.fs)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union(self):
u = self.s.union(self.items2)
for c in self.letters:
@@ -84,8 +80,6 @@ class TestWeakSet(unittest.TestCase):
self.assertEqual(self.s | set(self.items2), i)
self.assertEqual(self.s | frozenset(self.items2), i)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_intersection(self):
s = WeakSet(self.letters)
i = s.intersection(self.items2)
@@ -123,8 +117,6 @@ class TestWeakSet(unittest.TestCase):
self.assertEqual(self.s - set(self.items2), i)
self.assertEqual(self.s - frozenset(self.items2), i)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_symmetric_difference(self):
i = self.s.symmetric_difference(self.items2)
for c in self.letters:
@@ -215,8 +207,6 @@ class TestWeakSet(unittest.TestCase):
self.assertEqual(self.s, dup)
self.assertNotEqual(id(self.s), id(dup))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_add(self):
x = ustr('Q')
self.s.add(x)
@@ -352,8 +342,6 @@ class TestWeakSet(unittest.TestCase):
s2 = WeakSet()
self.assertFalse(s1 != s2)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_destroy_while_iterating(self):
# Issue #7105: iterators shouldn't crash when a key is implicitly removed
# Create new items to be sure no-one else holds a reference
@@ -370,8 +358,6 @@ class TestWeakSet(unittest.TestCase):
# The removal has been committed
self.assertEqual(len(s), len(items))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weak_destroy_and_mutate_while_iterating(self):
# Issue #7105: iterators shouldn't crash when a key is implicitly removed
items = [ustr(c) for c in string.ascii_letters]
-2
View File
@@ -2344,8 +2344,6 @@ class BasicElementTest(ElementTestCase, unittest.TestCase):
gc_collect()
self.assertIsNone(wref())
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_weakref(self):
flag = False
def wref_cb(w):
+97 -54
View File
@@ -2,7 +2,7 @@
This module is an implementation of PEP 205:
http://www.python.org/dev/peps/pep-0205/
https://www.python.org/dev/peps/pep-0205/
"""
# Naming convention: Variables named "wr" are weak reference objects;
@@ -33,6 +33,9 @@ __all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
"WeakSet", "WeakMethod", "finalize"]
_collections_abc.Set.register(WeakSet)
_collections_abc.MutableSet.register(WeakSet)
class WeakMethod(ref):
"""
A custom `weakref.ref` subclass which simulates a weak reference to
@@ -75,14 +78,14 @@ class WeakMethod(ref):
if not self._alive or not other._alive:
return self is other
return ref.__eq__(self, other) and self._func_ref == other._func_ref
return False
return NotImplemented
def __ne__(self, other):
if isinstance(other, WeakMethod):
if not self._alive or not other._alive:
return self is not other
return ref.__ne__(self, other) or self._func_ref != other._func_ref
return True
return NotImplemented
__hash__ = ref.__hash__
@@ -99,13 +102,7 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
# objects are unwrapped on the way out, and we always wrap on the
# way in).
def __init__(*args, **kw):
if not args:
raise TypeError("descriptor '__init__' of 'WeakValueDictionary' "
"object needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
def __init__(self, other=(), /, **kw):
def remove(wr, selfref=ref(self), _atomic_removal=_remove_dead_weakref):
self = selfref()
if self is not None:
@@ -114,22 +111,25 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
else:
# Atomic removal is necessary since this function
# can be called asynchronously by the GC
_atomic_removal(d, wr.key)
_atomic_removal(self.data, wr.key)
self._remove = remove
# A list of keys to be removed
self._pending_removals = []
self._iterating = set()
self.data = d = {}
self.update(*args, **kw)
self.data = {}
self.update(other, **kw)
def _commit_removals(self):
l = self._pending_removals
def _commit_removals(self, _atomic_removal=_remove_dead_weakref):
pop = self._pending_removals.pop
d = self.data
# We shouldn't encounter any KeyError, because this method should
# always be called *before* mutating the dict.
while l:
key = l.pop()
_remove_dead_weakref(d, key)
while True:
try:
key = pop()
except IndexError:
return
_atomic_removal(d, key)
def __getitem__(self, key):
if self._pending_removals:
@@ -171,10 +171,11 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
if self._pending_removals:
self._commit_removals()
new = WeakValueDictionary()
for key, wr in self.data.items():
o = wr()
if o is not None:
new[key] = o
with _IterationGuard(self):
for key, wr in self.data.items():
o = wr()
if o is not None:
new[key] = o
return new
__copy__ = copy
@@ -184,10 +185,11 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
if self._pending_removals:
self._commit_removals()
new = self.__class__()
for key, wr in self.data.items():
o = wr()
if o is not None:
new[deepcopy(key, memo)] = o
with _IterationGuard(self):
for key, wr in self.data.items():
o = wr()
if o is not None:
new[deepcopy(key, memo)] = o
return new
def get(self, key, default=None):
@@ -285,24 +287,17 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
else:
return o
def update(*args, **kwargs):
if not args:
raise TypeError("descriptor 'update' of 'WeakValueDictionary' "
"object needs an argument")
self, *args = args
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
dict = args[0] if args else None
def update(self, other=None, /, **kwargs):
if self._pending_removals:
self._commit_removals()
d = self.data
if dict is not None:
if not hasattr(dict, "items"):
dict = type({})(dict)
for key, o in dict.items():
if other is not None:
if not hasattr(other, "items"):
other = dict(other)
for key, o in other.items():
d[key] = KeyedRef(o, self._remove, key)
if len(kwargs):
self.update(kwargs)
for key, o in kwargs.items():
d[key] = KeyedRef(o, self._remove, key)
def valuerefs(self):
"""Return a list of weak references to the values.
@@ -318,6 +313,25 @@ class WeakValueDictionary(_collections_abc.MutableMapping):
self._commit_removals()
return list(self.data.values())
def __ior__(self, other):
self.update(other)
return self
def __or__(self, other):
if isinstance(other, _collections_abc.Mapping):
c = self.copy()
c.update(other)
return c
return NotImplemented
def __ror__(self, other):
if isinstance(other, _collections_abc.Mapping):
c = self.__class__()
c.update(other)
c.update(self)
return c
return NotImplemented
class KeyedRef(ref):
"""Specialized reference that includes a key corresponding to the value.
@@ -359,7 +373,10 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
if self._iterating:
self._pending_removals.append(k)
else:
del self.data[k]
try:
del self.data[k]
except KeyError:
pass
self._remove = remove
# A list of dead weakrefs (keys to be removed)
self._pending_removals = []
@@ -373,11 +390,16 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
# because a dead weakref never compares equal to a live weakref,
# even if they happened to refer to equal objects.
# However, it means keys may already have been removed.
l = self._pending_removals
pop = self._pending_removals.pop
d = self.data
while l:
while True:
try:
del d[l.pop()]
key = pop()
except IndexError:
return
try:
del d[key]
except KeyError:
pass
@@ -408,10 +430,11 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
def copy(self):
new = WeakKeyDictionary()
for key, value in self.data.items():
o = key()
if o is not None:
new[o] = value
with _IterationGuard(self):
for key, value in self.data.items():
o = key()
if o is not None:
new[o] = value
return new
__copy__ = copy
@@ -419,10 +442,11 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
def __deepcopy__(self, memo):
from copy import deepcopy
new = self.__class__()
for key, value in self.data.items():
o = key()
if o is not None:
new[o] = deepcopy(value, memo)
with _IterationGuard(self):
for key, value in self.data.items():
o = key()
if o is not None:
new[o] = deepcopy(value, memo)
return new
def get(self, key, default=None):
@@ -484,7 +508,7 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
def setdefault(self, key, default=None):
return self.data.setdefault(ref(key, self._remove),default)
def update(self, dict=None, **kwargs):
def update(self, dict=None, /, **kwargs):
d = self.data
if dict is not None:
if not hasattr(dict, "items"):
@@ -494,6 +518,25 @@ class WeakKeyDictionary(_collections_abc.MutableMapping):
if len(kwargs):
self.update(kwargs)
def __ior__(self, other):
self.update(other)
return self
def __or__(self, other):
if isinstance(other, _collections_abc.Mapping):
c = self.copy()
c.update(other)
return c
return NotImplemented
def __ror__(self, other):
if isinstance(other, _collections_abc.Mapping):
c = self.__class__()
c.update(other)
c.update(self)
return c
return NotImplemented
class finalize:
"""Class for finalization of weakrefable objects
@@ -523,7 +566,7 @@ class finalize:
class _Info:
__slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
def __init__(self, obj, func, *args, **kwargs):
def __init__(self, obj, func, /, *args, **kwargs):
if not self._registered_with_atexit:
# We may register the exit function more than once because
# of a thread race, but that is harmless
+1 -1
View File
@@ -20,7 +20,7 @@ once_cell = "1.4.1"
siphasher = "0.3"
rand = "0.8"
volatile = "0.3"
radium = "0.6"
radium = "0.7"
libc = "0.2.101"
ascii = "1.0"
unic-ucd-category = "0.9"
+75
View File
@@ -1,3 +1,4 @@
use core::ptr::{self, NonNull};
pub use core::sync::atomic::*;
pub use radium::Radium;
@@ -50,3 +51,77 @@ impl<T> sealed::Sealed for *mut T {}
impl<T> PyAtomicScalar for *mut T {
type Radium = atomic_ty!(*mut T, AtomicPtr<T>);
}
pub struct OncePtr<T> {
inner: PyAtomic<*mut T>,
}
impl<T> Default for OncePtr<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> OncePtr<T> {
#[inline]
pub fn new() -> Self {
OncePtr {
inner: Radium::new(ptr::null_mut()),
}
}
pub fn get(&self) -> Option<NonNull<T>> {
NonNull::new(self.inner.load(Ordering::Acquire))
}
pub fn set(&self, value: NonNull<T>) -> Result<(), NonNull<T>> {
let exchange = self.inner.compare_exchange(
ptr::null_mut(),
value.as_ptr(),
Ordering::AcqRel,
Ordering::Acquire,
);
match exchange {
Ok(_) => Ok(()),
Err(_) => Err(value),
}
}
pub fn get_or_init<F>(&self, f: F) -> NonNull<T>
where
F: FnOnce() -> Box<T>,
{
enum Void {}
match self.get_or_try_init(|| Ok::<_, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<NonNull<T>, E>
where
F: FnOnce() -> Result<Box<T>, E>,
{
if let Some(val) = self.get() {
return Ok(val);
}
Ok(self.initialize(f()?))
}
#[cold]
fn initialize(&self, val: Box<T>) -> NonNull<T> {
let ptr = Box::into_raw(val);
let exchange =
self.inner
.compare_exchange(ptr::null_mut(), ptr, Ordering::AcqRel, Ordering::Acquire);
let ptr = match exchange {
Ok(_) => ptr,
Err(winner) => {
drop(unsafe { Box::from_raw(ptr) });
winner
}
};
unsafe { NonNull::new_unchecked(ptr) }
}
}
+1 -1
View File
@@ -169,7 +169,7 @@ pub fn hash_bigint(value: &BigInt) -> PyHash {
}
#[inline(always)]
fn fix_sentinel(x: PyHash) -> PyHash {
pub fn fix_sentinel(x: PyHash) -> PyHash {
if x == SENTINEL {
-2
} else {
+2
View File
@@ -9,8 +9,10 @@ pub mod cmp;
pub mod encodings;
pub mod float_ops;
pub mod hash;
pub mod linked_list;
pub mod lock;
pub mod rc;
pub mod refcount;
pub mod static_cell;
pub mod str;
+375
View File
@@ -0,0 +1,375 @@
//! This module is modified from tokio::util::linked_list: https://github.com/tokio-rs/tokio/blob/master/tokio/src/util/linked_list.rs
//! Tokio is licensed under the MIT license:
//!
//! Copyright (c) 2021 Tokio Contributors
//!
//! Permission is hereby granted, free of charge, to any
//! person obtaining a copy of this software and associated
//! documentation files (the "Software"), to deal in the
//! Software without restriction, including without
//! limitation the rights to use, copy, modify, merge,
//! publish, distribute, sublicense, and/or sell copies of
//! the Software, and to permit persons to whom the Software
//! is furnished to do so, subject to the following
//! conditions:
//!
//! The above copyright notice and this permission notice
//! shall be included in all copies or substantial portions
//! of the Software.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//! DEALINGS IN THE SOFTWARE.
//!
//! Original header:
//!
//! An intrusive double linked list of data.
//!
//! The data structure supports tracking pinned nodes. Most of the data
//! structure's APIs are `unsafe` as they require the caller to ensure the
//! specified node is actually contained by the list.
#![allow(clippy::new_without_default, clippy::missing_safety_doc)]
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::{PhantomData, PhantomPinned};
use core::mem::ManuallyDrop;
use core::ptr::{self, NonNull};
/// An intrusive linked list.
///
/// Currently, the list is not emptied on drop. It is the caller's
/// responsibility to ensure the list is empty before dropping it.
pub struct LinkedList<L, T> {
/// Linked list head
head: Option<NonNull<T>>,
// /// Linked list tail
// tail: Option<NonNull<T>>,
/// Node type marker.
_marker: PhantomData<*const L>,
}
unsafe impl<L: Link> Send for LinkedList<L, L::Target> where L::Target: Send {}
unsafe impl<L: Link> Sync for LinkedList<L, L::Target> where L::Target: Sync {}
/// Defines how a type is tracked within a linked list.
///
/// In order to support storing a single type within multiple lists, accessing
/// the list pointers is decoupled from the entry type.
///
/// # Safety
///
/// Implementations must guarantee that `Target` types are pinned in memory. In
/// other words, when a node is inserted, the value will not be moved as long as
/// it is stored in the list.
pub unsafe trait Link {
/// Handle to the list entry.
///
/// This is usually a pointer-ish type.
type Handle;
/// Node type.
type Target;
/// Convert the handle to a raw pointer without consuming the handle.
#[allow(clippy::wrong_self_convention)]
fn as_raw(handle: &Self::Handle) -> NonNull<Self::Target>;
/// Convert the raw pointer to a handle
unsafe fn from_raw(ptr: NonNull<Self::Target>) -> Self::Handle;
/// Return the pointers for a node
unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>>;
}
/// Previous / next pointers.
pub struct Pointers<T> {
inner: UnsafeCell<PointersInner<T>>,
}
/// We do not want the compiler to put the `noalias` attribute on mutable
/// references to this type, so the type has been made `!Unpin` with a
/// `PhantomPinned` field.
///
/// Additionally, we never access the `prev` or `next` fields directly, as any
/// such access would implicitly involve the creation of a reference to the
/// field, which we want to avoid since the fields are not `!Unpin`, and would
/// hence be given the `noalias` attribute if we were to do such an access.
/// As an alternative to accessing the fields directly, the `Pointers` type
/// provides getters and setters for the two fields, and those are implemented
/// using raw pointer casts and offsets, which is valid since the struct is
/// #[repr(C)].
///
/// See this link for more information:
/// <https://github.com/rust-lang/rust/pull/82834>
#[repr(C)]
struct PointersInner<T> {
/// The previous node in the list. null if there is no previous node.
///
/// This field is accessed through pointer manipulation, so it is not dead code.
#[allow(dead_code)]
prev: Option<NonNull<T>>,
/// The next node in the list. null if there is no previous node.
///
/// This field is accessed through pointer manipulation, so it is not dead code.
#[allow(dead_code)]
next: Option<NonNull<T>>,
/// This type is !Unpin due to the heuristic from:
/// <https://github.com/rust-lang/rust/pull/82834>
_pin: PhantomPinned,
}
unsafe impl<T: Send> Send for Pointers<T> {}
unsafe impl<T: Sync> Sync for Pointers<T> {}
// ===== impl LinkedList =====
impl<L, T> LinkedList<L, T> {
/// Creates an empty linked list.
pub const fn new() -> LinkedList<L, T> {
LinkedList {
head: None,
// tail: None,
_marker: PhantomData,
}
}
}
impl<L: Link> LinkedList<L, L::Target> {
/// Adds an element first in the list.
pub fn push_front(&mut self, val: L::Handle) {
// The value should not be dropped, it is being inserted into the list
let val = ManuallyDrop::new(val);
let ptr = L::as_raw(&*val);
assert_ne!(self.head, Some(ptr));
unsafe {
L::pointers(ptr).as_mut().set_next(self.head);
L::pointers(ptr).as_mut().set_prev(None);
if let Some(head) = self.head {
L::pointers(head).as_mut().set_prev(Some(ptr));
}
self.head = Some(ptr);
// if self.tail.is_none() {
// self.tail = Some(ptr);
// }
}
}
// /// Removes the last element from a list and returns it, or None if it is
// /// empty.
// pub fn pop_back(&mut self) -> Option<L::Handle> {
// unsafe {
// let last = self.tail?;
// self.tail = L::pointers(last).as_ref().get_prev();
// if let Some(prev) = L::pointers(last).as_ref().get_prev() {
// L::pointers(prev).as_mut().set_next(None);
// } else {
// self.head = None
// }
// L::pointers(last).as_mut().set_prev(None);
// L::pointers(last).as_mut().set_next(None);
// Some(L::from_raw(last))
// }
// }
/// Returns whether the linked list does not contain any node
pub fn is_empty(&self) -> bool {
self.head.is_none()
// if self.head.is_some() {
// return false;
// }
// assert!(self.tail.is_none());
// true
}
/// Removes the specified node from the list
///
/// # Safety
///
/// The caller **must** ensure that `node` is currently contained by
/// `self` or not contained by any other list.
pub unsafe fn remove(&mut self, node: NonNull<L::Target>) -> Option<L::Handle> {
if let Some(prev) = L::pointers(node).as_ref().get_prev() {
debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node));
L::pointers(prev)
.as_mut()
.set_next(L::pointers(node).as_ref().get_next());
} else {
if self.head != Some(node) {
return None;
}
self.head = L::pointers(node).as_ref().get_next();
}
if let Some(next) = L::pointers(node).as_ref().get_next() {
debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node));
L::pointers(next)
.as_mut()
.set_prev(L::pointers(node).as_ref().get_prev());
} else {
// // This might be the last item in the list
// if self.tail != Some(node) {
// return None;
// }
// self.tail = L::pointers(node).as_ref().get_prev();
}
L::pointers(node).as_mut().set_next(None);
L::pointers(node).as_mut().set_prev(None);
Some(L::from_raw(node))
}
// pub fn last(&self) -> Option<&L::Target> {
// let tail = self.tail.as_ref()?;
// unsafe { Some(&*tail.as_ptr()) }
// }
// === rustpython additions ===
pub fn iter(&self) -> impl Iterator<Item = &L::Target> {
std::iter::successors(self.head, |node| unsafe {
L::pointers(*node).as_ref().get_next()
})
.map(|ptr| unsafe { ptr.as_ref() })
}
}
impl<L: Link> fmt::Debug for LinkedList<L, L::Target> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LinkedList")
.field("head", &self.head)
// .field("tail", &self.tail)
.finish()
}
}
impl<L: Link> Default for LinkedList<L, L::Target> {
fn default() -> Self {
Self::new()
}
}
// ===== impl DrainFilter =====
pub struct DrainFilter<'a, T: Link, F> {
list: &'a mut LinkedList<T, T::Target>,
filter: F,
curr: Option<NonNull<T::Target>>,
}
impl<T: Link> LinkedList<T, T::Target> {
pub fn drain_filter<F>(&mut self, filter: F) -> DrainFilter<'_, T, F>
where
F: FnMut(&mut T::Target) -> bool,
{
let curr = self.head;
DrainFilter {
curr,
filter,
list: self,
}
}
}
impl<'a, T, F> Iterator for DrainFilter<'a, T, F>
where
T: Link,
F: FnMut(&mut T::Target) -> bool,
{
type Item = T::Handle;
fn next(&mut self) -> Option<Self::Item> {
while let Some(curr) = self.curr {
// safety: the pointer references data contained by the list
self.curr = unsafe { T::pointers(curr).as_ref() }.get_next();
// safety: the value is still owned by the linked list.
if (self.filter)(unsafe { &mut *curr.as_ptr() }) {
return unsafe { self.list.remove(curr) };
}
}
None
}
}
// ===== impl Pointers =====
impl<T> Pointers<T> {
/// Create a new set of empty pointers
pub fn new() -> Pointers<T> {
Pointers {
inner: UnsafeCell::new(PointersInner {
prev: None,
next: None,
_pin: PhantomPinned,
}),
}
}
fn get_prev(&self) -> Option<NonNull<T>> {
// SAFETY: prev is the first field in PointersInner, which is #[repr(C)].
unsafe {
let inner = self.inner.get();
let prev = inner as *const Option<NonNull<T>>;
ptr::read(prev)
}
}
fn get_next(&self) -> Option<NonNull<T>> {
// SAFETY: next is the second field in PointersInner, which is #[repr(C)].
unsafe {
let inner = self.inner.get();
let prev = inner as *const Option<NonNull<T>>;
let next = prev.add(1);
ptr::read(next)
}
}
fn set_prev(&mut self, value: Option<NonNull<T>>) {
// SAFETY: prev is the first field in PointersInner, which is #[repr(C)].
unsafe {
let inner = self.inner.get();
let prev = inner as *mut Option<NonNull<T>>;
ptr::write(prev, value);
}
}
fn set_next(&mut self, value: Option<NonNull<T>>) {
// SAFETY: next is the second field in PointersInner, which is #[repr(C)].
unsafe {
let inner = self.inner.get();
let prev = inner as *mut Option<NonNull<T>>;
let next = prev.add(1);
ptr::write(next, value);
}
}
}
impl<T> fmt::Debug for Pointers<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let prev = self.get_prev();
let next = self.get_next();
f.debug_struct("Pointers")
.field("prev", &prev)
.field("next", &next)
.finish()
}
}
+60
View File
@@ -0,0 +1,60 @@
use crate::atomic::{Ordering::*, PyAtomic, Radium};
/// from alloc::sync
/// A soft limit on the amount of references that may be made to an `Arc`.
///
/// Going above this limit will abort your program (although not
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
pub struct RefCount {
strong: PyAtomic<usize>,
}
impl Default for RefCount {
fn default() -> Self {
Self::new()
}
}
impl RefCount {
pub fn new() -> Self {
RefCount {
strong: Radium::new(1),
}
}
#[inline]
pub fn get(&self) -> usize {
self.strong.load(SeqCst)
}
#[inline]
pub fn inc(&self) {
let old_size = self.strong.fetch_add(1, Relaxed);
if old_size > MAX_REFCOUNT {
std::process::abort();
}
}
/// Returns true if successful
#[inline]
pub fn safe_inc(&self) -> bool {
self.strong
.fetch_update(AcqRel, Acquire, |prev| (prev != 0).then(|| prev + 1))
.is_ok()
}
/// Decrement the reference count. Returns true when the refcount drops to 0.
#[inline]
pub fn dec(&self) -> bool {
if self.strong.fetch_sub(1, Release) != 1 {
return false;
}
PyAtomic::<usize>::fence(Acquire);
true
}
}
+1 -1
View File
@@ -667,7 +667,7 @@ impl ToTokens for GetSetNursery {
#( #cfgs )*
class.set_str_attr(
#name,
::rustpython_vm::PyGenericObject::new(
::rustpython_vm::PyRef::new_ref(
::rustpython_vm::builtins::PyGetSet::new(#name.into(), class.clone())
.with_get(&Self::#getter)
#setter #deleter,
+33 -11
View File
@@ -1,7 +1,7 @@
use crate::error::Diagnostic;
use crate::util::{
iter_use_idents, pyclass_ident_and_attrs, text_signature, AttributeExt, ClassItemMeta,
ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta,
iter_use_idents, pyclass_ident_and_attrs, text_signature, AttrItemMeta, AttributeExt,
ClassItemMeta, ContentItem, ContentItemInner, ErrorVec, ItemMeta, ItemNursery, SimpleItemMeta,
ALL_ALLOWED_NAMES,
};
use proc_macro2::TokenStream;
@@ -241,7 +241,7 @@ impl ContentItem for AttributeItem {
}
struct ModuleItemArgs<'a> {
item: &'a Item,
item: &'a mut Item,
attrs: &'a mut Vec<Attribute>,
context: &'a mut ModuleContext,
cfgs: &'a [Attribute],
@@ -370,15 +370,36 @@ impl ModuleItem for AttributeItem {
fn gen_module_item(&self, args: ModuleItemArgs<'_>) -> Result<()> {
let cfgs = args.cfgs.to_vec();
let attr = args.attrs.remove(self.index());
let get_py_name = |attr: &Attribute, ident: &Ident| -> Result<_> {
let item_meta = SimpleItemMeta::from_attr(ident.clone(), attr)?;
let py_name = item_meta.simple_name()?;
Ok(py_name)
};
let (py_name, tokens) = match args.item {
Item::Fn(syn::ItemFn { sig, .. }) => {
Item::Fn(syn::ItemFn { sig, block, .. }) => {
let ident = &sig.ident;
let py_name = get_py_name(&attr, ident)?;
// If `once` keyword is in #[pyattr],
// wrapping it with static_cell for preventing it from using it as function
let attr_meta = AttrItemMeta::from_attr(ident.clone(), &attr)?;
if attr_meta.inner()._bool("once")? {
let stmts = &block.stmts;
let return_type = match &sig.output {
syn::ReturnType::Default => {
unreachable!("#[pyattr] attached function must have return type.")
}
syn::ReturnType::Type(_, ty) => ty,
};
let stmt: syn::Stmt = parse_quote! {
{
rustpython_common::static_cell! {
static ERROR: #return_type;
}
ERROR
.get_or_init(|| {
#(#stmts)*
})
.clone()
}
};
block.stmts = vec![stmt];
}
let py_name = attr_meta.simple_name()?;
(
py_name.clone(),
quote_spanned! { ident.span() =>
@@ -387,7 +408,8 @@ impl ModuleItem for AttributeItem {
)
}
Item::Const(syn::ItemConst { ident, .. }) => {
let py_name = get_py_name(&attr, ident)?;
let item_meta = SimpleItemMeta::from_attr(ident.clone(), &attr)?;
let py_name = item_meta.simple_name()?;
(
py_name.clone(),
quote_spanned! { ident.span() =>
+1 -2
View File
@@ -8,8 +8,7 @@ pub(crate) fn impl_pyvalue(input: DeriveInput) -> Result<TokenStream> {
let ret = quote! {
impl ::rustpython_vm::PyValue for #ty {
fn class(_vm: &::rustpython_vm::VirtualMachine) -> &rustpython_vm::builtins::PyTypeRef {
use ::rustpython_vm::StaticType;
Self::static_type()
<Self as ::rustpython_vm::StaticType>::static_type()
}
}
};
+13
View File
@@ -236,6 +236,19 @@ impl ItemMeta for SimpleItemMeta {
}
}
pub(crate) struct AttrItemMeta(pub ItemMetaInner);
impl ItemMeta for AttrItemMeta {
const ALLOWED_NAMES: &'static [&'static str] = &["name", "once"];
fn from_inner(inner: ItemMetaInner) -> Self {
Self(inner)
}
fn inner(&self) -> &ItemMetaInner {
&self.0
}
}
pub(crate) struct ClassItemMeta(ItemMetaInner);
impl ItemMeta for ClassItemMeta {
-2
View File
@@ -72,8 +72,6 @@ def attr_is_not_inherited(type_, attr):
def extra_info(obj):
# RustPython doesn't support __text_signature__ for getting signatures of builtins
# https://github.com/RustPython/RustPython/issues/2410
if callable(obj) and not inspect._signature_is_builtin(obj):
try:
sig = str(inspect.signature(obj))
+15 -1
View File
@@ -47,4 +47,18 @@ assert () not in d.keys()
assert 10 not in d.keys()
assert (1, 10) not in d.keys()
assert "abc" not in d.keys()
assert ((3,4),5) not in d.keys()
assert ((3,4),5) not in d.keys()
d1 = {"a": 1, "b": 2}
d2 = {"c": 3, "d": 4}
assert d1.items().isdisjoint(d2.items())
assert d1.keys().isdisjoint(d2.keys())
d2 = {"b": 3, "d": 4}
assert d1.items().isdisjoint(d2.items())
assert not d1.keys().isdisjoint(d2.keys())
d2 = {"c": 2, "d": 4}
assert d1.items().isdisjoint(d2.items())
assert d1.keys().isdisjoint(d2.keys())
d2 = {"b": 2, "d": 4}
assert not d1.items().isdisjoint(d2.items())
assert not d1.keys().isdisjoint(d2.keys())
-23
View File
@@ -505,26 +505,3 @@ if "win" not in sys.platform:
for arg in [None, 1, 1.0, TabError]:
assert_raises(TypeError, os.system, arg)
if sys.platform.startswith("win"):
winver = sys.getwindowsversion()
# the biggest value of wSuiteMask (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa#members).
all_masks = 0x00000004 | 0x00000400 | 0x00004000 | 0x00000080 | 0x00000002 | 0x00000040 | 0x00000200 | \
0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000
# We really can't test if the results are correct, so it just checks for meaningful value
assert winver.major > 0
assert winver.minor >= 0
assert winver.build > 0
assert winver.platform == 2
assert isinstance(winver.service_pack, str)
assert 0 <= winver.suite_mask <= all_masks
assert 1 <= winver.product_type <= 3
# XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest
assert winver.major == winver.platform_version[0]
assert winver.minor == winver.platform_version[1]
assert winver.build == winver.platform_version[2]
+27 -4
View File
@@ -51,11 +51,11 @@ assert ("demo", "call", None) in events
assert sys.exc_info() == (None, None, None)
try:
1/0
1/0
except ZeroDivisionError as exc:
exc_info = sys.exc_info()
assert exc_info[0] == type(exc) == ZeroDivisionError
assert exc_info[1] == exc
exc_info = sys.exc_info()
assert exc_info[0] == type(exc) == ZeroDivisionError
assert exc_info[1] == exc
# Recursion:
@@ -69,3 +69,26 @@ assert sys.getrecursionlimit() == 200
with assert_raises(RecursionError):
recursive_call(300)
if sys.platform.startswith("win"):
winver = sys.getwindowsversion()
print(f'winver: {winver} {winver.platform_version}')
# the biggest value of wSuiteMask (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa#members).
all_masks = 0x00000004 | 0x00000400 | 0x00004000 | 0x00000080 | 0x00000002 | 0x00000040 | 0x00000200 | \
0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000
# We really can't test if the results are correct, so it just checks for meaningful value
assert winver.major > 0
assert winver.minor >= 0
assert winver.build > 0
assert winver.platform == 2
assert isinstance(winver.service_pack, str)
assert 0 <= winver.suite_mask <= all_masks
assert 1 <= winver.product_type <= 3
# XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest
# these fields can mismatch in CPython
# assert winver.major == winver.platform_version[0]
# assert winver.minor == winver.platform_version[1]
# assert winver.build == winver.platform_version[2]
+2 -1
View File
@@ -90,7 +90,8 @@ impl<'vm> ShellHelper<'vm> {
// we need to get a variable based off of globals/builtins
let globals = str_iter_method(self.globals.as_object().to_owned(), "keys").ok()?;
let builtins = str_iter_method(self.vm.builtins.clone(), "__dir__").ok()?;
let builtins =
str_iter_method(self.vm.builtins.as_object().to_owned(), "__dir__").ok()?;
(first, globals, Some(builtins))
};
Some((word_start, iter1.chain(iter2.into_iter().flatten())))
+19 -29
View File
@@ -23,6 +23,7 @@ mod array {
BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn,
PyMappingMethods,
},
sequence::{SequenceMutOp, SequenceOp},
sliceable::{PySliceableSequence, PySliceableSequenceMut, SaturatedSlice, SequenceIndex},
types::{
AsBuffer, AsMapping, Comparable, Constructor, IterNext, IterNextIterable, Iterable,
@@ -387,34 +388,24 @@ mod array {
}
}
fn mul(&self, counter: usize) -> Self {
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<Self> {
match self {
$(ArrayContentType::$n(v) => {
let elements = v.repeat(counter);
ArrayContentType::$n(elements)
// MemoryError instead Overflow Error, hard to says it is right
// but it is how cpython doing right now
let elements = v.mul(vm, value).map_err(|_| vm.new_memory_error("".to_owned()))?;
Ok(ArrayContentType::$n(elements))
})*
}
}
fn clear(&mut self) {
fn imul(&mut self, value: isize, vm: &VirtualMachine) -> PyResult<()> {
match self {
$(ArrayContentType::$n(v) => v.clear(),)*
}
}
fn imul(&mut self, counter: usize) {
if counter == 0 {
self.clear();
} else if counter != 1 {
match self {
$(ArrayContentType::$n(v) => {
let old = v.clone();
v.reserve((counter - 1) * old.len());
for _ in 1..counter {
v.extend(&old);
}
})*
}
$(ArrayContentType::$n(v) => {
// MemoryError instead Overflow Error, hard to says it is right
// but it is how cpython doing right now
v.imul(vm, value).map_err(|_| vm.new_memory_error("".to_owned()))
})*
}
}
@@ -742,8 +733,7 @@ mod array {
fn extend(zelf: PyRef<Self>, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let mut w = zelf.try_resizable(vm)?;
if zelf.is(&obj) {
w.imul(2);
Ok(())
w.imul(2, vm)
} else if let Some(array) = obj.payload::<PyArray>() {
w.iadd(&*array.read(), vm)
} else {
@@ -856,7 +846,7 @@ mod array {
if n < 0 {
return Err(vm.new_value_error("negative count".to_owned()));
}
let n = vm.check_repeat_or_memory_error(itemsize, n)?;
let n = vm.check_repeat_or_overflow_error(itemsize, n)?;
let nbytes = n * itemsize;
let b = vm.call_method(&f, "read", (nbytes,))?;
@@ -1044,7 +1034,7 @@ mod array {
vm: &VirtualMachine,
) -> PyResult<PyRef<Self>> {
if zelf.is(&other) {
zelf.try_resizable(vm)?.imul(2);
zelf.try_resizable(vm)?.imul(2, vm)?;
} else if let Some(other) = other.payload::<PyArray>() {
zelf.try_resizable(vm)?.iadd(&*other.read(), vm)?;
} else {
@@ -1059,14 +1049,14 @@ mod array {
#[pymethod(name = "__rmul__")]
#[pymethod(magic)]
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let value = vm.check_repeat_or_memory_error(self.len(), value)?;
Ok(Self::from(self.read().mul(value)).into_ref(vm))
self.read()
.mul(value, vm)
.map(|x| Self::from(x).into_ref(vm))
}
#[pymethod(magic)]
fn imul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let value = vm.check_repeat_or_memory_error(zelf.len(), value)?;
zelf.try_resizable(vm)?.imul(value);
zelf.try_resizable(vm)?.imul(value, vm)?;
Ok(zelf)
}
+8 -28
View File
@@ -11,38 +11,18 @@ mod decl {
};
use itertools::Itertools;
#[pyattr(name = "Error")]
#[pyattr(name = "Error", once)]
fn error_type(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static BINASCII_ERROR: PyTypeRef;
}
BINASCII_ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("binascii"),
"Error",
&vm.ctx.exceptions.value_error,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type(
"binascii",
"Error",
Some(vec![vm.ctx.exceptions.value_error.clone()]),
)
}
#[pyattr(name = "Incomplete")]
#[pyattr(name = "Incomplete", once)]
fn incomplete_type(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static BINASCII_INCOMPLTE: PyTypeRef;
}
BINASCII_INCOMPLTE
.get_or_init(|| {
vm.ctx.new_class(
Some("binascii"),
"Incomplete",
&vm.ctx.exceptions.exception_type,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type("binascii", "Incomplete", None)
}
fn hex_nibble(n: u8) -> u8 {
+7 -3
View File
@@ -11,7 +11,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef {
mod _csv {
use crate::common::lock::PyMutex;
use crate::vm::{
builtins::{PyStr, PyStrRef, PyType, PyTypeRef},
builtins::{PyStr, PyStrRef, PyTypeRef},
function::{ArgIterable, ArgumentError, FromArgs, FuncArgs},
match_class,
protocol::{PyIter, PyIterReturn},
@@ -30,9 +30,13 @@ mod _csv {
#[pyattr]
const QUOTE_NONE: i32 = QuoteStyle::None as i32;
#[pyattr(name = "Error")]
#[pyattr(name = "Error", once)]
fn error(vm: &VirtualMachine) -> PyTypeRef {
PyType::new_simple_ref("_csv.Error", &vm.ctx.exceptions.exception_type).unwrap()
vm.ctx.new_exception_type(
"_csv",
"Error",
Some(vec![vm.ctx.exceptions.exception_type.clone()]),
)
}
#[pyfunction]
+18 -42
View File
@@ -80,53 +80,29 @@ mod _socket {
vm.ctx.exceptions.os_error.clone()
}
#[pyattr]
#[pyattr(once)]
fn timeout(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("socket"),
"timeout",
&vm.ctx.exceptions.os_error,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type(
"socket",
"timeout",
Some(vec![vm.ctx.exceptions.os_error.clone()]),
)
}
#[pyattr]
#[pyattr(once)]
fn herror(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("socket"),
"herror",
&vm.ctx.exceptions.os_error,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type(
"socket",
"herror",
Some(vec![vm.ctx.exceptions.os_error.clone()]),
)
}
#[pyattr]
#[pyattr(once)]
fn gaierror(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("socket"),
"gaierror",
&vm.ctx.exceptions.os_error,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type(
"socket",
"gaierror",
Some(vec![vm.ctx.exceptions.os_error.clone()]),
)
}
#[pyfunction]
+43 -72
View File
@@ -30,7 +30,7 @@ mod _ssl {
},
socket::{self, PySocket},
vm::{
builtins::{PyBaseException, PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef, PyWeak},
builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef},
exceptions,
function::{
ArgBytesLike, ArgCallable, ArgMemoryBuffer, ArgStrOrBytesLike, IntoPyException,
@@ -39,7 +39,7 @@ mod _ssl {
stdlib::os::PyPathLike,
types::Constructor,
utils::{Either, ToCString},
ItemProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine,
ItemProtocol, PyObjectRef, PyObjectWeak, PyRef, PyResult, PyValue, VirtualMachine,
},
};
use crossbeam_utils::atomic::AtomicCell;
@@ -174,90 +174,58 @@ mod _ssl {
parse_version_info(openssl_api_version)
}
#[pyattr(name = "SSLError")]
/// An error occurred in the SSL implementation.
#[pyattr(name = "SSLError", once)]
fn ssl_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
PyType::new_simple_ref("ssl.SSLError", &vm.ctx.exceptions.os_error).unwrap()
})
.clone()
vm.ctx.new_exception_type(
"ssl",
"SSLError",
Some(vec![vm.ctx.exceptions.os_error.clone()]),
)
}
#[pyattr(name = "SSLCertVerificationError")]
/// A certificate could not be verified.
#[pyattr(name = "SSLCertVerificationError", once)]
fn ssl_cert_verification_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
let ssl_error = ssl_error(vm);
PyType::new_ref(
"ssl.SSLCertVerificationError",
vec![ssl_error, vm.ctx.exceptions.value_error.clone()],
Default::default(),
PyBaseException::make_slots(),
vm.ctx.types.type_type.clone(),
)
.unwrap()
})
.clone()
vm.ctx.new_exception_type(
"ssl",
"SSLCertVerificationError",
Some(vec![ssl_error(vm), vm.ctx.exceptions.value_error.clone()]),
)
}
#[pyattr(name = "SSLZeroReturnError")]
/// SSL/TLS session closed cleanly.
#[pyattr(name = "SSLZeroReturnError", once)]
fn ssl_zero_return_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
PyType::new_simple_ref("ssl.SSLZeroReturnError", &ssl_error(vm)).unwrap()
})
.clone()
vm.ctx
.new_exception_type("ssl", "SSLZeroReturnError", Some(vec![ssl_error(vm)]))
}
#[pyattr(name = "SSLWantReadError")]
/// Non-blocking SSL socket needs to read more data before the requested operation can be completed.
#[pyattr(name = "SSLWantReadError", once)]
fn ssl_want_read_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| PyType::new_simple_ref("ssl.SSLWantReadError", &ssl_error(vm)).unwrap())
.clone()
vm.ctx
.new_exception_type("ssl", "SSLWantReadError", Some(vec![ssl_error(vm)]))
}
#[pyattr(name = "SSLWantWriteError")]
/// Non-blocking SSL socket needs to write more data before the requested operation can be completed.
#[pyattr(name = "SSLWantWriteError", once)]
fn ssl_want_write_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| {
PyType::new_simple_ref("ssl.SSLWantWriteError", &ssl_error(vm)).unwrap()
})
.clone()
vm.ctx
.new_exception_type("ssl", "SSLWantWriteError", Some(vec![ssl_error(vm)]))
}
#[pyattr(name = "SSLSyscallError")]
/// System error when attempting SSL operation.
#[pyattr(name = "SSLSyscallError", once)]
fn ssl_syscall_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| PyType::new_simple_ref("ssl.SSLSyscallError", &ssl_error(vm)).unwrap())
.clone()
vm.ctx
.new_exception_type("ssl", "SSLSyscallError", Some(vec![ssl_error(vm)]))
}
#[pyattr(name = "SSLEOFError")]
/// SSL/TLS connection terminated abruptly.
#[pyattr(name = "SSLEOFError", once)]
fn ssl_eof_error(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static ERROR: PyTypeRef;
}
ERROR
.get_or_init(|| PyType::new_simple_ref("ssl.SSLEOFError", &ssl_error(vm)).unwrap())
.clone()
PyType::new_simple_ref("ssl.SSLEOFError", &ssl_error(vm)).unwrap()
}
type OpensslVersionInfo = (u8, u8, u8, u8, u8);
@@ -805,7 +773,7 @@ mod _ssl {
stream: PyRwLock::new(stream),
socket_type,
server_hostname: args.server_hostname,
owner: PyRwLock::new(args.owner.as_ref().map(|o| PyWeak::downgrade(o))),
owner: PyRwLock::new(args.owner.map(|o| o.downgrade(None, vm)).transpose()?),
})
}
}
@@ -922,7 +890,7 @@ mod _ssl {
stream: PyRwLock<ssl::SslStream<SocketStream>>,
socket_type: SslServerOrClient,
server_hostname: Option<PyStrRef>,
owner: PyRwLock<Option<PyWeak>>,
owner: PyRwLock<Option<PyObjectWeak>>,
}
impl fmt::Debug for PySslSocket {
@@ -935,11 +903,14 @@ mod _ssl {
impl PySslSocket {
#[pyproperty]
fn owner(&self) -> Option<PyObjectRef> {
self.owner.read().as_ref().and_then(PyWeak::upgrade)
self.owner.read().as_ref().and_then(|weak| weak.upgrade())
}
#[pyproperty(setter)]
fn set_owner(&self, owner: PyObjectRef) {
*self.owner.write() = Some(PyWeak::downgrade(&owner))
fn set_owner(&self, owner: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let mut lock = self.owner.write();
lock.take();
*lock = Some(owner.downgrade(None, vm)?);
Ok(())
}
#[pyproperty]
fn server_side(&self) -> bool {
+6 -14
View File
@@ -100,20 +100,12 @@ mod termios {
)
}
#[pyattr(name = "error")]
#[pyattr(name = "error", once)]
fn error_type(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static TERMIOS_ERROR: PyTypeRef;
}
TERMIOS_ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("termios"),
"error",
&vm.ctx.exceptions.os_error,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type(
"termios",
"error",
Some(vec![vm.ctx.exceptions.os_error.clone()]),
)
}
}
+7 -3
View File
@@ -4,7 +4,7 @@ pub(crate) use zlib::make_module;
mod zlib {
use crate::common::lock::PyMutex;
use crate::vm::{
builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyIntRef, PyType, PyTypeRef},
builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyIntRef, PyTypeRef},
function::{ArgBytesLike, OptionalArg},
IntoPyRef, PyResult, PyValue, VirtualMachine,
};
@@ -53,9 +53,13 @@ mod zlib {
#[pyattr]
const DEF_MEM_LEVEL: u8 = 8;
#[pyattr]
#[pyattr(once)]
fn error(vm: &VirtualMachine) -> PyTypeRef {
PyType::new_simple_ref("zlib.error", &vm.ctx.exceptions.exception_type).unwrap()
vm.ctx.new_exception_type(
"zlib",
"error",
Some(vec![vm.ctx.exceptions.exception_type.clone()]),
)
}
/// Compute an Adler-32 checksum of data.
+2
View File
@@ -68,6 +68,8 @@ memchr = "2"
adler32 = "1.0.3"
flate2 = "1.0.20"
once_cell = "1"
memoffset = "0.6"
optional = "0.5"
# RustPython crates implementing functionality based on CPython
sre-engine = "0.1.2"
+5 -28
View File
@@ -35,17 +35,6 @@ use crate::{
use bstr::ByteSlice;
use std::mem::size_of;
/// "bytearray(iterable_of_ints) -> bytearray\n\
/// bytearray(string, encoding[, errors]) -> bytearray\n\
/// bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\
/// bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\
/// bytearray() -> empty bytes array\n\n\
/// Construct a mutable bytearray object from:\n \
/// - an iterable yielding integers in range(256)\n \
/// - a text string encoded using the specified encoding\n \
/// - a bytes or a buffer object\n \
/// - any object implementing the buffer API.\n \
/// - an integer";
#[pyclass(module = false, name = "bytearray")]
#[derive(Debug, Default)]
pub struct PyByteArray {
@@ -296,7 +285,7 @@ impl PyByteArray {
}
}
fn irepeat(zelf: &crate::PyObjectView<Self>, n: usize, vm: &VirtualMachine) -> PyResult<()> {
fn irepeat(zelf: &crate::PyObjectView<Self>, n: isize, vm: &VirtualMachine) -> PyResult<()> {
if n == 1 {
return Ok(());
}
@@ -311,19 +300,8 @@ impl PyByteArray {
};
}
};
let elements = &mut w.elements;
if n == 0 {
elements.clear();
} else if n != 1 {
let old = elements.clone();
elements.reserve((n - 1) * old.len());
for _ in 1..n {
elements.extend(&old);
}
}
Ok(())
w.imul(n, vm)
}
#[pymethod]
@@ -646,14 +624,13 @@ impl PyByteArray {
#[pymethod(name = "__rmul__")]
#[pymethod(magic)]
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<Self> {
vm.check_repeat_or_memory_error(self.len(), value)
.map(|value| self.inner().repeat(value).into())
self.inner().mul(value, vm).map(|x| x.into())
}
#[pymethod(magic)]
fn imul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
vm.check_repeat_or_memory_error(zelf.len(), value)
.and_then(|value| Self::irepeat(&zelf, value, vm).map(|_| zelf))
Self::irepeat(&zelf, value, vm)?;
Ok(zelf)
}
#[pymethod(name = "__mod__")]
+3 -17
View File
@@ -23,15 +23,6 @@ use bstr::ByteSlice;
use std::mem::size_of;
use std::ops::Deref;
/// "bytes(iterable_of_ints) -> bytes\n\
/// bytes(string, encoding[, errors]) -> bytes\n\
/// bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer\n\
/// bytes(int) -> bytes object of size given by the parameter initialized with null bytes\n\
/// bytes() -> empty bytes object\n\nConstruct an immutable array of bytes from:\n \
/// - an iterable yielding integers in range(256)\n \
/// - a text string encoded using the specified encoding\n \
/// - any object implementing the buffer API.\n \
/// - an integer";
#[pyclass(module = false, name = "bytes")]
#[derive(Clone, Debug)]
pub struct PyBytes {
@@ -470,14 +461,9 @@ impl PyBytes {
// This only works for `bytes` itself, not its subclasses.
return Ok(zelf);
}
// todo: map err to overflow.
vm.check_repeat_or_memory_error(zelf.inner.len(), value)
.map(|value| {
let bytes: PyBytes = zelf.inner.repeat(value).into();
bytes.into_ref(vm)
})
// see issue 45044 on b.p.o.
.map_err(|_| vm.new_overflow_error("repeated bytes are too long".to_owned()))
zelf.inner
.mul(value, vm)
.map(|x| Self::from(x).into_ref(vm))
}
#[pymethod(name = "__mod__")]
+14
View File
@@ -63,6 +63,12 @@ impl PyDict {
#[allow(clippy::len_without_is_empty)]
#[pyimpl(with(AsMapping, Hashable, Comparable, Iterable), flags(BASETYPE))]
impl PyDict {
/// escape hatch to access the underlying data structure directly. prefer adding a method on
/// PyDict instead of using this
pub(crate) fn _as_dict_inner(&self) -> &DictContentType {
&self.entries
}
#[pyslot]
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
PyDict::default().into_pyresult_with_type(vm, cls)
@@ -987,6 +993,14 @@ trait ViewSetOps: DictView {
}
})
}
#[pymethod]
fn isdisjoint(zelf: PyRef<Self>, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
// TODO: to_set is an expensive operation. After merging #3316 rewrite implementation using PySequence_Contains.
let zelf = Self::to_set(zelf, vm)?;
let result = zelf.isdisjoint(other, vm)?;
Ok(result)
}
}
impl ViewSetOps for PyDictKeys {}
+9 -1
View File
@@ -4,7 +4,8 @@ use crate::{
function::{IntoPyObject, OptionalArg},
protocol::{PyIter, PyIterReturn},
types::{Constructor, IterNext, IterNextIterable},
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, VirtualMachine,
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
VirtualMachine,
};
use num_bigint::BigInt;
use num_traits::Zero;
@@ -52,6 +53,13 @@ impl PyEnumerate {
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::new(cls, args, vm)
}
#[pymethod(magic)]
fn reduce(zelf: PyRef<Self>) -> (PyTypeRef, (PyIter, BigInt)) {
(
zelf.clone_class(),
(zelf.iterator.clone(), zelf.counter.read().clone()),
)
}
}
impl IterNextIterable for PyEnumerate {}
+4 -4
View File
@@ -5,9 +5,9 @@ use super::{
tuple::PyTupleTyped, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyStrRef,
PyTupleRef, PyTypeRef,
};
use crate::common::lock::PyMutex;
use crate::{
bytecode,
common::lock::PyMutex,
frame::Frame,
function::{FuncArgs, OptionalArg},
protocol::PyMapping,
@@ -136,10 +136,10 @@ impl PyFunction {
);
}
*slot = Some(value);
} else if argpos(0..code.posonlyarg_count, &name).is_some() {
posonly_passed_as_kwarg.push(name);
} else if let Some(kwargs) = kwargs.as_ref() {
kwargs.set_item(name, value, vm)?;
} else if argpos(0..code.posonlyarg_count, &name).is_some() {
posonly_passed_as_kwarg.push(name);
} else {
return Err(
vm.new_type_error(format!("got an unexpected keyword argument '{}'", name))
@@ -299,7 +299,7 @@ impl PyFunction {
let frame = Frame::new(
code.clone(),
Scope::new(Some(locals), self.globals.clone()),
vm.builtins.dict().unwrap(),
vm.builtins.dict(),
self.closure.as_ref().map_or(&[], |c| c.as_slice()),
vm,
)
+38 -189
View File
@@ -2,24 +2,24 @@ use super::{PositionIterInternal, PyGenericAlias, PySlice, PyTupleRef, PyTypeRef
use crate::common::lock::{
PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard,
};
use crate::sequence::MutObjectSequenceOp;
use crate::{
function::{ArgIterable, FuncArgs, IntoPyObject, OptionalArg},
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sequence::{ObjectSequenceOp, SequenceMutOp, SequenceOp},
sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
stdlib::sys,
types::{
richcompare_wrapper, AsMapping, Comparable, Constructor, Hashable, IterNext,
IterNextIterable, Iterable, PyComparisonOp, RichCompareFunc, Unconstructible, Unhashable,
AsMapping, Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable,
PyComparisonOp, Unconstructible, Unhashable,
},
utils::Either,
vm::{ReprGuard, VirtualMachine},
IdProtocol, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef,
PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef, PyRef, PyResult,
PyValue, TryFromObject, TypeProtocol,
};
use std::fmt;
use std::mem::size_of;
use std::ops::{DerefMut, Range};
use std::ops::DerefMut;
/// Built-in mutable sequence.
///
@@ -240,189 +240,26 @@ impl PyList {
#[pymethod(magic)]
#[pymethod(name = "__rmul__")]
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let new_elements = sequence::seq_mul(vm, &self.borrow_vec(), value)?
.cloned()
.collect();
Ok(Self::new_ref(new_elements, &vm.ctx))
fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let elements = &*self.borrow_vec();
let v = elements.mul(vm, n)?;
Ok(Self::new_ref(v, &vm.ctx))
}
#[pymethod(magic)]
fn imul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let mut elements = zelf.borrow_vec_mut();
let mut new_elements: Vec<PyObjectRef> =
sequence::seq_mul(vm, &*elements, value)?.cloned().collect();
std::mem::swap(elements.deref_mut(), &mut new_elements);
Ok(zelf.clone())
}
fn _iter_equal<F: FnMut(), const SHORT: bool>(
&self,
needle: &PyObject,
range: Range<usize>,
mut f: F,
vm: &VirtualMachine,
) -> PyResult<usize> {
let needle_cls = needle.class();
let needle_cmp = needle_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
let mut borrower = None;
let mut i = range.start;
let index = loop {
if i >= range.end {
break usize::MAX;
}
let guard = if let Some(x) = borrower.take() {
x
} else {
self.borrow_vec()
};
let elem = if let Some(x) = guard.get(i) {
x
} else {
break usize::MAX;
};
if elem.is(needle) {
f();
if SHORT {
break i;
}
borrower = Some(guard);
} else {
let elem_cls = elem.class();
let reverse_first = !elem_cls.is(&needle_cls) && elem_cls.issubclass(&needle_cls);
let eq = if reverse_first {
let elem_cmp = elem_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
drop(elem_cls);
fn cmp(
elem: &PyObject,
needle: &PyObject,
elem_cmp: RichCompareFunc,
needle_cmp: RichCompareFunc,
vm: &VirtualMachine,
) -> PyResult<bool> {
match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => Ok(false),
},
}
}
if elem_cmp as usize == richcompare_wrapper as usize {
let elem = elem.clone();
drop(guard);
cmp(&elem, needle, elem_cmp, needle_cmp, vm)?
} else {
let eq = cmp(elem, needle, elem_cmp, needle_cmp, vm)?;
borrower = Some(guard);
eq
}
} else {
match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => {
drop(elem_cls);
borrower = Some(guard);
value
}
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
drop(elem_cls);
borrower = Some(guard);
obj.try_to_bool(vm)?
}
_ => {
let elem_cmp = elem_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
drop(elem_cls);
fn cmp(
elem: &PyObject,
needle: &PyObject,
elem_cmp: RichCompareFunc,
vm: &VirtualMachine,
) -> PyResult<bool> {
match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => Ok(false),
}
}
if elem_cmp as usize == richcompare_wrapper as usize {
let elem = elem.clone();
drop(guard);
cmp(&elem, needle, elem_cmp, vm)?
} else {
let eq = cmp(elem, needle, elem_cmp, vm)?;
borrower = Some(guard);
eq
}
}
}
};
if eq {
f();
if SHORT {
break i;
}
}
}
i += 1;
};
// TODO: Optioned<usize>
Ok(index)
}
fn foreach_equal<F: FnMut()>(
&self,
needle: &PyObject,
f: F,
vm: &VirtualMachine,
) -> PyResult<()> {
self._iter_equal::<_, false>(needle, 0..usize::MAX, f, vm)
.map(|_| ())
}
fn find_equal(
&self,
needle: &PyObject,
range: Range<usize>,
vm: &VirtualMachine,
) -> PyResult<usize> {
self._iter_equal::<_, true>(needle, range, || {}, vm)
fn imul(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
zelf.borrow_vec_mut().imul(vm, n)?;
Ok(zelf)
}
#[pymethod]
fn count(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
let mut count = 0;
self.foreach_equal(&needle, || count += 1, vm)?;
Ok(count)
self.mut_count(vm, &needle)
}
#[pymethod(magic)]
pub(crate) fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
Ok(self.find_equal(&needle, 0..usize::MAX, vm)? != usize::MAX)
self.mut_contains(vm, &needle)
}
#[pymethod]
@@ -437,12 +274,12 @@ impl PyList {
let start = start.map(|i| saturate_index(i, len)).unwrap_or(0);
let stop = stop
.map(|i| saturate_index(i, len))
.unwrap_or(sys::MAXSIZE as usize);
let index = self.find_equal(&needle, start..stop, vm)?;
if index == usize::MAX {
Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
} else {
.unwrap_or(isize::MAX as usize);
let index = self.mut_index_range(vm, &needle, start..stop)?;
if let Some(index) = index.into() {
Ok(index)
} else {
Err(vm.new_value_error(format!("'{}' is not in list", needle.str(vm)?)))
}
}
@@ -464,9 +301,9 @@ impl PyList {
#[pymethod]
fn remove(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let index = self.find_equal(&needle, 0..usize::MAX, vm)?;
let index = self.mut_index(vm, &needle)?;
if index != usize::MAX {
if let Some(index) = index.into() {
// defer delete out of borrow
Ok(self.borrow_vec_mut().remove(index))
} else {
@@ -540,6 +377,18 @@ impl PyList {
}
}
impl<'a> MutObjectSequenceOp<'a> for PyList {
type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>;
fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef> {
guard.get(index)
}
fn do_lock(&'a self) -> Self::Guard {
self.borrow_vec()
}
}
impl AsMapping for PyList {
fn as_mapping(_zelf: &crate::PyObjectView<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
PyMappingMethods {
@@ -593,9 +442,9 @@ impl Comparable for PyList {
return Ok(res.into());
}
let other = class_or_notimplemented!(Self, other);
let a = zelf.borrow_vec();
let b = other.borrow_vec();
sequence::cmp(vm, a.boxed_iter(), b.boxed_iter(), op).map(PyComparisonValue::Implemented)
let a = &*zelf.borrow_vec();
let b = &*other.borrow_vec();
a.cmp(vm, b, op).map(PyComparisonValue::Implemented)
}
}
+74 -57
View File
@@ -1,13 +1,15 @@
use super::pystr::IntoPyStrRef;
use super::{PyDictRef, PyStr, PyStrRef, PyTypeRef};
use crate::{
function::{FuncArgs, IntoPyObject},
types::GetAttr,
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine,
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue,
VirtualMachine,
};
#[pyclass(module = false, name = "module")]
#[derive(Debug)]
pub struct PyModule;
pub struct PyModule {}
impl PyValue for PyModule {
fn class(vm: &VirtualMachine) -> &PyTypeRef {
@@ -15,29 +17,6 @@ impl PyValue for PyModule {
}
}
pub fn init_module_dict(
vm: &VirtualMachine,
module_dict: &PyDictRef,
name: PyObjectRef,
doc: PyObjectRef,
) {
module_dict
.set_item("__name__", name, vm)
.expect("Failed to set __name__ on module");
module_dict
.set_item("__doc__", doc, vm)
.expect("Failed to set __doc__ on module");
module_dict
.set_item("__package__", vm.ctx.none(), vm)
.expect("Failed to set __package__ on module");
module_dict
.set_item("__loader__", vm.ctx.none(), vm)
.expect("Failed to set __loader__ on module");
module_dict
.set_item("__spec__", vm.ctx.none(), vm)
.expect("Failed to set __spec__ on module");
}
#[derive(FromArgs)]
struct ModuleInitArgs {
name: PyStrRef,
@@ -47,6 +26,15 @@ struct ModuleInitArgs {
#[pyimpl(with(GetAttr), flags(BASETYPE, HAS_DICT))]
impl PyModule {
// pub(crate) fn new(d: PyDictRef) -> Self {
// PyModule { dict: d.into() }
// }
// #[inline]
// pub fn dict(&self) -> PyDictRef {
// self.dict.get()
// }
#[pyslot]
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
PyModule {}.into_pyresult_with_type(vm, cls)
@@ -58,22 +46,30 @@ impl PyModule {
.slots
.flags
.has_feature(crate::types::PyTypeFlags::HAS_DICT));
init_module_dict(
vm,
&zelf.as_object().dict().unwrap(),
args.name.into(),
args.doc.into_pyobject(vm),
);
zelf.init_module_dict(args.name.into(), args.doc.into_pyobject(vm), vm);
}
fn name(zelf: PyRef<Self>, vm: &VirtualMachine) -> Option<String> {
vm.generic_getattribute_opt(
zelf.as_object().to_owned(),
PyStr::from("__name__").into_ref(vm),
None,
)
.unwrap_or(None)
.and_then(|obj| obj.payload::<PyStr>().map(|s| s.as_str().to_owned()))
fn getattr_inner(zelf: &PyObjectView<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
if let Some(attr) =
vm.generic_getattribute_opt(zelf.as_object().to_owned(), name.clone(), None)?
{
return Ok(attr);
}
if let Ok(getattr) = zelf.dict().get_item("__getattr__", vm) {
return vm.invoke(&getattr, (name,));
}
let module_name = if let Some(name) = Self::name(zelf.to_owned(), vm) {
format!(" '{}'", name)
} else {
"".to_owned()
};
Err(vm.new_attribute_error(format!("module{} has no attribute '{}'", module_name, name)))
}
fn name(zelf: PyRef<Self>, vm: &VirtualMachine) -> Option<PyStrRef> {
vm.generic_getattribute_opt(zelf.into(), PyStr::from("__name__").into_ref(vm), None)
.unwrap_or(None)
.and_then(|obj| obj.downcast::<PyStr>().ok())
}
#[pymethod(magic)]
@@ -94,26 +90,47 @@ impl PyModule {
}
}
impl PyObjectView<PyModule> {
// TODO: to be replaced by the commented-out dict method above once dictoffsets land
pub fn dict(&self) -> PyDictRef {
self.as_object().dict().unwrap()
}
// TODO: should be on PyModule, not PyObjectView<PyModule>
pub(crate) fn init_module_dict(
&self,
name: PyObjectRef,
doc: PyObjectRef,
vm: &VirtualMachine,
) {
let dict = self.dict();
dict.set_item("__name__", name, vm)
.expect("Failed to set __name__ on module");
dict.set_item("__doc__", doc, vm)
.expect("Failed to set __doc__ on module");
dict.set_item("__package__", vm.ctx.none(), vm)
.expect("Failed to set __package__ on module");
dict.set_item("__loader__", vm.ctx.none(), vm)
.expect("Failed to set __loader__ on module");
dict.set_item("__spec__", vm.ctx.none(), vm)
.expect("Failed to set __spec__ on module");
}
pub fn get_attr(&self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult {
PyModule::getattr_inner(self, attr_name.into_pystr_ref(vm), vm)
}
pub fn set_attr(
&self,
attr_name: impl IntoPyStrRef,
attr_value: impl Into<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
self.as_object().set_attr(attr_name, attr_value, vm)
}
}
impl GetAttr for PyModule {
fn getattro(zelf: PyRef<Self>, name: PyStrRef, vm: &VirtualMachine) -> PyResult {
if let Some(attr) =
vm.generic_getattribute_opt(zelf.as_object().to_owned(), name.clone(), None)?
{
return Ok(attr);
}
if let Some(getattr) = zelf
.as_object()
.dict()
.and_then(|d| d.get_item("__getattr__", vm).ok())
{
return vm.invoke(&getattr, (name,));
}
let module_name = if let Some(name) = Self::name(zelf, vm) {
format!(" '{}'", name)
} else {
"".to_owned()
};
Err(vm.new_attribute_error(format!("module{} has no attribute '{}'", module_name, name)))
Self::getattr_inner(&zelf, name, vm)
}
}
+19 -18
View File
@@ -13,7 +13,7 @@ use crate::{
/// SimpleNamespace(**kwargs)
#[pyclass(module = false, name = "SimpleNamespace")]
#[derive(Debug)]
pub struct PyNamespace;
pub struct PyNamespace {}
impl PyValue for PyNamespace {
fn class(vm: &VirtualMachine) -> &PyTypeRef {
@@ -31,7 +31,11 @@ impl Constructor for PyNamespace {
impl PyNamespace {
pub fn new_ref(ctx: &PyContext) -> PyRef<Self> {
PyRef::new_ref(Self, ctx.types.namespace_type.clone(), Some(ctx.new_dict()))
PyRef::new_ref(
Self {},
ctx.types.namespace_type.clone(),
Some(ctx.new_dict()),
)
}
}
@@ -58,18 +62,14 @@ impl PyNamespace {
};
let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
let parts = if let Some(dict) = zelf.as_object().dict() {
let mut parts = Vec::with_capacity(dict.len());
for (key, value) in dict {
let k = &key.repr(vm)?;
let key_str = k.as_str();
let value_repr = value.repr(vm)?;
parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr));
}
parts
} else {
vec![]
};
let dict = zelf.as_object().dict().unwrap();
let mut parts = Vec::with_capacity(dict.len());
for (key, value) in dict {
let k = &key.repr(vm)?;
let key_str = k.as_str();
let value_repr = value.repr(vm)?;
parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr));
}
format!("{}({})", name, parts.join(", "))
} else {
format!("{}(...)", name)
@@ -86,10 +86,11 @@ impl Comparable for PyNamespace {
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
let other = class_or_notimplemented!(Self, other);
match (zelf.as_object().dict(), other.as_object().dict()) {
(Some(d1), Some(d2)) => PyDict::cmp(&d1, d2.as_object(), op, vm),
_ => Ok(PyComparisonValue::NotImplemented),
}
let (d1, d2) = (
zelf.as_object().dict().unwrap(),
other.as_object().dict().unwrap(),
);
PyDict::cmp(&d1, d2.as_object(), op, vm)
}
}
+11 -7
View File
@@ -2,8 +2,8 @@ use super::{PyDict, PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef};
use crate::common::hash::PyHash;
use crate::{
function::FuncArgs, types::PyComparisonOp, utils::Either, IdProtocol, ItemProtocol,
PyArithmeticValue, PyAttributes, PyClassImpl, PyComparisonValue, PyContext, PyGenericObject,
PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, VirtualMachine,
PyArithmeticValue, PyAttributes, PyClassImpl, PyComparisonValue, PyContext, PyObject,
PyObjectRef, PyResult, PyValue, TypeProtocol, VirtualMachine,
};
/// object()
@@ -46,7 +46,7 @@ impl PyBaseObject {
}
}
Ok(PyGenericObject::new(PyBaseObject, cls, dict))
Ok(crate::PyRef::new_ref(PyBaseObject, cls, dict).into())
}
#[pyslot]
@@ -160,13 +160,13 @@ impl PyBaseObject {
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
setattr(&obj, name, Some(value), vm)
generic_setattr(&obj, name, Some(value), vm)
}
/// Implement delattr(self, name).
#[pymethod]
fn __delattr__(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
setattr(&obj, name, None, vm)
generic_setattr(&obj, name, None, vm)
}
#[pyslot]
@@ -176,7 +176,7 @@ impl PyBaseObject {
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
setattr(&*obj, attr_name, value, vm)
generic_setattr(&*obj, attr_name, value, vm)
}
/// Return str(self).
@@ -328,8 +328,12 @@ pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -
.map_err(|_| vm.new_attribute_error("This object has no __dict__".to_owned()))
}
pub fn generic_getattr(obj: PyObjectRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult {
vm.generic_getattribute(obj, attr_name)
}
#[cfg_attr(feature = "flame-it", flame)]
pub(crate) fn setattr(
pub fn generic_setattr(
obj: &PyObject,
attr_name: PyStrRef,
value: Option<PyObjectRef>,
+17 -7
View File
@@ -8,6 +8,7 @@ use crate::{
format::{FormatSpec, FormatString, FromTemplate},
function::{ArgIterable, FuncArgs, IntoPyException, IntoPyObject, OptionalArg, OptionalOption},
protocol::{PyIterReturn, PyMappingMethods},
sequence::SequenceOp,
sliceable::PySliceableSequence,
types::{
AsMapping, Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable,
@@ -89,13 +90,22 @@ impl PyStrKindData {
/// encoding defaults to sys.getdefaultencoding().
/// errors defaults to 'strict'."
#[pyclass(module = false, name = "str")]
#[derive(Debug)]
pub struct PyStr {
bytes: Box<[u8]>,
kind: PyStrKindData,
hash: PyAtomic<hash::PyHash>,
}
impl fmt::Debug for PyStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PyStr")
.field("value", &self.as_str())
.field("kind", &self.kind)
.field("hash", &self.hash)
.finish()
}
}
impl AsRef<str> for PyStr {
fn as_ref(&self) -> &str {
self.as_str()
@@ -484,11 +494,10 @@ impl PyStr {
// This only works for `str` itself, not its subclasses.
return Ok(zelf);
}
// todo: map err to overflow.
vm.check_repeat_or_memory_error(zelf.len(), value)
.map(|value| Self::from(zelf.as_str().repeat(value)).into_ref(vm))
// see issue 45044 on b.p.o.
.map_err(|_| vm.new_overflow_error("repeated bytes are too long".to_owned()))
zelf.as_str()
.as_bytes()
.mul(vm, value)
.map(|x| Self::from(unsafe { String::from_utf8_unchecked(x) }).into_ref(vm))
}
#[pymethod(magic)]
@@ -709,7 +718,8 @@ impl PyStr {
#[pymethod]
fn isdecimal(&self) -> bool {
!self.bytes.is_empty() && self.char_all(|c| c.is_ascii_digit())
!self.bytes.is_empty()
&& self.char_all(|c| GeneralCategory::of(c) == GeneralCategory::DecimalNumber)
}
#[pymethod(name = "__mod__")]
+12 -8
View File
@@ -1,6 +1,6 @@
use super::{
mappingproxy::PyMappingProxy, object, PyClassMethod, PyDictRef, PyList, PyStaticMethod, PyStr,
PyStrRef, PyTuple, PyTupleRef, PyWeak,
PyStrRef, PyTuple, PyTupleRef,
};
use crate::common::{
ascii,
@@ -10,8 +10,8 @@ use crate::common::{
use crate::{
function::{FuncArgs, KwArgs, OptionalArg},
types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr},
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyRef, PyResult,
PyValue, StaticType, TypeProtocol, VirtualMachine,
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyObjectWeak, PyRef,
PyResult, PyValue, StaticType, TypeProtocol, VirtualMachine,
};
use itertools::Itertools;
use std::collections::HashSet;
@@ -26,7 +26,7 @@ pub struct PyType {
pub base: Option<PyTypeRef>,
pub bases: Vec<PyTypeRef>,
pub mro: Vec<PyTypeRef>,
pub subclasses: PyRwLock<Vec<PyWeak>>,
pub subclasses: PyRwLock<Vec<PyObjectWeak>>,
pub attributes: PyRwLock<PyAttributes>,
pub slots: PyTypeSlots,
}
@@ -96,7 +96,7 @@ impl PyType {
slots.flags |= PyTypeFlags::HAS_DICT
}
*slots.name.write() = Some(String::from(name));
*slots.name.get_mut() = Some(String::from(name));
let new_type = PyRef::new_ref(
PyType {
@@ -116,10 +116,14 @@ impl PyType {
new_type.update_slot(attr_name, true);
}
}
let weakref_type = super::PyWeak::static_type();
for base in &new_type.bases {
base.subclasses
.write()
.push(PyWeak::downgrade(new_type.as_object()));
base.subclasses.write().push(
new_type
.as_object()
.downgrade_with_weakref_typ_opt(None, weakref_type.clone())
.unwrap(),
);
}
Ok(new_type)
+1 -1
View File
@@ -195,7 +195,7 @@ impl PySetInner {
self.compare(&other_set, PyComparisonOp::Le, vm)
}
fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
pub(super) fn isdisjoint(&self, other: ArgIterable, vm: &VirtualMachine) -> PyResult<bool> {
for item in other.iter(vm)? {
if self.contains(&*item?, vm)? {
return Ok(false);
+14
View File
@@ -194,6 +194,20 @@ impl PySlice {
let (start, stop, step) = self.inner_indices(length, vm)?;
Ok(vm.new_tuple((start, stop, step)))
}
#[allow(clippy::type_complexity)]
#[pymethod(magic)]
fn reduce(
zelf: PyRef<Self>,
) -> PyResult<(
PyTypeRef,
(Option<PyObjectRef>, PyObjectRef, Option<PyObjectRef>),
)> {
Ok((
zelf.clone_class(),
(zelf.start.clone(), zelf.stop.clone(), zelf.step.clone()),
))
}
}
impl Comparable for PySlice {
+4 -6
View File
@@ -3,7 +3,7 @@ use crate::common::hash::PyHash;
use crate::{
function::{IntoPyObject, OptionalArg},
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sequence::{ObjectSequenceOp, SequenceOp},
sliceable::PySliceableSequence,
stdlib::sys,
types::{
@@ -228,10 +228,8 @@ impl PyTuple {
// This only works for `tuple` itself, not its subclasses.
zelf
} else {
let elements = sequence::seq_mul(vm, &zelf.elements, value)?
.cloned()
.collect::<Vec<_>>()
.into_boxed_slice();
let v = zelf.elements.mul(vm, value)?;
let elements = v.into_boxed_slice();
Self { elements }.into_ref(vm)
})
}
@@ -360,7 +358,7 @@ impl Comparable for PyTuple {
let other = class_or_notimplemented!(Self, other);
let a = zelf.as_slice();
let b = other.as_slice();
sequence::cmp(vm, a.boxed_iter(), b.boxed_iter(), op).map(PyComparisonValue::Implemented)
a.cmp(vm, b, op).map(PyComparisonValue::Implemented)
}
}
+19 -7
View File
@@ -1,14 +1,14 @@
use super::{PyStrRef, PyTypeRef, PyWeak};
use super::{PyStrRef, PyTypeRef};
use crate::{
function::OptionalArg,
types::{Constructor, SetAttr},
PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, VirtualMachine,
PyClassImpl, PyContext, PyObjectRef, PyObjectWeak, PyResult, PyValue, VirtualMachine,
};
#[pyclass(module = false, name = "weakproxy")]
#[derive(Debug)]
pub struct PyWeakProxy {
weak: PyWeak,
weak: PyObjectWeak,
}
impl PyValue for PyWeakProxy {
@@ -33,16 +33,28 @@ impl Constructor for PyWeakProxy {
Self::Args { referent, callback }: Self::Args,
vm: &VirtualMachine,
) -> PyResult {
if callback.is_present() {
panic!("Passed a callback to weakproxy, but weakproxy does not yet support proxies.");
}
// using an internal subclass as the class prevents us from getting the generic weakref,
// which would mess up the weakref count
let weak_cls = WEAK_SUBCLASS.get_or_init(|| {
vm.ctx.new_class(
None,
"__weakproxy",
&vm.ctx.types.weakref_type,
super::PyWeak::make_slots(),
)
});
// TODO: PyWeakProxy should use the same payload as PyWeak
PyWeakProxy {
weak: PyWeak::downgrade(&referent),
weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?,
}
.into_pyresult_with_type(vm, cls)
}
}
crate::common::static_cell! {
static WEAK_SUBCLASS: PyTypeRef;
}
#[pyimpl(with(SetAttr, Constructor))]
impl PyWeakProxy {
// TODO: callbacks
+25 -38
View File
@@ -1,40 +1,21 @@
use super::{PyGenericAlias, PyTypeRef};
use crate::common::hash::PyHash;
use crate::common::atomic::{Ordering, Radium};
use crate::common::hash::{self, PyHash};
use crate::{
function::OptionalArg,
types::{Callable, Comparable, Constructor, Hashable, PyComparisonOp},
IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyObjectWeak, PyRef, PyResult,
PyValue, TypeProtocol, VirtualMachine,
IdProtocol, PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue,
TypeProtocol, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
#[pyclass(module = false, name = "weakref")]
#[derive(Debug)]
pub struct PyWeak {
referent: PyObjectWeak,
hash: AtomicCell<Option<PyHash>>,
}
impl PyWeak {
pub fn downgrade(obj: &PyObject) -> PyWeak {
PyWeak {
referent: obj.downgrade(),
hash: AtomicCell::new(None),
}
}
pub fn upgrade(&self) -> Option<PyObjectRef> {
self.referent.upgrade()
}
}
pub use crate::pyobjectrc::PyWeak;
#[derive(FromArgs)]
pub struct WeakNewArgs {
#[pyarg(positional)]
referent: PyObjectRef,
#[pyarg(positional, optional)]
_callback: OptionalArg<PyObjectRef>,
callback: OptionalArg<PyObjectRef>,
}
impl PyValue for PyWeak {
@@ -54,16 +35,13 @@ impl Callable for PyWeak {
impl Constructor for PyWeak {
type Args = WeakNewArgs;
// TODO callbacks
fn py_new(
cls: PyTypeRef,
Self::Args {
referent,
_callback,
}: Self::Args,
Self::Args { referent, callback }: Self::Args,
vm: &VirtualMachine,
) -> PyResult {
PyWeak::downgrade(&referent).into_pyresult_with_type(vm, cls)
let weak = referent.downgrade_with_typ(callback.into_option(), cls, vm)?;
Ok(weak.into_object())
}
}
@@ -92,17 +70,26 @@ impl PyWeak {
impl Hashable for PyWeak {
fn hash(zelf: &crate::PyObjectView<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
match zelf.hash.load() {
Some(hash) => Ok(hash),
None => {
let hash = match zelf.hash.load(Ordering::Relaxed) {
hash::SENTINEL => {
let obj = zelf
.upgrade()
.ok_or_else(|| vm.new_type_error("weak object has gone away".to_owned()))?;
let hash = obj.hash(vm)?;
zelf.hash.store(Some(hash));
Ok(hash)
match Radium::compare_exchange(
&zelf.hash,
hash::SENTINEL,
hash::fix_sentinel(hash),
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => hash,
Err(prev_stored) => prev_stored,
}
}
}
hash => hash,
};
Ok(hash)
}
}
@@ -118,7 +105,7 @@ impl Comparable for PyWeak {
let both = zelf.upgrade().and_then(|s| other.upgrade().map(|o| (s, o)));
let eq = match both {
Some((a, b)) => vm.bool_eq(&a, &b)?,
None => false,
None => zelf.is(other),
};
Ok(eq.into())
})
+8 -4
View File
@@ -4,8 +4,8 @@ use crate::{
pystr, PyByteArray, PyBytes, PyBytesRef, PyInt, PyIntRef, PyStr, PyStrRef, PyTypeRef,
},
cformat::CFormatBytes,
function::ArgIterable,
function::{OptionalArg, OptionalOption},
function::{ArgIterable, OptionalArg, OptionalOption},
sequence::{SequenceMutOp, SequenceOp},
sliceable::PySliceableSequence,
types::PyComparisonOp,
utils::Either,
@@ -909,8 +909,12 @@ impl PyBytesInner {
.format(vm, values)
}
pub fn repeat(&self, n: usize) -> Vec<u8> {
self.elements.repeat(n)
pub fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
self.elements.mul(vm, n)
}
pub fn imul(&mut self, n: isize, vm: &VirtualMachine) -> PyResult<()> {
self.elements.imul(vm, n)
}
}
+48 -25
View File
@@ -13,6 +13,7 @@ use crate::{
};
use std::fmt;
use std::mem::size_of;
use std::ops::ControlFlow;
// HashIndex is intended to be same size with hash::PyHash
// but it doesn't mean the values are compatible with actual pyhash value
@@ -223,6 +224,8 @@ impl<T> DictInner<T> {
}
}
type PopInnerResult<T> = ControlFlow<Option<DictEntry<T>>>;
impl<T: Clone> Dict<T> {
fn read(&self) -> PyRwLockReadGuard<'_, DictInner<T>> {
self.inner.read()
@@ -348,18 +351,23 @@ impl<T: Clone> Dict<T> {
pub fn delete_if_exists<K>(&self, vm: &VirtualMachine, key: &K) -> PyResult<bool>
where
K: DictKey,
{
self.delete_if(vm, key, |_| Ok(true))
}
/// pred should be VERY CAREFUL about what it does as it is called while
/// the dict's internal mutex is held
pub(crate) fn delete_if<K, F>(&self, vm: &VirtualMachine, key: &K, pred: F) -> PyResult<bool>
where
K: DictKey,
F: Fn(&T) -> PyResult<bool>,
{
let hash = key.key_hash(vm)?;
let deleted = loop {
let lookup = self.lookup(vm, key, hash, None)?;
if let IndexEntry::Index(_) = lookup.0 {
if let Ok(Some(entry)) = self.pop_inner(lookup) {
break Some(entry);
} else {
// The dict was changed since we did lookup. Let's try again.
}
} else {
break None;
match self.pop_inner_if(lookup, &pred)? {
ControlFlow::Break(entry) => break entry,
ControlFlow::Continue(()) => continue,
}
};
Ok(deleted.is_some())
@@ -371,10 +379,9 @@ impl<T: Clone> Dict<T> {
let lookup = self.lookup(vm, key, hash, None)?;
let (entry, index_index) = lookup;
if let IndexEntry::Index(_) = entry {
if let Ok(Some(entry)) = self.pop_inner(lookup) {
break Some(entry);
} else {
// The dict was changed since we did lookup. Let's try again.
match self.pop_inner(lookup) {
ControlFlow::Break(Some(entry)) => break Some(entry),
_ => continue,
}
} else {
let mut inner = self.write();
@@ -573,25 +580,42 @@ impl<T: Clone> Dict<T> {
}
// returns Err(()) if changed since lookup
fn pop_inner(&self, lookup: LookupResult) -> Result<Option<DictEntry<T>>, ()> {
fn pop_inner(&self, lookup: LookupResult) -> PopInnerResult<T> {
self.pop_inner_if(lookup, |_| Ok::<_, std::convert::Infallible>(true))
.unwrap_or_else(|x| match x {})
}
fn pop_inner_if<E>(
&self,
lookup: LookupResult,
pred: impl Fn(&T) -> Result<bool, E>,
) -> Result<PopInnerResult<T>, E> {
let (entry_index, index_index) = lookup;
let entry_index = if let IndexEntry::Index(entry_index) = entry_index {
entry_index
} else {
return Ok(None);
return Ok(ControlFlow::Break(None));
};
let mut inner = self.write();
if matches!(inner.entries.get(entry_index), Some(Some(entry)) if entry.index == index_index)
{
// all good
let inner = &mut *self.write();
let slot = if let Some(slot) = inner.entries.get_mut(entry_index) {
slot
} else {
// The dict was changed since we did lookup. Let's try again.
return Err(());
return Ok(ControlFlow::Continue(()));
};
match slot {
Some(entry) if entry.index == index_index => {
if !pred(&entry.value)? {
return Ok(ControlFlow::Break(None));
}
}
// The dict was changed since we did lookup. Let's try again.
_ => return Ok(ControlFlow::Continue(())),
}
inner.indices[index_index] = IndexEntry::DUMMY;
inner.used -= 1;
let removed = std::mem::take(&mut inner.entries[entry_index]);
Ok(removed)
let removed = slot.take();
Ok(ControlFlow::Break(removed))
}
/// Retrieve and delete a key
@@ -599,10 +623,9 @@ impl<T: Clone> Dict<T> {
let hash_value = key.key_hash(vm)?;
let removed = loop {
let lookup = self.lookup(vm, key, hash_value, None)?;
if let Ok(ret) = self.pop_inner(lookup) {
break ret.map(|e| e.value);
} else {
// changed since lookup, loop again
match self.pop_inner(lookup) {
ControlFlow::Break(entry) => break entry.map(|e| e.value),
ControlFlow::Continue(()) => continue,
}
};
Ok(removed)
+1 -1
View File
@@ -66,7 +66,7 @@ mod pyobject;
mod pyobjectrc;
pub mod readline;
pub mod scope;
mod sequence;
pub mod sequence;
pub mod signal;
pub mod sliceable;
pub mod stdlib;
+30 -6
View File
@@ -4,17 +4,16 @@ use crate::common::{
static_cell,
};
pub use crate::pyobjectrc::{
PyGenericObject, PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyObjectWrap, PyRef,
PyWeakRef,
PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyObjectWrap, PyRef, PyWeakRef,
};
use crate::{
builtins::{
builtinfunc::{PyBuiltinFunction, PyBuiltinMethod, PyNativeFuncDef},
bytes,
getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetSet},
object, pystr, PyBaseExceptionRef, PyBoundMethod, PyDict, PyDictRef, PyEllipsis, PyFloat,
PyFrozenSet, PyGenericAlias, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented,
PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef,
object, pystr, PyBaseException, PyBaseExceptionRef, PyBoundMethod, PyDict, PyDictRef,
PyEllipsis, PyFloat, PyFrozenSet, PyGenericAlias, PyInt, PyIntRef, PyList, PyListRef,
PyNone, PyNotImplemented, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef,
},
dictdatatype::Dict,
exceptions,
@@ -94,6 +93,7 @@ impl PyContext {
let types = TypeZoo::init();
let exceptions = exceptions::ExceptionZoo::init();
#[inline]
fn create_object<T: PyObjectPayload + PyValue>(payload: T, cls: &PyTypeRef) -> PyRef<T> {
PyRef::new_ref(payload, cls.clone(), None)
}
@@ -242,6 +242,30 @@ impl PyContext {
.unwrap()
}
pub fn new_exception_type(
&self,
module: &str,
name: &str,
bases: Option<Vec<PyTypeRef>>,
) -> PyTypeRef {
let bases = if let Some(bases) = bases {
bases
} else {
vec![self.exceptions.exception_type.clone()]
};
let mut attrs = PyAttributes::default();
attrs.insert("__module__".to_string(), self.new_str(module).into());
PyType::new_ref(
name,
bases,
attrs,
PyBaseException::make_slots(),
self.types.type_type.clone(),
)
.unwrap()
}
#[inline]
pub fn make_funcdef<F, FKind>(&self, name: impl Into<PyStr>, f: F) -> PyNativeFuncDef
where
@@ -309,7 +333,7 @@ impl PyContext {
class.slots.flags.contains(PyTypeFlags::HAS_DICT),
dict.is_some()
);
PyGenericObject::new(object::PyBaseObject, class, dict)
PyRef::new_ref(object::PyBaseObject, class, dict).into()
}
}
+583 -163
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -33,7 +33,7 @@ impl Scope {
) -> Scope {
if !globals.contains_key("__builtins__", vm) {
globals
.set_item("__builtins__", vm.builtins.clone(), vm)
.set_item("__builtins__", vm.builtins.clone().into(), vm)
.unwrap();
}
Scope::new(locals, globals)
+275 -88
View File
@@ -1,107 +1,294 @@
use crate::types::PyComparisonOp;
use crate::vm::VirtualMachine;
use crate::{PyObjectRef, PyResult};
use itertools::Itertools;
use optional::Optioned;
use std::{collections::VecDeque, ops::Range};
pub(super) type DynPyIter<'a> = Box<dyn ExactSizeIterator<Item = &'a PyObjectRef> + 'a>;
use crate::{
types::{richcompare_wrapper, PyComparisonOp, RichCompareFunc},
utils::Either,
vm::VirtualMachine,
IdProtocol, PyComparisonValue, PyObject, PyObjectRef, PyResult, TypeProtocol,
};
#[allow(clippy::len_without_is_empty)]
pub(crate) trait SimpleSeq {
fn len(&self) -> usize;
fn boxed_iter(&self) -> DynPyIter;
}
pub trait ObjectSequenceOp<'a> {
type Iter: ExactSizeIterator<Item = &'a PyObjectRef>;
impl<'a, D> SimpleSeq for D
where
D: 'a + std::ops::Deref<Target = [PyObjectRef]>,
{
fn len(&self) -> usize {
self.deref().len()
}
fn iter(&'a self) -> Self::Iter;
fn boxed_iter(&self) -> DynPyIter {
Box::new(self.deref().iter())
}
}
pub(crate) fn eq(vm: &VirtualMachine, zelf: DynPyIter, other: DynPyIter) -> PyResult<bool> {
if zelf.len() == other.len() {
for (a, b) in Iterator::zip(zelf, other) {
fn eq(&'a self, vm: &VirtualMachine, other: &'a Self) -> PyResult<bool> {
let lhs = self.iter();
let rhs = other.iter();
if lhs.len() != rhs.len() {
return Ok(false);
}
for (a, b) in lhs.zip_eq(rhs) {
if !vm.identical_or_equal(a, b)? {
return Ok(false);
}
}
Ok(true)
} else {
Ok(false)
}
}
pub fn cmp(
vm: &VirtualMachine,
zelf: DynPyIter,
other: DynPyIter,
op: PyComparisonOp,
) -> PyResult<bool> {
let less = match op {
PyComparisonOp::Eq => return eq(vm, zelf, other),
PyComparisonOp::Ne => return eq(vm, zelf, other).map(|eq| !eq),
PyComparisonOp::Lt | PyComparisonOp::Le => true,
PyComparisonOp::Gt | PyComparisonOp::Ge => false,
};
let (lhs_len, rhs_len) = (zelf.len(), other.len());
for (a, b) in Iterator::zip(zelf, other) {
let ret = if less {
vm.bool_seq_lt(a, b)?
} else {
vm.bool_seq_gt(a, b)?
fn cmp(&'a self, vm: &VirtualMachine, other: &'a Self, op: PyComparisonOp) -> PyResult<bool> {
let less = match op {
PyComparisonOp::Eq => return self.eq(vm, other),
PyComparisonOp::Ne => return self.eq(vm, other).map(|eq| !eq),
PyComparisonOp::Lt | PyComparisonOp::Le => true,
PyComparisonOp::Gt | PyComparisonOp::Ge => false,
};
if let Some(v) = ret {
return Ok(v);
}
}
Ok(op.eval_ord(lhs_len.cmp(&rhs_len)))
}
pub(crate) struct SeqMul<'a> {
seq: &'a dyn SimpleSeq,
repetitions: usize,
iter: Option<DynPyIter<'a>>,
}
impl ExactSizeIterator for SeqMul<'_> {}
impl<'a> Iterator for SeqMul<'a> {
type Item = &'a PyObjectRef;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.as_mut().and_then(Iterator::next) {
Some(item) => Some(item),
None => {
if self.repetitions == 0 {
None
} else {
self.repetitions -= 1;
self.iter = Some(self.seq.boxed_iter());
self.next()
}
let lhs = self.iter();
let rhs = other.iter();
let lhs_len = lhs.len();
let rhs_len = rhs.len();
for (a, b) in lhs.zip(rhs) {
let ret = if less {
vm.bool_seq_lt(a, b)?
} else {
vm.bool_seq_gt(a, b)?
};
if let Some(v) = ret {
return Ok(v);
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.iter.as_ref().map_or(0, ExactSizeIterator::len)
+ (self.repetitions * self.seq.len());
(size, Some(size))
Ok(op.eval_ord(lhs_len.cmp(&rhs_len)))
}
}
pub(crate) fn seq_mul<'a>(
vm: &VirtualMachine,
seq: &'a impl SimpleSeq,
repetitions: isize,
) -> PyResult<SeqMul<'a>> {
vm.check_repeat_or_memory_error(seq.len(), repetitions)
.map(|repetitions| SeqMul {
seq,
repetitions,
iter: None,
})
impl<'a> ObjectSequenceOp<'a> for [PyObjectRef] {
type Iter = core::slice::Iter<'a, PyObjectRef>;
fn iter(&'a self) -> Self::Iter {
self.iter()
}
}
impl<'a> ObjectSequenceOp<'a> for VecDeque<PyObjectRef> {
type Iter = std::collections::vec_deque::Iter<'a, PyObjectRef>;
fn iter(&'a self) -> Self::Iter {
self.iter()
}
}
pub trait MutObjectSequenceOp<'a> {
type Guard;
fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef>;
fn do_lock(&'a self) -> Self::Guard;
fn mut_count(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<usize> {
let mut count = 0;
self._mut_iter_equal_skeleton::<_, false>(vm, needle, 0..isize::MAX as usize, || {
count += 1
})?;
Ok(count)
}
fn mut_index_range(
&'a self,
vm: &VirtualMachine,
needle: &PyObject,
range: Range<usize>,
) -> PyResult<Optioned<usize>> {
self._mut_iter_equal_skeleton::<_, true>(vm, needle, range, || {})
}
fn mut_index(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<Optioned<usize>> {
self.mut_index_range(vm, needle, 0..isize::MAX as usize)
}
fn mut_contains(&'a self, vm: &VirtualMachine, needle: &PyObject) -> PyResult<bool> {
self.mut_index(vm, needle).map(|x| x.is_some())
}
fn _mut_iter_equal_skeleton<F, const SHORT: bool>(
&'a self,
vm: &VirtualMachine,
needle: &PyObject,
range: Range<usize>,
mut f: F,
) -> PyResult<Optioned<usize>>
where
F: FnMut(),
{
let needle_cls = needle.class();
let needle_cmp = needle_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
let mut borrower = None;
let mut i = range.start;
let index = loop {
if i >= range.end {
break Optioned::<usize>::none();
}
let guard = if let Some(x) = borrower.take() {
x
} else {
self.do_lock()
};
let elem = if let Some(x) = Self::do_get(i, &guard) {
x
} else {
break Optioned::<usize>::none();
};
if elem.is(needle) {
f();
if SHORT {
break Optioned::<usize>::some(i);
}
borrower = Some(guard);
} else {
let elem_cls = elem.class();
let reverse_first = !elem_cls.is(&needle_cls) && elem_cls.issubclass(&needle_cls);
let eq = if reverse_first {
let elem_cmp = elem_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
drop(elem_cls);
fn cmp(
elem: &PyObject,
needle: &PyObject,
elem_cmp: RichCompareFunc,
needle_cmp: RichCompareFunc,
vm: &VirtualMachine,
) -> PyResult<bool> {
match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => Ok(false),
},
}
}
if elem_cmp as usize == richcompare_wrapper as usize {
let elem = elem.clone();
drop(guard);
cmp(&elem, needle, elem_cmp, needle_cmp, vm)?
} else {
let eq = cmp(elem, needle, elem_cmp, needle_cmp, vm)?;
borrower = Some(guard);
eq
}
} else {
match needle_cmp(needle, elem, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => {
drop(elem_cls);
borrower = Some(guard);
value
}
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
drop(elem_cls);
borrower = Some(guard);
obj.try_to_bool(vm)?
}
_ => {
let elem_cmp = elem_cls
.mro_find_map(|cls| cls.slots.richcompare.load())
.unwrap();
drop(elem_cls);
fn cmp(
elem: &PyObject,
needle: &PyObject,
elem_cmp: RichCompareFunc,
vm: &VirtualMachine,
) -> PyResult<bool> {
match elem_cmp(elem, needle, PyComparisonOp::Eq, vm)? {
Either::B(PyComparisonValue::Implemented(value)) => Ok(value),
Either::A(obj) if !obj.is(&vm.ctx.not_implemented) => {
obj.try_to_bool(vm)
}
_ => Ok(false),
}
}
if elem_cmp as usize == richcompare_wrapper as usize {
let elem = elem.clone();
drop(guard);
cmp(&elem, needle, elem_cmp, vm)?
} else {
let eq = cmp(elem, needle, elem_cmp, vm)?;
borrower = Some(guard);
eq
}
}
}
};
if eq {
f();
if SHORT {
break Optioned::<usize>::some(i);
}
}
}
i += 1;
};
Ok(index)
}
}
pub trait SequenceOp<T: Clone> {
fn as_slice(&self) -> &[T];
fn mul(&self, vm: &VirtualMachine, n: isize) -> PyResult<Vec<T>> {
let n = vm.check_repeat_or_overflow_error(self.as_slice().len(), n)?;
let mut v = Vec::with_capacity(n * self.as_slice().len());
for _ in 0..n {
v.extend_from_slice(self.as_slice());
}
Ok(v)
}
}
impl<T: Clone> SequenceOp<T> for [T] {
fn as_slice(&self) -> &[T] {
self
}
}
pub trait SequenceMutOp<T: Clone> {
fn as_slice(&self) -> &[T];
fn as_vec_mut(&mut self) -> &mut Vec<T>;
fn imul(&mut self, vm: &VirtualMachine, n: isize) -> PyResult<()> {
let n = vm.check_repeat_or_overflow_error(self.as_slice().len(), n)?;
if n == 0 {
self.as_vec_mut().clear();
} else if n != 1 {
let mut sample = self.as_slice().to_vec();
if n != 2 {
self.as_vec_mut().reserve(sample.len() * (n - 1));
for _ in 0..n - 2 {
self.as_vec_mut().extend_from_slice(&sample);
}
}
self.as_vec_mut().append(&mut sample);
}
Ok(())
}
}
impl<T: Clone> SequenceMutOp<T> for Vec<T> {
fn as_slice(&self) -> &[T] {
self.as_slice()
}
fn as_vec_mut(&mut self) -> &mut Vec<T> {
self
}
}
+1 -1
View File
@@ -214,7 +214,7 @@ mod builtins {
let (globals, locals) = match self.globals {
Some(globals) => {
if !globals.contains_key("__builtins__", vm) {
let builtins_dict = vm.builtins.dict().unwrap().into();
let builtins_dict = vm.builtins.dict().into();
globals.set_item("__builtins__", builtins_dict, vm)?;
}
(
+69 -107
View File
@@ -2,16 +2,17 @@ pub(crate) use _collections::make_module;
#[pymodule]
mod _collections {
use crate::builtins::{PositionIterInternal, PyGenericAlias};
use crate::common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard};
use crate::{
builtins::{
IterStatus::{Active, Exhausted},
PyInt, PyTypeRef,
PositionIterInternal, PyGenericAlias, PyInt, PyTypeRef,
},
common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard},
function::{FuncArgs, KwArgs, OptionalArg},
protocol::PyIterReturn,
sequence, sliceable,
sequence::{MutObjectSequenceOp, ObjectSequenceOp},
sliceable,
sliceable::saturate_index,
types::{
Comparable, Constructor, Hashable, IterNext, IterNextIterable, Iterable,
PyComparisonOp, Unhashable,
@@ -22,7 +23,7 @@ mod _collections {
};
use crossbeam_utils::atomic::AtomicCell;
use itertools::Itertools;
use std::cmp::{max, min};
use std::cmp::max;
use std::collections::VecDeque;
#[pyattr]
@@ -54,24 +55,6 @@ mod _collections {
}
}
struct SimpleSeqDeque<'a>(PyRwLockReadGuard<'a, VecDeque<PyObjectRef>>);
impl sequence::SimpleSeq for SimpleSeqDeque<'_> {
fn len(&self) -> usize {
self.0.len()
}
fn boxed_iter(&self) -> sequence::DynPyIter {
Box::new(self.0.iter())
}
}
impl<'a> From<PyRwLockReadGuard<'a, VecDeque<PyObjectRef>>> for SimpleSeqDeque<'a> {
fn from(from: PyRwLockReadGuard<'a, VecDeque<PyObjectRef>>) -> Self {
Self(from)
}
}
#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Iterable))]
impl PyDeque {
#[pyslot]
@@ -178,17 +161,11 @@ mod _collections {
#[pymethod]
fn count(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
let mut count = 0;
let start_state = self.state.load();
let deque = self.borrow_deque().clone();
for elem in deque.iter() {
if vm.identical_or_equal(elem, &obj)? {
count += 1;
}
let count = self.mut_count(vm, &obj)?;
if start_state != self.state.load() {
return Err(vm.new_runtime_error("deque mutated during iteration".to_owned()));
}
if start_state != self.state.load() {
return Err(vm.new_runtime_error("deque mutated during iteration".to_owned()));
}
Ok(count)
}
@@ -235,14 +212,6 @@ mod _collections {
Ok(())
}
fn adjust_negative_index(&self, index: isize) -> usize {
if index.is_negative() {
max(index + self.borrow_deque().len() as isize, 0) as usize
} else {
index as usize
}
}
#[pymethod]
fn index(
&self,
@@ -251,35 +220,25 @@ mod _collections {
stop: OptionalArg<isize>,
vm: &VirtualMachine,
) -> PyResult<usize> {
let deque = self.borrow_deque().clone();
let start_state = self.state.load();
let start = self.adjust_negative_index(start.unwrap_or(0));
let stop = min(
self.adjust_negative_index(stop.unwrap_or_else(|| deque.len() as isize)),
deque.len(),
);
for (i, elem) in deque
.iter()
.skip(start)
.take(stop.saturating_sub(start))
.enumerate()
{
let is_element = vm.identical_or_equal(elem, &obj)?;
if start_state != self.state.load() {
return Err(vm.new_runtime_error("deque mutated during iteration".to_owned()));
}
if is_element {
return Ok(i + start);
}
let len = self.len();
let start = start.map(|i| saturate_index(i, len)).unwrap_or(0);
let stop = stop
.map(|i| saturate_index(i, len))
.unwrap_or(isize::MAX as usize);
let index = self.mut_index_range(vm, &obj, start..stop)?;
if start_state != self.state.load() {
Err(vm.new_runtime_error("deque mutated during iteration".to_owned()))
} else if let Some(index) = index.into() {
Ok(index)
} else {
Err(vm.new_value_error(
obj.repr(vm)
.map(|repr| format!("{} is not in deque", repr))
.unwrap_or_else(|_| String::new()),
))
}
Err(vm.new_value_error(
obj.repr(vm)
.map(|repr| format!("{} is not in deque", repr))
.unwrap_or_else(|_| String::new()),
))
}
#[pymethod]
@@ -326,23 +285,17 @@ mod _collections {
#[pymethod]
fn remove(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let deque = self.borrow_deque().clone();
let start_state = self.state.load();
let index = self.mut_index(vm, &obj)?;
let mut idx = None;
for (i, elem) in deque.iter().enumerate() {
if vm.identical_or_equal(elem, &obj)? {
idx = Some(i);
break;
}
}
let mut deque = self.borrow_deque_mut();
if start_state != self.state.load() {
Err(vm.new_index_error("deque mutated during remove().".to_owned()))
} else {
} else if let Some(index) = index.into() {
let mut deque = self.borrow_deque_mut();
self.state.fetch_add(1);
idx.map(|idx| deque.remove(idx).unwrap())
.ok_or_else(|| vm.new_value_error("deque.remove(x): x not in deque".to_owned()))
Ok(deque.remove(index).unwrap())
} else {
Err(vm.new_value_error("deque.remove(x): x not in deque".to_owned()))
}
}
@@ -431,32 +384,32 @@ mod _collections {
#[pymethod(magic)]
fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
let start_state = self.state.load();
let deque = self.borrow_deque().clone();
for element in deque.iter() {
let is_element = vm.identical_or_equal(element, &needle)?;
if start_state != self.state.load() {
return Err(vm.new_runtime_error("deque mutated during iteration".to_owned()));
}
if is_element {
return Ok(true);
}
let ret = self.mut_contains(vm, &needle)?;
if start_state != self.state.load() {
Err(vm.new_runtime_error("deque mutated during iteration".to_owned()))
} else {
Ok(ret)
}
}
Ok(false)
fn _mul(&self, n: isize, vm: &VirtualMachine) -> PyResult<VecDeque<PyObjectRef>> {
let deque = self.borrow_deque();
let n = vm.check_repeat_or_overflow_error(deque.len(), n)?;
let mul_len = n * deque.len();
let iter = deque.iter().cycle().take(mul_len);
let skipped = self
.maxlen
.and_then(|maxlen| mul_len.checked_sub(maxlen))
.unwrap_or(0);
let deque = iter.skip(skipped).cloned().collect();
Ok(deque)
}
#[pymethod(magic)]
#[pymethod(name = "__rmul__")]
fn mul(&self, value: isize, vm: &VirtualMachine) -> PyResult<Self> {
let deque: SimpleSeqDeque = self.borrow_deque().into();
let mul = sequence::seq_mul(vm, &deque, value)?;
let skipped = self
.maxlen
.and_then(|maxlen| mul.len().checked_sub(maxlen))
.unwrap_or(0);
let deque = mul.skip(skipped).cloned().collect();
fn mul(&self, n: isize, vm: &VirtualMachine) -> PyResult<Self> {
let deque = self._mul(n, vm)?;
Ok(PyDeque {
deque: PyRwLock::new(deque),
maxlen: self.maxlen,
@@ -465,12 +418,9 @@ mod _collections {
}
#[pymethod(magic)]
fn imul(zelf: PyRef<Self>, value: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let mul_deque = zelf.mul(value, vm)?;
std::mem::swap(
&mut *zelf.borrow_deque_mut(),
&mut *mul_deque.borrow_deque_mut(),
);
fn imul(zelf: PyRef<Self>, n: isize, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
let mul_deque = zelf._mul(n, vm)?;
*zelf.borrow_deque_mut() = mul_deque;
Ok(zelf)
}
@@ -536,6 +486,18 @@ mod _collections {
}
}
impl<'a> MutObjectSequenceOp<'a> for PyDeque {
type Guard = PyRwLockReadGuard<'a, VecDeque<PyObjectRef>>;
fn do_get(index: usize, guard: &Self::Guard) -> Option<&PyObjectRef> {
guard.get(index)
}
fn do_lock(&'a self) -> Self::Guard {
self.borrow_deque()
}
}
impl Comparable for PyDeque {
fn cmp(
zelf: &crate::PyObjectView<Self>,
@@ -547,9 +509,9 @@ mod _collections {
return Ok(res.into());
}
let other = class_or_notimplemented!(Self, other);
let (lhs, rhs) = (zelf.borrow_deque(), other.borrow_deque());
sequence::cmp(vm, Box::new(lhs.iter()), Box::new(rhs.iter()), op)
.map(PyComparisonValue::Implemented)
let lhs = zelf.borrow_deque();
let rhs = other.borrow_deque();
lhs.cmp(vm, &rhs, op).map(PyComparisonValue::Implemented)
}
}
+1 -1
View File
@@ -638,7 +638,7 @@ mod decl {
}
.into_ref(vm);
state.grouper = Some(grouper.downgrade());
state.grouper = Some(grouper.downgrade(None, vm).unwrap());
Ok(PyIterReturn::Return(
(state.current_key.as_ref().unwrap().clone(), grouper).into_pyobject(vm),
))
+2 -14
View File
@@ -960,21 +960,9 @@ pub(crate) mod _struct {
#[pyfunction]
fn _clearcache() {}
#[pyattr(name = "error")]
#[pyattr(name = "error", once)]
fn error_type(vm: &VirtualMachine) -> PyTypeRef {
rustpython_common::static_cell! {
static STRUCT_ERROR: PyTypeRef;
}
STRUCT_ERROR
.get_or_init(|| {
vm.ctx.new_class(
Some("struct"),
"error",
&vm.ctx.exceptions.exception_type,
Default::default(),
)
})
.clone()
vm.ctx.new_exception_type("struct", "error", None)
}
fn new_struct_error(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef {
+21 -7
View File
@@ -8,7 +8,8 @@ pub(crate) use _weakref::make_module;
#[pymodule]
mod _weakref {
use crate::{builtins::PyTypeRef, PyObjectRef, VirtualMachine};
use crate::builtins::{PyDictRef, PyTypeRef, PyWeak};
use crate::{PyObjectRef, PyResult, VirtualMachine};
#[pyattr(name = "ref")]
fn ref_(vm: &VirtualMachine) -> PyTypeRef {
@@ -33,17 +34,30 @@ mod _weakref {
#[pyfunction]
fn getweakrefcount(obj: PyObjectRef) -> usize {
obj.weak_count()
obj.weak_count().unwrap_or(0)
}
#[pyfunction]
fn getweakrefs(_obj: PyObjectRef) -> Vec<PyObjectRef> {
// TODO: implement this, may require a different gc
Vec::new()
fn getweakrefs(obj: PyObjectRef) -> Vec<PyObjectRef> {
match obj.get_weak_references() {
Some(v) => v.into_iter().map(|weak| weak.into_object()).collect(),
None => vec![],
}
}
#[pyfunction]
fn _remove_dead_weakref(_obj: PyObjectRef, _key: PyObjectRef) {
// TODO
fn _remove_dead_weakref(
dict: PyDictRef,
key: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
dict._as_dict_inner()
.delete_if(vm, &key, |wr| {
let wr = wr
.payload::<PyWeak>()
.ok_or_else(|| vm.new_type_error("not a weakref".to_owned()))?;
Ok(wr.is_dead())
})
.map(drop)
}
}
+2
View File
@@ -150,6 +150,8 @@ pub(crate) type DescrSetFunc =
pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult;
pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>;
pub use crate::builtins::object::{generic_getattr, generic_setattr};
fn as_mapping_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods {
macro_rules! then_some_closure {
($cond:expr, $closure:expr) => {
+3 -3
View File
@@ -85,11 +85,12 @@ pub struct TypeZoo {
impl TypeZoo {
pub(crate) fn init() -> Self {
let (type_type, object_type) = crate::pyobjectrc::init_type_hierarchy();
let (type_type, object_type, weakref_type) = crate::pyobjectrc::init_type_hierarchy();
Self {
// the order matters for type, object and int
// the order matters for type, object, weakref, and int
type_type: pytype::PyType::init_manually(type_type).clone(),
object_type: object::PyBaseObject::init_manually(object_type).clone(),
weakref_type: weakref::PyWeak::init_manually(weakref_type).clone(),
int_type: int::PyInt::init_bare_type().clone(),
// types exposed as builtins
@@ -162,7 +163,6 @@ impl TypeZoo {
traceback_type: traceback::PyTraceback::init_bare_type().clone(),
tuple_iterator_type: tuple::PyTupleIterator::init_bare_type().clone(),
weakproxy_type: weakproxy::PyWeakProxy::init_bare_type().clone(),
weakref_type: weakref::PyWeak::init_bare_type().clone(),
method_descriptor_type: builtinfunc::PyBuiltinMethod::init_bare_type().clone(),
none_type: singletons::PyNone::init_bare_type().clone(),
not_implemented_type: singletons::PyNotImplemented::init_bare_type().clone(),
+37 -37
View File
@@ -9,7 +9,7 @@ use crate::compile::{self, CompileError, CompileErrorType, CompileOpts};
use crate::{
builtins::{
code::{self, PyCode},
module, object,
object,
pystr::IntoPyStrRef,
tuple::{IntoPyTuple, PyTuple, PyTupleRef, PyTupleTyped},
PyBaseException, PyBaseExceptionRef, PyDictRef, PyInt, PyIntRef, PyList, PyModule, PyStr,
@@ -26,9 +26,8 @@ use crate::{
scope::Scope,
signal, stdlib,
types::PyComparisonOp,
IdProtocol, ItemProtocol, PyArithmeticValue, PyContext, PyGenericObject, PyLease, PyMethod,
PyObject, PyObjectRef, PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject,
TypeProtocol,
IdProtocol, ItemProtocol, PyArithmeticValue, PyContext, PyLease, PyMethod, PyObject,
PyObjectRef, PyObjectWrap, PyRef, PyRefExact, PyResult, PyValue, TryFromObject, TypeProtocol,
};
use crossbeam_utils::atomic::AtomicCell;
use num_traits::{Signed, ToPrimitive};
@@ -46,8 +45,8 @@ use std::fmt;
///
/// To construct this, please refer to the [`Interpreter`](Interpreter)
pub struct VirtualMachine {
pub builtins: PyObjectRef,
pub sys_module: PyObjectRef,
pub builtins: PyRef<PyModule>,
pub sys_module: PyRef<PyModule>,
pub ctx: PyRc<PyContext>,
pub frames: RefCell<Vec<FrameRef>>,
pub wasm_id: Option<String>,
@@ -248,14 +247,17 @@ impl VirtualMachine {
// make a new module without access to the vm; doesn't
// set __spec__, __loader__, etc. attributes
let new_module =
|dict| PyGenericObject::new(PyModule {}, ctx.types.module_type.clone(), Some(dict));
let new_module = || {
PyRef::new_ref(
PyModule {},
ctx.types.module_type.clone(),
Some(ctx.new_dict()),
)
};
// Hard-core modules:
let builtins_dict = ctx.new_dict();
let builtins = new_module(builtins_dict.clone());
let sysmod_dict = ctx.new_dict();
let sysmod = new_module(sysmod_dict.clone());
let builtins = new_module();
let sys_module = new_module();
let import_func = ctx.none();
let profile_func = RefCell::new(ctx.none());
@@ -279,7 +281,7 @@ impl VirtualMachine {
let mut vm = VirtualMachine {
builtins,
sys_module: sysmod,
sys_module,
ctx: PyRc::new(ctx),
frames: RefCell::new(vec![]),
wasm_id: None,
@@ -309,18 +311,14 @@ impl VirtualMachine {
let frozen = frozen::map_frozen(&vm, frozen::get_module_inits()).collect();
PyRc::get_mut(&mut vm.state).unwrap().frozen = frozen;
module::init_module_dict(
&vm,
&builtins_dict,
vm.builtins.init_module_dict(
vm.ctx.new_str(ascii!("builtins")).into(),
vm.ctx.none(),
);
module::init_module_dict(
&vm,
&sysmod_dict,
vm.ctx.new_str(ascii!("sys")).into(),
vm.ctx.none(),
);
vm.sys_module
.init_module_dict(vm.ctx.new_str(ascii!("sys")).into(), vm.ctx.none(), &vm);
vm
}
@@ -331,8 +329,8 @@ impl VirtualMachine {
panic!("Double Initialize Error");
}
stdlib::builtins::make_module(self, self.builtins.clone());
stdlib::sys::init_module(self, &self.sys_module, &self.builtins);
stdlib::builtins::make_module(self, self.builtins.clone().into());
stdlib::sys::init_module(self, self.sys_module.as_ref(), self.builtins.as_ref());
let mut inner_init = || -> PyResult<()> {
#[cfg(not(target_arch = "wasm32"))]
@@ -511,8 +509,7 @@ impl VirtualMachine {
}
pub fn run_code_obj(&self, code: PyRef<PyCode>, scope: Scope) -> PyResult {
let frame =
Frame::new(code, scope, self.builtins.dict().unwrap(), &[], self).into_ref(self);
let frame = Frame::new(code, scope, self.builtins.dict(), &[], self).into_ref(self);
self.run_frame_full(frame)
}
@@ -624,14 +621,14 @@ impl VirtualMachine {
}
pub fn new_module(&self, name: &str, dict: PyDictRef, doc: Option<&str>) -> PyObjectRef {
module::init_module_dict(
self,
&dict,
let module = PyRef::new_ref(PyModule {}, self.ctx.types.module_type.clone(), Some(dict));
module.init_module_dict(
self.new_pyobj(name.to_owned()),
doc.map(|doc| self.new_pyobj(doc.to_owned()))
.unwrap_or_else(|| self.ctx.none()),
self,
);
PyGenericObject::new(PyModule {}, self.ctx.types.module_type.clone(), Some(dict))
module.into_object()
}
/// Instantiate an exception with arguments.
@@ -1748,13 +1745,16 @@ impl VirtualMachine {
/// Checks that the multiplication is able to be performed. On Ok returns the
/// index as a usize for sequences to be able to use immediately.
pub fn check_repeat_or_memory_error(&self, length: usize, n: isize) -> PyResult<usize> {
let n = n.to_usize().unwrap_or(0);
if n > 0 && length > stdlib::sys::MAXSIZE as usize / n {
// Empty message is currently used in CPython.
Err(self.new_memory_error("".to_owned()))
pub fn check_repeat_or_overflow_error(&self, length: usize, n: isize) -> PyResult<usize> {
if n <= 0 {
Ok(0)
} else {
Ok(n)
let n = n as usize;
if length > stdlib::sys::MAXSIZE as usize / n {
Err(self.new_overflow_error("repeated value are too long".to_owned()))
} else {
Ok(n)
}
}
}
@@ -1898,7 +1898,7 @@ impl VirtualMachine {
attr_value: impl Into<PyObjectRef>,
) -> PyResult<()> {
let val = attr_value.into();
object::setattr(module, attr_name.into_pystr_ref(self), Some(val), self)
object::generic_setattr(module, attr_name.into_pystr_ref(self), Some(val), self)
}
}
@@ -1939,7 +1939,7 @@ impl<'vm> ReprGuard<'vm> {
let mut guards = vm.repr_guards.borrow_mut();
// Should this be a flag on the obj itself? putting it in a global variable for now until it
// decided the form of the PyGenericObject. https://github.com/RustPython/RustPython/issues/371
// decided the form of PyObject. https://github.com/RustPython/RustPython/issues/371
let id = obj.get_id();
if guards.contains(&id) {
return None;
+3 -3
View File
@@ -10,7 +10,7 @@ mod _browser {
builtins::{PyDictRef, PyStrRef},
function::{ArgCallable, IntoPyObject, OptionalArg},
import::import_file,
PyClassImpl, PyGenericObject, PyObjectRef, PyResult, PyValue, VirtualMachine,
PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine,
};
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::JsFuture;
@@ -179,8 +179,8 @@ mod _browser {
}
#[pyattr]
fn document(vm: &VirtualMachine) -> PyObjectRef {
PyGenericObject::new(
fn document(vm: &VirtualMachine) -> PyRef<Document> {
PyRef::new_ref(
Document {
doc: window().document().expect("Document missing from window"),
},
+1 -1
View File
@@ -84,7 +84,7 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
let wasm_vm = WASMVirtualMachine {
id: wasm_id.clone(),
};
let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap();
let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap().unwrap();
let closure = move |args: Option<Box<[JsValue]>>,
kwargs: Option<Object>|
+6 -6
View File
@@ -123,12 +123,12 @@ mod _js {
}
#[pymethod]
fn new_closure(&self, obj: PyObjectRef, vm: &VirtualMachine) -> JsClosure {
fn new_closure(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<JsClosure> {
JsClosure::new(obj, false, vm)
}
#[pymethod]
fn new_closure_once(&self, obj: PyObjectRef, vm: &VirtualMachine) -> JsClosure {
fn new_closure_once(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<JsClosure> {
JsClosure::new(obj, true, vm)
}
@@ -306,11 +306,11 @@ mod _js {
#[pyimpl]
impl JsClosure {
fn new(obj: PyObjectRef, once: bool, vm: &VirtualMachine) -> Self {
fn new(obj: PyObjectRef, once: bool, vm: &VirtualMachine) -> PyResult<Self> {
let wasm_vm = WASMVirtualMachine {
id: vm.wasm_id.clone().unwrap(),
};
let weak_py_obj = wasm_vm.push_held_rc(obj).unwrap();
let weak_py_obj = wasm_vm.push_held_rc(obj).unwrap()?;
let f = move |this: JsValue, args: Box<[JsValue]>| {
let py_obj = match wasm_vm.assert_valid() {
Ok(_) => weak_py_obj
@@ -337,11 +337,11 @@ mod _js {
Closure::once(Box::new(f))
};
let wrapped = PyJsValue::new(wrap_closure(closure.as_ref())).into_ref(vm);
JsClosure {
Ok(JsClosure {
closure: Some((closure, wrapped)).into(),
destroyed: false.into(),
detached: false.into(),
}
})
}
#[pyproperty]
+5 -5
View File
@@ -8,7 +8,7 @@ use wasm_bindgen::prelude::*;
use rustpython_vm::compile::{self, Mode};
use rustpython_vm::scope::Scope;
use rustpython_vm::{InitParameter, Interpreter, PySettings, VirtualMachine};
use rustpython_vm::{ItemProtocol, PyObjectRef, PyObjectWeak, PyValue};
use rustpython_vm::{ItemProtocol, PyObjectRef, PyObjectWeak, PyResult, PyValue};
use crate::browser_module::setup_browser_module;
use crate::convert::{self, PyResultExt};
@@ -195,11 +195,11 @@ impl WASMVirtualMachine {
STORED_VMS.with(|cell| cell.borrow().contains_key(&self.id))
}
pub(crate) fn push_held_rc(&self, obj: PyObjectRef) -> Result<PyObjectWeak, JsValue> {
self.with(|stored_vm| {
let weak = obj.downgrade();
pub(crate) fn push_held_rc(&self, obj: PyObjectRef) -> Result<PyResult<PyObjectWeak>, JsValue> {
self.with_vm(|vm, stored_vm| {
let weak = obj.downgrade(None, vm)?;
stored_vm.held_objects.borrow_mut().push(obj);
weak
Ok(weak)
})
}