File tree Expand file tree Collapse file tree 3 files changed +28
-3
lines changed Expand file tree Collapse file tree 3 files changed +28
-3
lines changed Original file line number Diff line number Diff line change @@ -505,13 +505,16 @@ def _is_wrapper(f):
505
505
def _is_wrapper (f ):
506
506
return hasattr (f , '__wrapped__' ) and not stop (f )
507
507
f = func # remember the original func for error reporting
508
- memo = {id (f )} # Memoise by id to tolerate non-hashable objects
508
+ # Memoise by id to tolerate non-hashable objects, but store objects to
509
+ # ensure they aren't destroyed, which would allow their IDs to be reused.
510
+ memo = {id (f ): f }
511
+ recursion_limit = sys .getrecursionlimit ()
509
512
while _is_wrapper (func ):
510
513
func = func .__wrapped__
511
514
id_func = id (func )
512
- if id_func in memo :
515
+ if ( id_func in memo ) or ( len ( memo ) >= recursion_limit ) :
513
516
raise ValueError ('wrapper loop when unwrapping {!r}' .format (f ))
514
- memo . add ( id_func )
517
+ memo [ id_func ] = func
515
518
return func
516
519
517
520
# -------------------------------------------------- source code extraction
Original file line number Diff line number Diff line change @@ -3554,6 +3554,19 @@ def test_builtins_have_signatures(self):
3554
3554
self .assertIsNone (obj .__text_signature__ )
3555
3555
3556
3556
3557
+ class NTimesUnwrappable :
3558
+ def __init__ (self , n ):
3559
+ self .n = n
3560
+ self ._next = None
3561
+
3562
+ @property
3563
+ def __wrapped__ (self ):
3564
+ if self .n <= 0 :
3565
+ raise Exception ("Unwrapped too many times" )
3566
+ if self ._next is None :
3567
+ self ._next = NTimesUnwrappable (self .n - 1 )
3568
+ return self ._next
3569
+
3557
3570
class TestUnwrap (unittest .TestCase ):
3558
3571
3559
3572
def test_unwrap_one (self ):
@@ -3609,6 +3622,11 @@ class C:
3609
3622
__wrapped__ = func
3610
3623
self .assertIsNone (inspect .unwrap (C ()))
3611
3624
3625
+ def test_recursion_limit (self ):
3626
+ obj = NTimesUnwrappable (sys .getrecursionlimit () + 1 )
3627
+ with self .assertRaisesRegex (ValueError , 'wrapper loop' ):
3628
+ inspect .unwrap (obj )
3629
+
3612
3630
class TestMain (unittest .TestCase ):
3613
3631
def test_only_source (self ):
3614
3632
module = importlib .import_module ('unittest' )
Original file line number Diff line number Diff line change @@ -995,6 +995,10 @@ Library
995
995
- Issue #29581: ABCMeta.__new__ now accepts ``**kwargs``, allowing abstract base
996
996
classes to use keyword parameters in __init_subclass__. Patch by Nate Soares.
997
997
998
+ - Issue #25532: inspect.unwrap() will now only try to unwrap an object
999
+ sys.getrecursionlimit() times, to protect against objects which create a new
1000
+ object on every attribute access.
1001
+
998
1002
Windows
999
1003
-------
1000
1004
You can’t perform that action at this time.
0 commit comments