def test_monitor():
"""A CallMonitor records each call to a method, and its arguments."""
class SomethingElse(object):
def foo(self, n, y=None):
self.n = n
return y
s = SomethingElse()
original_method = six.get_method_function(s.foo)
with CallMonitor(s.foo) as monitor:
assert s.foo(1, y='a') == 'a'
assert s.foo(2) == None
assert six.get_method_function(s.foo) is original_method
assert s.n == 2
assert monitor.times_called == 2
assert monitor.calls[0].args == (1,)
assert monitor.calls[0].kwargs == {'y':'a'}
assert monitor.calls[0].return_value == 'a'
assert monitor.calls[1].args == (2,)
assert monitor.calls[1].kwargs == {}
assert monitor.calls[1].return_value == None
python类get_method_function()的实例源码
def _get_abc_interface_props_and_funcs(cls):
properties = set()
function_sigs = {}
if not hasattr(cls, '__abstractmethods__'):
return properties, function_sigs
for name in cls.__abstractmethods__:
if _builtin_attrs(name):
pass # shortcut
value = getattr(cls, name)
if isinstance(value, (staticmethod, classmethod, types.MethodType)):
func = six.get_method_function(value)
function_sigs[name] = _get_function_signature(func)
elif isinstance(value, types.FunctionType):
function_sigs[name] = _get_function_signature(value)
elif isinstance(value, property):
properties.add(name)
return properties, function_sigs
def call_unlogged(method, *args, **kwargs):
"""Calls the original method without logging when ``logged`` is applied.
In case you pass in an ordinary method that was not decorated, it will work as usual.
Args:
method: The method object from the object.
*args: Args to pass to the method.
**kwargs: Keyword arguments to pass to the method.
Returns:
Whatever that method returns.
"""
try:
f = method.original_function
except AttributeError:
f = get_method_function(method)
return f(get_method_self(method), *args, **kwargs)
def __getattr__(self, attr):
"""Route all other attribute requests into the parent object's browser. Black magic included
Here is the explanation:
If you call ``.elements`` on this object directly, it will correctly inject the parent
locator. But if you call eg. ``element``, what will happen is that it will invoke the
original method from underlying browser and that method's ``self`` is the underlying browser
and not this wrapper. Therefore ``element`` would call the original ``elements`` without
injecting the parent.
What this getter does is that if you pull out a method, it detects that, unbinds the
pure function and rebinds it to this wrapper. The method that came from the browser object
is now executed not against the browser, but against this wrapper, enabling us to intercept
every single ``elements`` call.
"""
value = getattr(self._browser, attr)
if inspect.ismethod(value):
function = six.get_method_function(value)
# Bind the function like it was defined on this class
value = function.__get__(self, BrowserParentWrapper)
return value
def test_get_method_function():
class X(object):
def m(self):
pass
x = X()
assert six.get_method_function(x.m) is X.__dict__["m"]
py.test.raises(AttributeError, six.get_method_function, hasattr)
def get_op_handler(operation):
""" Import and load the operation handler """
# Patch the unversioned sdk path to include the appropriate API version for the
# resource type in question.
from azure.cli.core._profile import CLOUD
import types
for rt in ResourceType:
if operation.startswith(rt.import_prefix + ".operations."):
subs = operation[len(rt.import_prefix + ".operations."):]
operation_group = subs[:subs.index('_operations')]
operation = operation.replace(
rt.import_prefix,
get_versioned_sdk_path(CLOUD.profile, rt, operation_group=operation_group))
elif operation.startswith(rt.import_prefix):
operation = operation.replace(rt.import_prefix,
get_versioned_sdk_path(CLOUD.profile, rt))
try:
mod_to_import, attr_path = operation.split('#')
op = import_module(mod_to_import)
for part in attr_path.split('.'):
op = getattr(op, part)
if isinstance(op, types.FunctionType):
return op
return six.get_method_function(op)
except (ValueError, AttributeError):
raise ValueError("The operation '{}' is invalid.".format(operation))
def replaced(method, replacement, on=None):
"""A context manager which replaces the method passed in as `method` with the callable
in `replacement` for the duration of the managed context.
.. code-block:: python
class SomethingElse(object):
def foo(self, n, y='yyy'):
assert None, 'This should never be reached in this test'
s = SomethingElse()
def replacement(n, y=None):
return y
with replaced(s.foo, replacement):
assert s.foo(1, y='a') == 'a'
assert s.foo(2) == None
assert s.foo(2) == 'yyy'
"""
StubClass.signatures_match(method, replacement, ignore_self=True)
is_unbound_method = six.PY2 and inspect.ismethod(method) and not method.im_self
if (not inspect.ismethod(method) or is_unbound_method) and not on:
raise ValueError('You have to supply a on= when stubbing an unbound method')
target = on or six.get_method_self(method)
if inspect.isfunction(method) or inspect.isbuiltin(method):
method_name = method.__name__
else:
method_name = six.get_method_function(method).__name__
saved_method = getattr(target, method_name)
try:
setattr(target, method_name, replacement)
yield
finally:
setattr(target, method_name, saved_method)
def test_get_method_function():
class X(object):
def m(self):
pass
x = X()
assert six.get_method_function(x.m) is X.__dict__["m"]
py.test.raises(AttributeError, six.get_method_function, hasattr)
def _is_empty_function(func, unwrap=False):
""" Return True if func is considered empty.
All functions with no return statement have an implicit return None - this is explicit in the code object.
"""
if isinstance(func, (staticmethod, classmethod, types.MethodType)):
func = six.get_method_function(func)
if isinstance(func, property):
func = property.fget
if unwrap:
func = _unwrap_function(func)
try:
code_obj = six.get_function_code(func)
except AttributeError:
# This callable is something else - assume it is OK.
return True
# quick check
if code_obj.co_code == b'd\x00\x00S' and code_obj.co_consts[0] is None:
return True
if code_obj.co_code == b'd\x01\x00S' and code_obj.co_consts[1] is None:
return True
# convert bytes to instructions
instructions = _get_instructions(code_obj)
if len(instructions) < 2:
return True # this never happens as there is always the implicit return None which is 2 instructions
assert instructions[-1].opname == 'RETURN_VALUE' # returns TOS (top of stack)
instruction = instructions[-2]
if not (instruction.opname == 'LOAD_CONST' and code_obj.co_consts[instruction.arg] is None): # TOS is None
return False # return is not None
instructions = instructions[:-2]
if len(instructions) == 0:
return True
# look for raise NotImplementedError
if instructions[-1].opname == 'RAISE_VARARGS':
# the thing we are raising should be the result of __call__ (instantiating exception object)
if instructions[-2].opname == 'CALL_FUNCTION':
for instr in instructions[:-2]:
if instr.opname == 'LOAD_GLOBAL' and code_obj.co_names[instr.arg] == 'NotImplementedError':
return True
return False
def _find_method(obj, func):
if obj:
try:
func_self = six.get_method_self(func)
except AttributeError: # func has no __self__
pass
else:
if func_self is obj:
return six.get_method_function(func).__name__
raise ValueError("Function %s is not a method of: %s" % (func, obj))
def _get_op_handler(operation):
""" Import and load the operation handler """
try:
mod_to_import, attr_path = operation.split('#')
op = import_module(mod_to_import)
for part in attr_path.split('.'):
op = getattr(op, part)
if isinstance(op, types.FunctionType):
return op
return six.get_method_function(op)
except (ValueError, AttributeError):
raise ValueError("The operation '{}' is invalid.".format(operation))
def test_get_method_function():
class X(object):
def m(self):
pass
x = X()
assert six.get_method_function(x.m) is X.__dict__["m"]
py.test.raises(AttributeError, six.get_method_function, hasattr)
def _getobj(self):
# Regular object-making first
obj = super(SpecInstance, self)._getobj()
# Then decorate it with our parent's extra attributes, allowing nested
# test classes to appear as an aggregate of parents' "scopes".
# NOTE: need parent.parent due to instance->class hierarchy
# NOTE: of course, skipping if we've gone out the top into a module etc
if (
not hasattr(self, 'parent') or
not hasattr(self.parent, 'parent') or
not isinstance(self.parent.parent, SpecInstance)
):
return obj
parent_obj = self.parent.parent.obj
# Obtain parent attributes, etc not found on our obj (serves as both a
# useful identifier of "stuff added to an outer class" and a way of
# ensuring that we can override such attrs), and set them on obj
delta = set(dir(parent_obj)).difference(set(dir(obj)))
for name in delta:
value = getattr(parent_obj, name)
# Classes get skipped; they'd always just be other 'inner' classes
# that we don't want to copy elsewhere.
if isinstance(value, six.class_types):
continue
# Functions (methods) may get skipped, or not, depending:
if isinstance(value, types.MethodType):
# If they look like tests, they get skipped; don't want to copy
# tests around!
if istestfunction(name):
continue
# Non-test == they're probably lifecycle methods
# (setup/teardown) or helpers (_do_thing). Rebind them to the
# target instance, otherwise the 'self' in the setup/helper is
# not the same 'self' as that in the actual test method it runs
# around or within!
# TODO: arguably, all setup or helper methods should become
# autouse class fixtures (see e.g. pytest docs under 'xunit
# setup on steroids')
func = six.get_method_function(value)
setattr(obj, name, six.create_bound_method(func, obj))
# Anything else should be some data-type attribute, which is copied
# verbatim / by-value.
else:
setattr(obj, name, value)
return obj
def test_replace():
"""Replaced methods replace the original one, but are restored after the with."""
class SomethingElse(object):
def foo(self, n, y=None):
assert None, 'This should never be reached in this test'
# Case: bound method
s = SomethingElse()
def replacement(n, y=None):
return y
original_method = six.get_method_function(s.foo)
with replaced(s.foo, replacement):
assert s.foo(1, y='a') == 'a'
assert s.foo(2) == None
assert six.get_method_function(s.foo) is original_method
# Case: unbound method
"""Python 3 does not support the concept of unbound methods, they are
just plain functions without an im_class pointing back to their class.
See https://docs.python.org/3/whatsnew/3.0.html#operators-and-special-methods,
and https://mail.python.org/pipermail/python-dev/2005-January/050625.html
for the rationale.
To be able to support them under Python3, on= is mandatory.
"""
s = SomethingElse()
def replacement(self, n, y=None):
return y
original_method = six.get_unbound_function(SomethingElse.foo)
with replaced(SomethingElse.foo, replacement, on=SomethingElse):
assert s.foo(1, y='a') == 'a'
assert s.foo(2) == None
restored_method = six.get_unbound_function(SomethingElse.foo)
assert restored_method is original_method
# Case: unbound method (no on= given)
s = SomethingElse()
def replacement(self, n, y=None):
return y
with pytest.raises(ValueError, message='You have to supply a on= when stubbing an unbound method'):
with replaced(SomethingElse.foo, replacement):
pass
def object_build(self, node, obj):
"""recursive method which create a partial ast from real objects
(only function, class, and method are handled)
"""
if obj in self._done:
return self._done[obj]
self._done[obj] = node
for name in dir(obj):
try:
member = getattr(obj, name)
except AttributeError:
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
if ismethod(member):
member = six.get_method_function(member)
if isfunction(member):
# verify this is not an imported function
filename = getattr(six.get_function_code(member),
'co_filename', None)
if filename is None:
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif filename != getattr(self._module, '__file__', None):
attach_dummy_node(node, name, member)
else:
object_build_function(node, member, name)
elif isbuiltin(member):
if (not _io_discrepancy(member) and
self.imported_member(node, member, name)):
continue
object_build_methoddescriptor(node, member, name)
elif isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
if not class_node in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
# recursion
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
elif ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif type(member) in _CONSTANTS:
attach_const_node(node, name, member)
else:
# create an empty node so that the name is actually defined
attach_dummy_node(node, name, member)
def object_build(self, node, obj):
"""recursive method which create a partial ast from real objects
(only function, class, and method are handled)
"""
if obj in self._done:
return self._done[obj]
self._done[obj] = node
for name in dir(obj):
try:
member = getattr(obj, name)
except AttributeError:
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
if ismethod(member):
member = six.get_method_function(member)
if isfunction(member):
# verify this is not an imported function
filename = getattr(six.get_function_code(member),
'co_filename', None)
if filename is None:
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif filename != getattr(self._module, '__file__', None):
attach_dummy_node(node, name, member)
else:
object_build_function(node, member, name)
elif isbuiltin(member):
if (not _io_discrepancy(member) and
self.imported_member(node, member, name)):
continue
object_build_methoddescriptor(node, member, name)
elif isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
if not class_node in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
# recursion
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
elif ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif type(member) in _CONSTANTS:
attach_const_node(node, name, member)
else:
# create an empty node so that the name is actually defined
attach_dummy_node(node, name, member)
def object_build(self, node, obj):
"""recursive method which create a partial ast from real objects
(only function, class, and method are handled)
"""
if obj in self._done:
return self._done[obj]
self._done[obj] = node
for name in dir(obj):
try:
member = getattr(obj, name)
except AttributeError:
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
if ismethod(member):
member = six.get_method_function(member)
if isfunction(member):
# verify this is not an imported function
filename = getattr(six.get_function_code(member),
'co_filename', None)
if filename is None:
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif filename != getattr(self._module, '__file__', None):
attach_dummy_node(node, name, member)
else:
object_build_function(node, member, name)
elif isbuiltin(member):
if (not _io_discrepancy(member) and
self.imported_member(node, member, name)):
continue
object_build_methoddescriptor(node, member, name)
elif isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
if not class_node in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
# recursion
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
elif ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif type(member) in _CONSTANTS:
attach_const_node(node, name, member)
else:
# create an empty node so that the name is actually defined
attach_dummy_node(node, name, member)
def object_build(self, node, obj):
"""recursive method which create a partial ast from real objects
(only function, class, and method are handled)
"""
if obj in self._done:
return self._done[obj]
self._done[obj] = node
for name in dir(obj):
try:
member = getattr(obj, name)
except AttributeError:
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
if ismethod(member):
member = six.get_method_function(member)
if isfunction(member):
# verify this is not an imported function
filename = getattr(six.get_function_code(member),
'co_filename', None)
if filename is None:
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif filename != getattr(self._module, '__file__', None):
attach_dummy_node(node, name, member)
else:
object_build_function(node, member, name)
elif isbuiltin(member):
if (not _io_discrepancy(member) and
self.imported_member(node, member, name)):
continue
object_build_methoddescriptor(node, member, name)
elif isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
if not class_node in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
# recursion
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
elif ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif type(member) in _CONSTANTS:
attach_const_node(node, name, member)
else:
# create an empty node so that the name is actually defined
attach_dummy_node(node, name, member)