Skip to content

Commit 6e1ca79

Browse files
Merge remote-tracking branch 'origin/main' into c-api
2 parents 7a19dcf + 0c9ed36 commit 6e1ca79

7 files changed

Lines changed: 153 additions & 55 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,7 @@ jobs:
310310
extra_test_args: [] # TODO: Enable '-u all'
311311
env_polluting_tests:
312312
- test_set
313-
skips:
314-
- test_rlcompleter
315-
- test_pathlib # panic by surrogate chars
316-
- test_posixpath # OSError: (22, 'The filename, directory name, or volume label syntax is incorrect. (os error 123)')
317-
- test_venv # couple of failing tests
313+
skips: []
318314
timeout: 50
319315
fail-fast: false
320316
steps:

Lib/test/test_venv.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ def pip_cmd_checker(cmd, **kwargs):
266266
with patch('venv.subprocess.check_output', pip_cmd_checker):
267267
builder.upgrade_dependencies(fake_context)
268268

269+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
269270
@requireVenvCreate
270271
def test_prefixes(self):
271272
"""
@@ -285,6 +286,7 @@ def test_prefixes(self):
285286
self.assertEqual(pathlib.Path(out.strip().decode()),
286287
pathlib.Path(expected), prefix)
287288

289+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
288290
@requireVenvCreate
289291
def test_sysconfig(self):
290292
"""
@@ -318,6 +320,7 @@ def test_sysconfig(self):
318320
out, err = check_output(cmd, encoding='utf-8')
319321
self.assertEqual(out.strip(), expected, err)
320322

323+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
321324
@requireVenvCreate
322325
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
323326
def test_sysconfig_symlinks(self):
@@ -458,6 +461,7 @@ def test_isolation(self):
458461
data = self.get_text_file_contents('pyvenv.cfg')
459462
self.assertIn('include-system-site-packages = %s\n' % s, data)
460463

464+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
461465
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
462466
def test_symlinking(self):
463467
"""
@@ -482,6 +486,7 @@ def test_symlinking(self):
482486
# run the test, the pyvenv.cfg in the venv created in the test will
483487
# point to the venv being used to run the test, and we lose the link
484488
# to the source build - so Python can't initialise properly.
489+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
485490
@requireVenvCreate
486491
def test_executable(self):
487492
"""
@@ -494,6 +499,7 @@ def test_executable(self):
494499
'import sys; print(sys.executable)'])
495500
self.assertEqual(out.strip(), envpy.encode())
496501

502+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
497503
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
498504
def test_executable_symlinks(self):
499505
"""
@@ -562,6 +568,7 @@ def test_special_chars_csh(self):
562568
self.assertEndsWith(lines[1], env_name.encode())
563569

564570
# gh-124651: test quoted strings on Windows
571+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
565572
@unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
566573
def test_special_chars_windows(self):
567574
"""
@@ -585,6 +592,7 @@ def test_special_chars_windows(self):
585592
self.assertTrue(env_name.encode() in lines[0])
586593
self.assertEndsWith(lines[1], env_name.encode())
587594

595+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
588596
@unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
589597
def test_unicode_in_batch_file(self):
590598
"""
@@ -616,6 +624,7 @@ def test_failed_symlink(self):
616624
filepath_regex = r"'[A-Z]:\\\\(?:[^\\\\]+\\\\)*[^\\\\]+'"
617625
self.assertRegex(err, rf"Unable to symlink {filepath_regex} to {filepath_regex}")
618626

627+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
619628
@requireVenvCreate
620629
def test_multiprocessing(self):
621630
"""
@@ -635,6 +644,7 @@ def test_multiprocessing(self):
635644
'pool.terminate()'])
636645
self.assertEqual(out.strip(), "python".encode())
637646

