bpo-33261: guard access to __code__ attribute in inspect (GH-6448)

This commit is contained in:
Jeroen Demeyer 2019-04-02 16:03:42 +02:00 committed by Petr Viktorin
parent 487b73ab39
commit fcef60f59d
4 changed files with 29 additions and 9 deletions

View file

@ -168,23 +168,30 @@ def isfunction(object):
__kwdefaults__ dict of keyword only parameters with defaults""" __kwdefaults__ dict of keyword only parameters with defaults"""
return isinstance(object, types.FunctionType) return isinstance(object, types.FunctionType)
def _has_code_flag(f, flag):
"""Return true if ``f`` is a function (or a method or functools.partial
wrapper wrapping a function) whose code object has the given ``flag``
set in its flags."""
while ismethod(f):
f = f.__func__
f = functools._unwrap_partial(f)
if not isfunction(f):
return False
return bool(f.__code__.co_flags & flag)
def isgeneratorfunction(obj): def isgeneratorfunction(obj):
"""Return true if the object is a user-defined generator function. """Return true if the object is a user-defined generator function.
Generator function objects provide the same attributes as functions. Generator function objects provide the same attributes as functions.
See help(isfunction) for a list of attributes.""" See help(isfunction) for a list of attributes."""
obj = functools._unwrap_partial(obj) return _has_code_flag(obj, CO_GENERATOR)
return bool((isfunction(obj) or ismethod(obj)) and
obj.__code__.co_flags & CO_GENERATOR)
def iscoroutinefunction(obj): def iscoroutinefunction(obj):
"""Return true if the object is a coroutine function. """Return true if the object is a coroutine function.
Coroutine functions are defined with "async def" syntax. Coroutine functions are defined with "async def" syntax.
""" """
obj = functools._unwrap_partial(obj) return _has_code_flag(obj, CO_COROUTINE)
return bool(((isfunction(obj) or ismethod(obj)) and
obj.__code__.co_flags & CO_COROUTINE))
def isasyncgenfunction(obj): def isasyncgenfunction(obj):
"""Return true if the object is an asynchronous generator function. """Return true if the object is an asynchronous generator function.
@ -192,9 +199,7 @@ def isasyncgenfunction(obj):
Asynchronous generator functions are defined with "async def" Asynchronous generator functions are defined with "async def"
syntax and have "yield" expressions in their body. syntax and have "yield" expressions in their body.
""" """
obj = functools._unwrap_partial(obj) return _has_code_flag(obj, CO_ASYNC_GENERATOR)
return bool((isfunction(obj) or ismethod(obj)) and
obj.__code__.co_flags & CO_ASYNC_GENERATOR)
def isasyncgen(object): def isasyncgen(object):
"""Return true if the object is an asynchronous generator.""" """Return true if the object is an asynchronous generator."""

View file

@ -80,3 +80,14 @@ try:
raise Exception() raise Exception()
except: except:
tb = sys.exc_info()[2] tb = sys.exc_info()[2]
class Callable:
def __call__(self, *args):
return args
def as_method_of(self, obj):
from types import MethodType
return MethodType(self, obj)
custom_method = Callable().as_method_of(42)
del Callable

View file

@ -146,6 +146,7 @@ class TestPredicates(IsTestBase):
self.istest(inspect.isfunction, 'mod.spam') self.istest(inspect.isfunction, 'mod.spam')
self.istest(inspect.isfunction, 'mod.StupidGit.abuse') self.istest(inspect.isfunction, 'mod.StupidGit.abuse')
self.istest(inspect.ismethod, 'git.argue') self.istest(inspect.ismethod, 'git.argue')
self.istest(inspect.ismethod, 'mod.custom_method')
self.istest(inspect.ismodule, 'mod') self.istest(inspect.ismodule, 'mod')
self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory') self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory')
self.istest(inspect.isgenerator, '(x for x in range(2))') self.istest(inspect.isgenerator, '(x for x in range(2))')

View file

@ -0,0 +1,3 @@
Do not raise AttributeError when calling the inspect functions
isgeneratorfunction, iscoroutinefunction, isasyncgenfunction on a method
created from an arbitrary callable. Instead, return False.