@@ -622,12 +622,13 @@ def print_coverage(self, outfile=sys.stdout, *, missing_width=None) -> None:
622622
623623 @staticmethod
624624 def find_functions (items , visited : set ):
625- import inspect
625+ # Don't use isinstance() or inspect.isfunction, as isinstance as may call __class__,
626+ # which may have side effects (e.g., using Celery https://github.com/celery/celery).
626627 def is_patchable_function (func ):
627628 # PyPy has no "builtin functions" like CPython. instead, it uses
628629 # regular functions, with a special type of code object.
629630 # the second condition is always True on CPython
630- return inspect . isfunction ( func ) and type (func .__code__ ) is types .CodeType
631+ return issubclass ( type ( func ), types . FunctionType ) and type (func .__code__ ) is types .CodeType
631632
632633 def find_funcs (root ):
633634 if is_patchable_function (root ):
@@ -637,7 +638,7 @@ def find_funcs(root):
637638
638639 # Prefer isinstance(x,type) over isclass(x) because many many
639640 # things, such as str(), are classes
640- elif isinstance ( root , type ):
641+ elif issubclass ( type ( root ) , type ):
641642 if root not in visited :
642643 visited .add (root )
643644
@@ -653,7 +654,7 @@ def find_funcs(root):
653654 yield from find_funcs (base .__dict__ [obj_key ])
654655 break
655656
656- elif (isinstance ( root , classmethod ) or isinstance ( root , staticmethod )) and \
657+ elif (issubclass ( type ( root ) , classmethod ) or issubclass ( type ( root ) , staticmethod )) and \
657658 is_patchable_function (root .__func__ ):
658659 if root .__func__ not in visited :
659660 visited .add (root .__func__ )
0 commit comments