Skip to content

Commit 5f1d5d2

Browse files
morealclaude
andauthored
Cleanup some direct magic method definitions (#7441)
* Migrate direct __repr__ definitions to Representable trait Move legacy #[pymethod] __repr__ to impl Representable for: - PySSLContext, PySSLSocket (ssl.rs) - BufferedReader, BufferedWriter, BufferedRandom (_io.rs) Enable __repr__ guard in derive to prevent future direct definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Migrate direct __del__ definition to Destructor trait for PySocket Move #[pymethod] __del__ to impl Destructor for PySocket. Preserves ResourceWarning emission and close behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove redundant __iter__ pymethods from PyFuture and PyTask Both types already have impl Iterable, so the direct #[pymethod] __iter__ definitions were duplicates. Enable __iter__/__next__ guards in derive to prevent future direct definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Enable derive guards for __get__/__set__/__delete__ descriptor methods All concrete types already use GetDescriptor/SetDescriptor traits. Activate the compile-time guard to prevent future direct definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Enable derive guards for AsMapping/AsSequence slot methods Remove redundant #[pymethod(name = "__len__")] from PyStr (already provided via AsMapping/AsSequence trait impls). Enable compile-time guards for __len__, __contains__, __getitem__, __setitem__, __delitem__ to prevent future direct definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f27490c commit 5f1d5d2

File tree

5 files changed

+67
-71
lines changed

5 files changed

+67
-71
lines changed

crates/derive-impl/src/pyclass.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ where
917917
("__new__", "Constructor"),
918918
("__init__", "Initializer"),
919919
// Representable trait
920-
// ("__repr__", "Representable"),
920+
("__repr__", "Representable"),
921921
// ("__str__", "???"), // allow __str__
922922
// Hashable trait
923923
("__hash__", "Hashable"),
@@ -927,9 +927,9 @@ where
927927
// NOTE: __getattribute__, __setattr__, __delattr__ are intentionally NOT forbidden
928928
// because they need pymethod for subclass override mechanism to work properly.
929929
// GetDescriptor/SetDescriptor traits
930-
// ("__get__", "GetDescriptor"),
931-
// ("__set__", "SetDescriptor"),
932-
// ("__delete__", "SetDescriptor"),
930+
("__get__", "GetDescriptor"),
931+
("__set__", "SetDescriptor"),
932+
("__delete__", "SetDescriptor"),
933933
// AsNumber trait
934934
("__add__", "AsNumber"),
935935
("__radd__", "AsNumber"),
@@ -981,15 +981,15 @@ where
981981
("__index__", "AsNumber"),
982982
("__bool__", "AsNumber"),
983983
// AsSequence trait
984-
// ("__len__", "AsSequence (or AsMapping)"),
985-
// ("__contains__", "AsSequence"),
984+
("__len__", "AsSequence (or AsMapping)"),
985+
("__contains__", "AsSequence"),
986986
// AsMapping trait
987-
// ("__getitem__", "AsMapping (or AsSequence)"),
988-
// ("__setitem__", "AsMapping (or AsSequence)"),
989-
// ("__delitem__", "AsMapping (or AsSequence)"),
990-
// IterNext trait
991-
// ("__iter__", "IterNext"),
992-
// ("__next__", "IterNext"),
987+
("__getitem__", "AsMapping (or AsSequence)"),
988+
("__setitem__", "AsMapping (or AsSequence)"),
989+
("__delitem__", "AsMapping (or AsSequence)"),
990+
// Iterable/IterNext traits
991+
("__iter__", "Iterable"),
992+
("__next__", "IterNext"),
993993
// Comparable trait
994994
("__eq__", "Comparable"),
995995
("__ne__", "Comparable"),

crates/stdlib/src/_asyncio.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -764,11 +764,6 @@ pub(crate) mod _asyncio {
764764
Ok(())
765765
}
766766

767-
#[pymethod]
768-
fn __iter__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyFutureIter> {
769-
Self::__await__(zelf, vm)
770-
}
771-
772767
#[pymethod]
773768
fn __await__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyFutureIter> {
774769
Ok(PyFutureIter {
@@ -1828,13 +1823,6 @@ pub(crate) mod _asyncio {
18281823
}
18291824
}
18301825

1831-
#[pymethod]
1832-
fn __iter__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyFutureIter> {
1833-
Ok(PyFutureIter {
1834-
future: PyRwLock::new(Some(zelf.into())),
1835-
})
1836-
}
1837-
18381826
#[pymethod]
18391827
fn __await__(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyFutureIter> {
18401828
Ok(PyFutureIter {

crates/stdlib/src/socket.rs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod _socket {
2020
ArgBytesLike, ArgIntoFloat, ArgMemoryBuffer, ArgStrOrBytesLike, Either, FsPath,
2121
OptionalArg, OptionalOption,
2222
},
23-
types::{Constructor, DefaultConstructor, Initializer, Representable},
23+
types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable},
2424
utils::ToCString,
2525
};
2626

@@ -1417,7 +1417,44 @@ mod _socket {
14171417
}
14181418
}
14191419

1420-
#[pyclass(with(Constructor, Initializer, Representable), flags(BASETYPE))]
1420+
impl Destructor for PySocket {
1421+
fn del(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<()> {
1422+
// Emit ResourceWarning if socket is still open
1423+
if zelf.sock.read().is_some() {
1424+
let laddr = if let Ok(sock) = zelf.sock()
1425+
&& let Ok(addr) = sock.local_addr()
1426+
&& let Ok(repr) = get_addr_tuple(&addr, vm).repr(vm)
1427+
{
1428+
format!(", laddr={}", repr.as_wtf8())
1429+
} else {
1430+
String::new()
1431+
};
1432+
1433+
let msg = format!(
1434+
"unclosed <socket.socket fd={}, family={}, type={}, proto={}{}>",
1435+
zelf.fileno(),
1436+
zelf.family.load(),
1437+
zelf.kind.load(),
1438+
zelf.proto.load(),
1439+
laddr
1440+
);
1441+
let _ = crate::vm::warn::warn(
1442+
vm.ctx.new_str(msg).into(),
1443+
Some(vm.ctx.exceptions.resource_warning.to_owned()),
1444+
1,
1445+
None,
1446+
vm,
1447+
);
1448+
}
1449+
let _ = zelf.close();
1450+
Ok(())
1451+
}
1452+
}
1453+
1454+
#[pyclass(
1455+
with(Constructor, Initializer, Representable, Destructor),
1456+
flags(BASETYPE)
1457+
)]
14211458
impl PySocket {
14221459
fn _init(
14231460
zelf: PyRef<Self>,
@@ -2139,38 +2176,6 @@ mod _socket {
21392176
Ok(())
21402177
}
21412178

2142-
#[pymethod]
2143-
fn __del__(&self, vm: &VirtualMachine) {
2144-
// Emit ResourceWarning if socket is still open
2145-
if self.sock.read().is_some() {
2146-
let laddr = if let Ok(sock) = self.sock()
2147-
&& let Ok(addr) = sock.local_addr()
2148-
&& let Ok(repr) = get_addr_tuple(&addr, vm).repr(vm)
2149-
{
2150-
format!(", laddr={}", repr.as_wtf8())
2151-
} else {
2152-
String::new()
2153-
};
2154-
2155-
let msg = format!(
2156-
"unclosed <socket.socket fd={}, family={}, type={}, proto={}{}>",
2157-
self.fileno(),
2158-
self.family.load(),
2159-
self.kind.load(),
2160-
self.proto.load(),
2161-
laddr
2162-
);
2163-
let _ = crate::vm::warn::warn(
2164-
vm.ctx.new_str(msg).into(),
2165-
Some(vm.ctx.exceptions.resource_warning.to_owned()),
2166-
1,
2167-
None,
2168-
vm,
2169-
);
2170-
}
2171-
let _ = self.close();
2172-
}
2173-
21742179
#[pymethod]
21752180
#[inline]
21762181
fn detach(&self) -> i64 {

crates/stdlib/src/ssl.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -857,18 +857,13 @@ mod _ssl {
857857
binary_form: OptionalArg<bool>,
858858
}
859859

860-
#[pyclass(with(Constructor), flags(BASETYPE))]
860+
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]
861861
impl PySSLContext {
862862
// Helper method to convert DER certificate bytes to Python dict
863863
fn cert_der_to_dict(&self, vm: &VirtualMachine, cert_der: &[u8]) -> PyResult<PyObjectRef> {
864864
cert::cert_der_to_dict_helper(vm, cert_der)
865865
}
866866

867-
#[pymethod]
868-
fn __repr__(&self) -> String {
869-
format!("<SSLContext(protocol={})>", self.protocol)
870-
}
871-
872867
#[pygetset]
873868
fn check_hostname(&self) -> bool {
874869
*self.check_hostname.read()
@@ -2191,6 +2186,13 @@ mod _ssl {
21912186
}
21922187
}
21932188

2189+
impl Representable for PySSLContext {
2190+
#[inline]
2191+
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
2192+
Ok(format!("<SSLContext(protocol={})>", zelf.protocol))
2193+
}
2194+
}
2195+
21942196
impl Constructor for PySSLContext {
21952197
type Args = (i32,);
21962198

@@ -2374,7 +2376,7 @@ mod _ssl {
23742376
Completed, // unwrap() completed successfully
23752377
}
23762378

2377-
#[pyclass(with(Constructor), flags(BASETYPE))]
2379+
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]
23782380
impl PySSLSocket {
23792381
// Check if this is BIO mode
23802382
pub(crate) fn is_bio_mode(&self) -> bool {
@@ -3021,11 +3023,6 @@ mod _ssl {
30213023
}
30223024
}
30233025

3024-
#[pymethod]
3025-
fn __repr__(&self) -> String {
3026-
"<SSLSocket>".to_string()
3027-
}
3028-
30293026
// Helper function to convert Python PROTO_* constants to rustls versions
30303027
fn get_rustls_versions(
30313028
minimum: i32,
@@ -4562,6 +4559,13 @@ mod _ssl {
45624559
}
45634560
}
45644561

4562+
impl Representable for PySSLSocket {
4563+
#[inline]
4564+
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
4565+
Ok("<SSLSocket>".to_owned())
4566+
}
4567+
}
4568+
45654569
impl Constructor for PySSLSocket {
45664570
type Args = ();
45674571

crates/vm/src/builtins/str.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,6 @@ impl PyStr {
663663
self.data.is_empty()
664664
}
665665

666-
#[pymethod(name = "__len__")]
667666
#[inline]
668667
pub fn char_len(&self) -> usize {
669668
self.data.char_len()

0 commit comments

Comments
 (0)