8000 Init copyslot by youknowone · Pull Request #6515 · RustPython/RustPython · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Lib/test/test_codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3189,7 +3189,6 @@ def test_readline(self):
sout = reader.readline()
self.assertEqual(sout, b"\x80")

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_buffer_api_usage(self):
# We check all the transform codecs accept memoryview input
# for encoding and decoding
Expand Down
8 changes: 0 additions & 8 deletions Lib/test/test_memoryio.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,6 @@ class CBytesIOTest(PyBytesIOTest):
ioclass = io.BytesIO
UnsupportedOperation = io.UnsupportedOperation

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bytes_array(self):
super().test_bytes_array()

Expand All @@ -739,8 +737,6 @@ def test_flags(self):
def test_getbuffer(self):
super().test_getbuffer()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_init(self):
super().test_init()

Expand Down Expand Up @@ -770,8 +766,6 @@ def test_relative_seek(self):
def test_seek(self):
super().test_seek()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_subclassing(self):
super().test_subclassing()

Expand Down Expand Up @@ -884,8 +878,6 @@ def test_detach(self):
def test_flags(self):
super().test_flags()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_init(self):
super().test_init()

Expand Down
15 changes: 0 additions & 15 deletions Lib/test/test_pickle.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,6 @@ def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_buffers_error(self): # TODO(RUSTPYTHON): Remove this test when it passes
Expand Down Expand Up @@ -220,11 +215,6 @@ def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()


class InMemoryPickleTests(AbstractPickleTests, AbstractUnpickleTests,
BigmemPickleTests, unittest.TestCase):
Expand Down Expand Up @@ -309,11 +299,6 @@ def test_in_band_buffers(self): # TODO(RUSTPYTHON): Remove this test when it pas
def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()

class PersistentPicklerUnpicklerMixin(object):

