Skip to content

Instantly share code, notes, and snippets.

@goodwillcoding
Last active December 12, 2015 09:39
Show Gist options
  • Save goodwillcoding/4753404 to your computer and use it in GitHub Desktop.
Save goodwillcoding/4753404 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
"""
"""
from importlib import import_module
from inspect import stack
from inspect import ismethod
from os.path import dirname
from os.path import split
# --------------------------------------------------------------------------- #
def get_inst_func(module_path, method_path):
"""
Given a module path and path to an instance metho retrieve the function
corresponding to the instance method. The returned function is not bound
to the class and as such and "self" can be passed in.
Relative imports are supported.
:param module_path: path to the module. (i.e. foo.bar or ..foo.bar)
:type module_path: str/unicode
:param method_path: path to the instance method inside the module
:type module_path: str/unicode
:return: unbound function (not unbound method)
:rtype: function
Example:
Lets say in 'project/my_module.py' you have:
class FooClass(object):
def bar_method(self):
pass
Calling get_inst_func will get you this:
>>> print get_inst_func('project.my_file', 'FooClass.bar_method')
<function bar_method at 0xbadbeef>
"""
absolute_package = None
error_message = None
# get information about the caller
caller_frame = stack()[1]
caller_frame_file = caller_frame[1]
# check if this is a relative path, and if so traverse up the the last
# frames file name and compute the the full name package for that
# called import_func
last_path = dirname(caller_frame_file)
caller_package_parts = []
for part in module_path.split('.'):
if len(part) == 0:
last_path, module_part = split(last_path)
caller_package_parts.insert(0, module_part)
else:
break
# this should get us the caller package path
if len(caller_package_parts) > 0:
absolute_package = '.'.join(caller_package_parts)
# import the module
subject = import_module(module_path, package=absolute_package)
# now lets handle the function import bit
func_parts = method_path.split('.')
# iterate over all the part of the method path, "recursively" retrieving
# its next part. if any part is missing, compile the error message
# with proper path to be raised later
for part_idx in range(len(func_parts)):
part = func_parts[part_idx]
if hasattr(subject, part):
subject = getattr(subject, part)
else:
if absolute_package is not None:
# if this is a relative package, use the given module_path
# and current subject path
part_path = "%s.%s" % (module_path, subject.__name__)
elif part_idx == 0:
# if this the first element the part path is the module name
part_path = subject.__name__
else:
# otherwise use the absolute computed path
part_path = '%s.%s' % (subject.__module__, subject.__name__)
error_message = "cannot import '%s' from '%s'" % (part, part_path)
break
# now lets see if subject we got is a method and if its not we compile an
# error message to be raised later
if error_message is None and ismethod(subject) is False:
bad_method_name = '%s.%s' % (module_path, method_path)
error_message = "'%s' is not an instance method" % bad_method_name
# clean up the reference to frame stack
del caller_frame
# raise any error that happened
if error_message is not None:
raise ImportError(error_message)
return subject.im_func
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment