Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
104b9e0c
Commit
104b9e0c
authored
Feb 05, 2015
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix many custom mro() edge cases and improve code quality (#22735)
Patch by Eldar Abusalimov.
parent
9125fe2e
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
622 additions
and
254 deletions
+622
-254
Lib/test/test_descr.py
Lib/test/test_descr.py
+226
-1
Misc/ACKS
Misc/ACKS
+1
-0
Misc/NEWS
Misc/NEWS
+3
-0
Objects/typeobject.c
Objects/typeobject.c
+392
-253
No files found.
Lib/test/test_descr.py
View file @
104b9e0c
...
...
@@ -4996,11 +4996,236 @@ class SharedKeyTests(unittest.TestCase):
self
.
assertLess
(
sys
.
getsizeof
(
vars
(
b
)),
sys
.
getsizeof
({}))
class
DebugHelperMeta
(
type
):
"""
Sets default __doc__ and simplifies repr() output.
"""
def
__new__
(
mcls
,
name
,
bases
,
attrs
):
if
attrs
.
get
(
'__doc__'
)
is
None
:
attrs
[
'__doc__'
]
=
name
# helps when debugging with gdb
return
type
.
__new__
(
mcls
,
name
,
bases
,
attrs
)
def
__repr__
(
cls
):
return
repr
(
cls
.
__name__
)
class
MroTest
(
unittest
.
TestCase
):
"""
Regressions for some bugs revealed through
mcsl.mro() customization (typeobject.c: mro_internal()) and
cls.__bases__ assignment (typeobject.c: type_set_bases()).
"""
def
setUp
(
self
):
self
.
step
=
0
self
.
ready
=
False
def
step_until
(
self
,
limit
):
ret
=
(
self
.
step
<
limit
)
if
ret
:
self
.
step
+=
1
return
ret
def
test_incomplete_set_bases_on_self
(
self
):
"""
type_set_bases must be aware that type->tp_mro can be NULL.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
step_until
(
1
):
assert
cls
.
__mro__
is
None
cls
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_reent_set_bases_on_base
(
self
):
"""
Deep reentrancy must not over-decref old_mro.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
not
None
and
cls
.
__name__
==
'B'
:
# 4-5 steps are usually enough to make it crash somewhere
if
self
.
step_until
(
10
):
A
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B
(
A
):
pass
B
.
__bases__
+=
()
def
test_reent_set_bases_on_direct_base
(
self
):
"""
Similar to test_reent_set_bases_on_base, but may crash differently.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
base
=
cls
.
__bases__
[
0
]
if
base
is
not
object
:
if
self
.
step_until
(
5
):
base
.
__bases__
+=
()
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B
(
A
):
pass
class
C
(
B
):
pass
def
test_reent_set_bases_tp_base_cycle
(
self
):
"""
type_set_bases must check for an inheritance cycle not only through
MRO of the type, which may be not yet updated in case of reentrance,
but also through tp_base chain, which is assigned before diving into
inner calls to mro().
Otherwise, the following snippet can loop forever:
do {
// ...
type = type->tp_base;
} while (type != NULL);
Functions that rely on tp_base (like solid_base and PyType_IsSubtype)
would not be happy in that case, causing a stack overflow.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
:
if
cls
.
__name__
==
'B1'
:
B2
.
__bases__
=
(
B1
,)
if
cls
.
__name__
==
'B2'
:
B1
.
__bases__
=
(
B2
,)
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
self
.
ready
=
True
with
self
.
assertRaises
(
TypeError
):
B1
.
__bases__
+=
()
def
test_tp_subclasses_cycle_in_update_slots
(
self
):
"""
type_set_bases must check for reentrancy upon finishing its job
by updating tp_subclasses of old/new bases of the type.
Otherwise, an implicit inheritance cycle through tp_subclasses
can break functions that recurse on elements of that field
(like recurse_down_subclasses and mro_hierarchy) eventually
leading to a stack overflow.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
and
cls
.
__name__
==
'C'
:
self
.
ready
=
False
C
.
__bases__
=
(
B2
,)
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
class
C
(
A
):
pass
self
.
ready
=
True
C
.
__bases__
=
(
B1
,)
B1
.
__bases__
=
(
C
,)
self
.
assertEqual
(
C
.
__bases__
,
(
B2
,))
self
.
assertEqual
(
B2
.
__subclasses__
(),
[
C
])
self
.
assertEqual
(
B1
.
__subclasses__
(),
[])
self
.
assertEqual
(
B1
.
__bases__
,
(
C
,))
self
.
assertEqual
(
C
.
__subclasses__
(),
[
B1
])
def
test_tp_subclasses_cycle_error_return_path
(
self
):
"""
The same as test_tp_subclasses_cycle_in_update_slots, but tests
a code path executed on error (goto bail).
"""
class
E
(
Exception
):
pass
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
self
.
ready
and
cls
.
__name__
==
'C'
:
if
C
.
__bases__
==
(
B2
,):
self
.
ready
=
False
else
:
C
.
__bases__
=
(
B2
,)
raise
E
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
class
B1
(
A
):
pass
class
B2
(
A
):
pass
class
C
(
A
):
pass
self
.
ready
=
True
with
self
.
assertRaises
(
E
):
C
.
__bases__
=
(
B1
,)
B1
.
__bases__
=
(
C
,)
self
.
assertEqual
(
C
.
__bases__
,
(
B2
,))
self
.
assertEqual
(
C
.
__mro__
,
tuple
(
type
.
mro
(
C
)))
def
test_incomplete_extend
(
self
):
"""
Extending an unitialized type with type->tp_mro == NULL must
throw a reasonable TypeError exception, instead of failing
with PyErr_BadInternalCall.
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
None
and
cls
.
__name__
!=
'X'
:
with
self
.
assertRaises
(
TypeError
):
class
X
(
cls
):
pass
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_incomplete_super
(
self
):
"""
Attrubute lookup on a super object must be aware that
its target type can be uninitialized (type->tp_mro == NULL).
"""
class
M
(
DebugHelperMeta
):
def
mro
(
cls
):
if
cls
.
__mro__
is
None
:
with
self
.
assertRaises
(
AttributeError
):
super
(
cls
,
cls
).
xxx
return
type
.
mro
(
cls
)
class
A
(
metaclass
=
M
):
pass
def
test_main
():
# Run all local test cases, with PTypesLongInitTest first.
support
.
run_unittest
(
PTypesLongInitTest
,
OperatorsTest
,
ClassPropertiesAndMethods
,
DictProxyTests
,
MiscTests
,
PicklingTests
,
SharedKeyTests
)
MiscTests
,
PicklingTests
,
SharedKeyTests
,
MroTest
)
if
__name__
==
"__main__"
:
test_main
()
Misc/ACKS
View file @
104b9e0c
...
...
@@ -16,6 +16,7 @@ Michael Abbott
Rajiv Abraham
David Abrahams
Marc Abramowitz
Eldar Abusalimov
Ron Adam
Anton Afanasyev
Ali Afshar
...
...
Misc/NEWS
View file @
104b9e0c
...
...
@@ -11,6 +11,9 @@ Release date: TBA
Core and Builtins
-----------------
- Issue #22735: Fix many edge cases (including crashes) involving custom mro()
implementations.
- Issue #22896: Avoid using PyObject_AsCharBuffer(), PyObject_AsReadBuffer()
and PyObject_AsWriteBuffer().
...
...
Objects/typeobject.c
View file @
104b9e0c
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment