__doc__ handing in the right way (#6390)

This commit is contained in:
Jeong, YunWon
2025-12-10 20:04:09 +09:00
committed by GitHub
parent 90717e5ef7
commit 98fff96f1c
5 changed files with 26 additions and 26 deletions

View File

@@ -342,7 +342,6 @@ class PropertySubclassTests(unittest.TestCase):
with self.assertRaises(AttributeError):
p = slotted_prop(documented_getter)
@unittest.expectedFailure # TODO: RUSTPYTHON
@unittest.skipIf(sys.flags.optimize >= 2,
"Docstrings are omitted with -O2 and above")
def test_property_with_slots_and_doc_slot_docstring_present(self):

View File

@@ -5269,7 +5269,7 @@ class GenericTests(BaseTestCase):
for t in things:
self.assertEqual(weakref.ref(t)(), t)
@unittest.expectedFailure # TODO: RUSTPYTHON
@unittest.expectedFailure # TODO: RUSTPYTHON - __slots__ with Generic doesn't prevent new attributes
def test_parameterized_slots(self):
T = TypeVar('T')
class C(Generic[T]):
@@ -5289,7 +5289,7 @@ class GenericTests(BaseTestCase):
self.assertEqual(get_type_hints(foo, globals(), locals())['x'], C[C])
self.assertEqual(copy(C[int]), deepcopy(C[int]))
@unittest.expectedFailure # TODO: RUSTPYTHON
@unittest.expectedFailure # TODO: RUSTPYTHON - __slots__ with Generic doesn't prevent new attributes
def test_parameterized_slots_dict(self):
T = TypeVar('T')
class D(Generic[T]):

View File

@@ -2679,10 +2679,12 @@ impl Compiler {
let qualname_name = self.name("__qualname__");
emit!(self, Instruction::StoreLocal(qualname_name));
// Store __doc__
self.load_docstring(doc_str);
let doc = self.name("__doc__");
emit!(self, Instruction::StoreLocal(doc));
// Store __doc__ only if there's an explicit docstring
if let Some(doc) = doc_str {
self.emit_load_const(ConstantData::Str { value: doc.into() });
let doc_name = self.name("__doc__");
emit!(self, Instruction::StoreLocal(doc_name));
}
// Store __firstlineno__ (new in Python 3.12+)
self.emit_load_const(ConstantData::Integer {
@@ -2876,17 +2878,6 @@ impl Compiler {
self.store_name(name)
}
fn load_docstring(&mut self, doc_str: Option<String>) {
// TODO: __doc__ must be default None and no bytecode unless it is Some
// Duplicate top of stack (the function or class object)
// Doc string value:
self.emit_load_const(match doc_str {
Some(doc) => ConstantData::Str { value: doc.into() },
None => ConstantData::None, // set docstring None if not declared
});
}
fn compile_while(&mut self, test: &Expr, body: &[Stmt], orelse: &[Stmt]) -> CompileResult<()> {
let while_block = self.new_block();
let else_block = self.new_block();

View File

@@ -1198,11 +1198,10 @@ impl Constructor for PyType {
member: member_def,
});
let attr_name = vm.ctx.intern_str(member.to_string());
if !typ.has_attr(attr_name) {
typ.set_attr(attr_name, member_descriptor.into());
}
let attr_name = vm.ctx.intern_str(member.as_str());
// __slots__ attributes always get a member descriptor
// (this overrides any inherited attribute from MRO)
typ.set_attr(attr_name, member_descriptor.into());
offset += 1;
}
}
@@ -1236,6 +1235,16 @@ impl Constructor for PyType {
}
}
// Set __doc__ to None if not already present in the type's dict
// This matches CPython's behavior in type_dict_set_doc (typeobject.c)
// which ensures every type has a __doc__ entry in its dict
{
let __doc__ = identifier!(vm, __doc__);
if !typ.attributes.read().contains_key(&__doc__) {
typ.attributes.write().insert(__doc__, vm.ctx.none());
}
}
// avoid deadlock
let attributes = typ
.attributes
@@ -1377,8 +1386,9 @@ impl Py<PyType> {
return Ok(vm.ctx.new_str(doc_str).into());
}
// Check if there's a __doc__ in the type's dict
if let Some(doc_attr) = self.get_attr(vm.ctx.intern_str("__doc__")) {
// Check if there's a __doc__ in THIS type's dict only (not MRO)
// CPython returns None if __doc__ is not in the type's own dict
if let Some(doc_attr) = self.get_direct_attr(vm.ctx.intern_str("__doc__")) {
// If it's a descriptor, call its __get__ method
let descr_get = doc_attr
.class()

View File

@@ -186,7 +186,7 @@ A.b()
class A:
pass
assert A.__doc__ == None
assert A.__doc__ is None, A.__doc__
class B:
"Docstring"