647+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
638648
@requireVenvCreate
639649
def test_multiprocessing_recursion(self):
640650
"""
@@ -892,6 +902,7 @@ def test_venv_same_path(self):
892902
self.assertFalse(same_path(path1, path2))
893903

894904
# gh-126084: venvwlauncher should run pythonw, not python
905+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
895906
@requireVenvCreate
896907
@unittest.skipUnless(os.name == 'nt', 'only relevant on Windows')
897908
def test_venvwlauncher(self):
@@ -926,11 +937,13 @@ def assert_pip_not_installed(self):
926937
self.assertEqual(out.strip(), "OK")
927938

928939

940+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
929941
def test_no_pip_by_default(self):
930942
rmtree(self.env_dir)
931943
self.run_with_capture(venv.create, self.env_dir)
932944
self.assert_pip_not_installed()
933945

946+
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
934947
def test_explicit_no_pip(self):
935948
rmtree(self.env_dir)
936949
self.run_with_capture(venv.create, self.env_dir, with_pip=False)

crates/capi/capi_todo.md

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,6 @@ RustPython C API target: `crates/capi/src/abstract_.rs`
1111
- `PyIter_Send`
1212
- `PyObject_GetIter`
1313
- `PyObject_Size`
14-
- `PySequence_Check`
15-
- `PySequence_Concat`
16-
- `PySequence_Count`
17-
- `PySequence_DelItem`
18-
- `PySequence_DelSlice`
19-
- `PySequence_GetItem`
20-
- `PySequence_GetSlice`
21-
- `PySequence_InPlaceConcat`
22-
- `PySequence_InPlaceRepeat`
23-
- `PySequence_Index`
24-
- `PySequence_List`
25-
- `PySequence_Repeat`
26-
- `PySequence_SetItem`
27-
- `PySequence_SetSlice`
28-
- `PySequence_Size`
29-
- `PySequence_Tuple`
30-
31-
## `bytearrayobject.h`
32-
33-
RustPython C API target: `crates/capi/src/bytearrayobject.rs` (not present yet)
34-
35-
- `PyByteArray_AsString`
36-
- `PyByteArray_Check`
37-
- `PyByteArray_FromObject`
38-
- `PyByteArray_FromStringAndSize`
39-
- `PyByteArray_Resize`
40-
- `PyByteArray_Size`
4114

4215
## `descrobject.h`
4316

@@ -143,10 +116,6 @@ RustPython C API target: `crates/capi/src/pystate.rs`
143116
- `PyInterpreterState_Get`
144117
- `PyInterpreterState_GetID`
145118

146-
## `setobject.h`
147-
148-
RustPython C API target: `crates/capi/src/setobject.rs`
149-
150119
## `sliceobject.h`
151120

152121
RustPython C API target: `crates/capi/src/sliceobject.rs` (not present yet)
@@ -170,13 +139,3 @@ RustPython C API target: `crates/capi/src/warnings.rs` (not present yet)
170139

171140
- `PyErr_WarnEx`
172141
- `PyErr_WarnExplicit`
173-
174-
## `weakrefobject.h`
175-
176-
RustPython C API target: `crates/capi/src/weakrefobject.rs` (not present yet)
177-
178-
- `PyWeakref_CheckProxy`
179-
- `PyWeakref_CheckRef`
180-
- `PyWeakref_GetRef`
181-
- `PyWeakref_NewProxy`
182-
- `PyWeakref_NewRef`

crates/capi/src/abstract_.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use crate::{PyObject, pystate::with_vm};
22
use alloc::slice;
33
use core::ffi::c_int;
4+
pub use mapping::*;
5+
pub use number::*;
46
use rustpython_vm::builtins::{PyDict, PyStr, PyTuple};
57
use rustpython_vm::function::{FuncArgs, KwArgs, PosArgs};
68
use rustpython_vm::{AsObject, Py, PyObjectRef, PyResult, VirtualMachine};
79

810
mod mapping;
911
mod number;
10-
pub use mapping::*;
11-
pub use number::*;
1212

1313
const PY_VECTORCALL_ARGUMENTS_OFFSET: usize = 1usize << (usize::BITS as usize - 1);
1414

crates/capi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub mod traceback;
3333
pub mod tupleobject;
3434
pub mod unicodeobject;
3535
mod util;
36+
pub mod weakrefobject;
3637

3738
/// Get main interpreter of this process. Will be None if it has not been initialized yet.
3839
pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> {

crates/capi/src/weakrefobject.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use crate::PyObject;
2+
use crate::object::define_py_check;
3+
use crate::pystate::with_vm;
4+
use core::ffi::c_int;
5+
use rustpython_vm::builtins::{PyWeak, PyWeakProxy};
6+
7+
define_py_check!(fn PyWeakref_CheckProxy, types.weakproxy_type);
8+
define_py_check!(fn PyWeakref_CheckRef, types.weakref_type);
9+
10+
#[unsafe(no_mangle)]
11+
pub unsafe extern "C" fn PyWeakref_GetRef(
12+
reference: *mut PyObject,
13+
result: *mut *mut PyObject,
14+
) -> c_int {
15+
with_vm(|vm| {
16+
unsafe {
17+
*result = core::ptr::null_mut();
18+
}
19+
20+
let reference = unsafe { &*reference };
21+
let upgraded = if let Some(weak) = reference.downcast_ref::<PyWeak>() {
22+
weak.upgrade()
23+
} else if let Some(proxy) = reference.downcast_ref::<PyWeakProxy>() {
24+
proxy.get_weak().upgrade()
25+
} else {
26+
return Err(vm.new_type_error("expected a weakref"));
27+
};
28+
29+
if let Some(obj) = upgraded {
30+
unsafe {
31+
*result = obj.into_raw().as_ptr();
32+
}
33+
Ok(true)
34+
} else {
35+
Ok(false)
36+
}
37+
})
38+
}
39+
40+
#[unsafe(no_mangle)]
41+
pub unsafe extern "C" fn PyWeakref_NewProxy(
42+
ob: *mut PyObject,
43+
callback: *mut PyObject,
44+
) -> *mut PyObject {
45+
with_vm(|vm| {
46+
let ob = unsafe { &*ob };
47+
let callback = unsafe { callback.as_ref() }
48+
.filter(|callback| !vm.is_none(callback))
49+
.map(ToOwned::to_owned);
50+
PyWeakProxy::new_weakproxy(ob, callback, vm)
51+
})
52+
}
53+
54+
#[unsafe(no_mangle)]
55+
pub unsafe extern "C" fn PyWeakref_NewRef(
56+
ob: *mut PyObject,
57+
callback: *mut PyObject,
58+
) -> *mut PyObject {
59+
with_vm(|vm| {
60+
let ob = unsafe { &*ob };
61+
let callback = unsafe { callback.as_ref() }
62+
.filter(|callback| !vm.is_none(callback))
63+
.map(ToOwned::to_owned);
64+
ob.downgrade(callback, vm)
65+
})
66+
}
67+
68+
#[cfg(false)]
69+
mod tests {
70+
use pyo3::prelude::*;
71+
use pyo3::types::PyAnyMethods;
72+
use pyo3::types::{PyInt, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference};
73+
74+
#[test]
75+
fn check_ref_and_proxy() {
76+
Python::attach(|py| {
77+
let object_ty = py.get_type::<PyInt>();
78+
79+
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
80+
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
81+
82+
assert!(weak_ref.is_instance_of::<PyWeakrefReference>());
83+
assert!(weak_proxy.is_instance_of::<PyWeakrefProxy>());
84+
});
85+
}
86+
87+
#[test]
88+
fn new_ref_and_get_ref() {
89+
Python::attach(|py| {
90+
let object_ty = py.get_type::<PyInt>();
91+
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
92+
93+
assert!(weak_ref.upgrade().is_some_and(|obj| obj.is(&object_ty)));
94+
});
95+
}
96+
97+
#[test]
98+
fn new_proxy() {
99+
Python::attach(|py| {
100+
let object_ty = py.get_type::<PyInt>();
101+
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
102+
103+
assert!(weak_proxy.upgrade().is_some_and(|obj| obj.is(&object_ty)));
104+
});
105+
}
106+
}

crates/vm/src/builtins/weakproxy.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@ impl Constructor for PyWeakProxy {
4242
Self::Args { referent, callback }: Self::Args,
4343
vm: &VirtualMachine,
4444
) -> PyResult<Self> {
45+
let weak = Self::new_weak(referent.as_ref(), callback.into_option(), vm)?;
46+
// TODO: PyWeakProxy should use the same payload as PyWeak
47+
Ok(Self { weak })
48+
}
49+
}
50+
51+
crate::common::static_cell! {
52+
static WEAK_SUBCLASS: PyTypeRef;
53+
}
54+
55+
impl PyWeakProxy {
56+
fn new_weak(
57+
referent: &PyObject,
58+
callback: Option<PyObjectRef>,
59+
vm: &VirtualMachine,
60+
) -> PyResult<PyRef<PyWeak>> {
4561
// using an internal subclass as the class prevents us from getting the generic weakref,
4662
// which would mess up the weakref count
4763
let weak_cls = WEAK_SUBCLASS.get_or_init(|| {
@@ -52,15 +68,22 @@ impl Constructor for PyWeakProxy {
5268
super::PyWeak::make_slots(),
5369
)
5470
});
55-
// TODO: PyWeakProxy should use the same payload as PyWeak
56-
Ok(Self {
57-
weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?,
58-
})
71+
referent.downgrade_with_typ(callback, weak_cls.clone(), vm)
5972
}
60-
}
6173

62-
crate::common::static_cell! {
63-
static WEAK_SUBCLASS: PyTypeRef;
74+
pub fn new_weakproxy(
75+
referent: &PyObject,
76+
callback: Option<PyObjectRef>,
77+
vm: &VirtualMachine,
78+
) -> PyResult<PyRef<Self>> {
79+
let weak = Self::new_weak(referent, callback, vm)?;
80+
Ok(Self { weak }.into_ref(&vm.ctx))
81+
}
82+
83+
#[must_use]
84+
pub fn get_weak(&self) -> &PyRef<PyWeak> {
85+
&self.weak
86+
}
6487
}
6588

6689
#[pyclass(with(

0 commit comments

Comments
 (0)