def dumps(self, arg, proto=None):
Expand Down
5 changes: 0 additions & 5 deletions Lib/test/test_pickletools.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,6 @@ def test_oob_buffers(self): # TODO(RUSTPYTHON): Remove this test when it passes
def test_oob_buffers_writable_to_readonly(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_oob_buffers_writable_to_readonly()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_optional_frames(self): # TODO(RUSTPYTHON): Remove this test when it passes
return super().test_optional_frames()

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_py_methods(self): # TODO(RUSTPYTHON): Remove this test when it passes
Expand Down
28 changes: 0 additions & 28 deletions Lib/test/test_urllib2.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,6 @@ def test_raise(self):
self.assertRaises(urllib.error.URLError, o.open, req)
self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_http_error(self):
# XXX http_error_default
# http errors are a special case
Expand All @@ -666,8 +664,6 @@ def test_http_error(self):
self.assertEqual((handler, method_name), got[:2])
self.assertEqual(args, got[2])

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_processors(self):
# *_request / *_response methods get called appropriately
o = OpenerDirector()
Expand Down Expand Up @@ -874,8 +870,6 @@ def test_file(self):
self.assertEqual(req.type, "ftp")
self.assertEqual(req.type == "ftp", ftp)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_http(self):

h = urllib.request.AbstractHTTPHandler()
Expand Down Expand Up @@ -1136,8 +1130,6 @@ def test_fixpath_in_weirdurls(self):
self.assertEqual(newreq.host, 'www.python.org')
self.assertEqual(newreq.selector, '')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_errors(self):
h = urllib.request.HTTPErrorProcessor()
o = h.parent = MockOpener()
Expand All @@ -1163,8 +1155,6 @@ def test_errors(self):
self.assertEqual(o.proto, "http") # o.error called
self.assertEqual(o.args, (req, r, 502, "Bad gateway", {}))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cookies(self):
cj = MockCookieJar()
h = urllib.request.HTTPCookieProcessor(cj)
Expand Down Expand Up @@ -1291,8 +1281,6 @@ def test_relative_redirect(self):
MockHeaders({"location": valid_url}))
self.assertEqual(o.req.get_full_url(), valid_url)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cookie_redirect(self):
# cookies shouldn't leak into redirected requests
from http.cookiejar import CookieJar
Expand All @@ -1308,8 +1296,6 @@ def test_cookie_redirect(self):
o.open("http://www.example.com/")
self.assertFalse(hh.req.has_header("Cookie"))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_redirect_fragment(self):
redirected_url = 'http://www.example.com/index.html#OK\r\n\r\n'
hh = MockHTTPHandler(302, 'Location: ' + redirected_url)
Expand Down Expand Up @@ -1374,8 +1360,6 @@ def http_open(self, req):
request = handler.last_buf
self.assertTrue(request.startswith(expected), repr(request))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy(self):
u = "proxy.example.com:3128"
for d in dict(http=u), dict(HTTP=u):
Expand Down Expand Up @@ -1420,8 +1404,6 @@ def test_proxy_no_proxy_all(self):
self.assertEqual(req.host, "www.python.org")
del os.environ['no_proxy']

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy_https(self):
o = OpenerDirector()
ph = urllib.request.ProxyHandler(dict(https="proxy.example.com:3128"))
Expand Down Expand Up @@ -1509,8 +1491,6 @@ def check_basic_auth(self, headers, realm):
"http://acme.example.com/protected",
"http://acme.example.com/protected")

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_auth(self):
realm = "realm2@example.com"
realm2 = "realm2@example.com"
Expand Down Expand Up @@ -1556,8 +1536,6 @@ def test_basic_auth(self):
for challenge in challenges]
self.check_basic_auth(headers, realm)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_proxy_basic_auth(self):
opener = OpenerDirector()
ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))
Expand All @@ -1575,8 +1553,6 @@ def test_proxy_basic_auth(self):
"proxy.example.com:3128",
)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_and_digest_auth_handlers(self):
# HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
# response (http://python.org/sf/1479302), where it should instead
Expand Down Expand Up @@ -1684,8 +1660,6 @@ def _test_basic_auth(self, opener, auth_handler, auth_header,
self.assertEqual(len(http_handler.requests), 1)
self.assertFalse(http_handler.requests[0].has_header(auth_header))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_prior_auth_auto_send(self):
# Assume already authenticated if is_authenticated=True
# for APIs like Github that don't return 401
Expand Down Expand Up @@ -1713,8 +1687,6 @@ def test_basic_prior_auth_auto_send(self):
# expect request to be sent with auth header
self.assertTrue(http_handler.has_auth_header)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_basic_prior_auth_send_after_first_success(self):
# Auto send auth header after authentication is successful once

Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_zipfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1746,8 +1746,6 @@ def test_empty_file_raises_BadZipFile(self):
fp.write("short file")
self.assertRaises(zipfile.BadZipFile, zipfile.ZipFile, TESTFN)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_negative_central_directory_offset_raises_BadZipFile(self):
# Zip file containing an empty EOCD record
buffer = bytearray(b'PK\x05\x06' + b'\0'*18)
Expand Down
23 changes: 19 additions & 4 deletions crates/derive-impl/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ pub(crate) fn impl_pyclass_impl(attr: PunctuatedNestedMeta, item: Item) -> Resul
const METHOD_DEFS: &'static [::rustpython_vm::function::PyMethodDef] = &#method_defs;

fn extend_slots(slots: &mut ::rustpython_vm::types::PyTypeSlots) {
#impl_ty::__extend_slots(slots);
#with_slots
#impl_ty::__extend_slots(slots);
}
}
}
Expand Down Expand Up @@ -1672,9 +1672,24 @@ fn extract_impl_attrs(attr: PunctuatedNestedMeta, item: &Ident) -> Result<Extrac
#extend_class(ctx, class);
});
with_method_defs.push(method_defs);
with_slots.push(quote_spanned! { item_span =>
#extend_slots(slots);
});
// For Initializer and Constructor traits, directly set the slot
// instead of calling __extend_slots. This ensures that the trait
// impl's override (e.g., slot_init in impl Initializer) is used,
// not the trait's default implementation.
let slot_code = if path.is_ident("Initializer") {
quote_spanned! { item_span =>
slots.init.store(Some(<Self as ::rustpython_vm::types::Initializer>::slot_init as _));
}
} else if path.is_ident("Constructor") {
quote_spanned! { item_span =>
slots.new.store(Some(<Self as ::rustpython_vm::types::Constructor>::slot_new as _));
}
} else {
quote_spanned! { item_span =>
#extend_slots(slots);
}
};
with_slots.push(slot_code);
}
} else if path.is_ident("flags") {
for meta in nested {
Expand Down
49 changes: 24 additions & 25 deletions crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,44 +120,36 @@ impl Initializer for PyBaseObject {

// object_init: excess_args validation
fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
if args.is_empty() {
return Ok(());
}

let typ = zelf.class();
let object_type = &vm.ctx.types.object_type;

let typ_init = typ.slots.init.load().map(|f| f as usize);
let object_init = object_type.slots.init.load().map(|f| f as usize);
let typ_new = typ.slots.new.load().map(|f| f as usize);
let object_new = object_type.slots.new.load().map(|f| f as usize);

// For heap types (Python classes), check if __new__ is defined anywhere in MRO
// (before object) because heap types always have slots.new = new_wrapper via MRO
let is_heap_type = typ
.slots
.flags
.contains(crate::types::PyTypeFlags::HEAPTYPE);
let new_overridden = if is_heap_type {
// Check if __new__ is defined in any base class (excluding object)
let new_id = identifier!(vm, __new__);
typ.mro_collect()
.into_iter()
.take_while(|t| !std::ptr::eq(t.as_ref(), *object_type))
.any(|t| t.attributes.read().contains_key(new_id))
} else {
// For built-in types, use slot comparison
typ_new != object_new
};

// If both __init__ and __new__ are overridden, allow excess args
if typ_init != object_init && new_overridden {
return Ok(());
// if (type->tp_init != object_init) → first error
if typ_init != object_init {
return Err(vm.new_type_error(
"object.__init__() takes exactly one argument (the instance to initialize)"
.to_owned(),
));
}

// Otherwise, reject excess args
if !args.is_empty() {
let typ_new = typ.slots.new.load().map(|f| f as usize);
let object_new = object_type.slots.new.load().map(|f| f as usize);

// if (type->tp_new == object_new) → second error
if typ_new == object_new {
return Err(vm.new_type_error(format!(
"{}.__init__() takes exactly one argument (the instance to initialize)",
typ.name()
)));
}

// Both conditions false → OK (e.g., tuple, dict with custom __new__)
Ok(())
}

Expand Down Expand Up @@ -591,6 +583,13 @@ pub fn object_set_dict(obj: PyObjectRef, dict: PyDictRef, vm: &VirtualMachine) -
}

pub fn init(ctx: &Context) {
// Manually set init slot - derive macro doesn't generate extend_slots
// for trait impl that overrides #[pyslot] method
ctx.types
.object_type
.slots
.init
.store(Some(<PyBaseObject as Initializer>::slot_init));
PyBaseObject::extend_class(ctx, ctx.types.object_type);
}

Expand Down
Loading
Loading
0