Skip to content

Commit

Permalink
:add: revision version with the b3j0f.utils.proxy.__getproxy__ attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
b3j0f committed Jun 27, 2015
1 parent 19b479a commit 461b874
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 93 deletions.
5 changes: 4 additions & 1 deletion b3j0f/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
# SOFTWARE.
# --------------------------------------------------------------------

"""b3j0f.utils package.
"""

#: project version
__version__ = '0.9.3'
__version__ = '0.9.4'
227 changes: 136 additions & 91 deletions b3j0f/utils/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
__PROXY_CLASS__ = 'Proxy'
#: attribute name for proxified element
__PROXIFIED__ = '__proxified__'
#: instance method name for delegating proxy generation to the elt to proxify
__GETPROXY__ = '__getproxy__'


def proxify_elt(elt, bases=None, _dict=None):
Expand Down Expand Up @@ -148,11 +150,6 @@ def proxify_routine(routine, impl=None):
# init impl
impl = routine if impl is None else impl

try:
__file__ = getfile(routine)
except TypeError:
__file__ = '<string>'

is_method = ismethod(routine)
if is_method:
function = routine.__func__
Expand Down Expand Up @@ -184,73 +181,67 @@ def proxify_routine(routine, impl=None):
updated.append(wrapper_update)

@wraps(function, assigned=assigned, updated=updated)
def function(*args, **kwargs):
pass

# get params from routine wrapper
args, varargs, kwargs, _ = getargspec(function)
def wrappedfunction(*args, **kwargs):
"""Default wrap function.
"""
function = wrappedfunction
# get params from function
args, varargs, kwargs, _ = getargspec(function)

# get params from routine
name = function.__name__

# flag for lambda function
islambda = __LAMBDA_NAME__ == function.__name__
if islambda:
name = '_{0}'.format(int(time()))

# get join method for reducing concatenation time execution
join = "".join
result = _compilecode(
function=function, name=name, impl=impl,
args=args, varargs=varargs, kwargs=kwargs
)

# default indentation
indent = ' '

if islambda:
newcodestr = "{0} = lambda ".format(name)
else:
newcodestr = "def {0}(".format(name)
# set wrapping assignments
for wrapper_assignment in WRAPPER_ASSIGNMENTS:
try:
value = getattr(function, wrapper_assignment)
except AttributeError:
pass
else:
setattr(result, wrapper_assignment, value)
# set proxy module
result.__module__ = proxify_routine.__module__
# update wrapping updating
for wrapper_update in WRAPPER_UPDATES:
try:
value = getattr(function, wrapper_update)
except AttributeError:
pass
else:
getattr(result, wrapper_update).update(value)

if args:
newcodestr = join((newcodestr, "{0}".format(args[0])))
for arg in args[1:]:
newcodestr = join((newcodestr, ", {0}".format(arg)))
# set proxyfied element on proxy
setattr(result, __PROXIFIED__, routine)

if varargs is not None:
if args:
newcodestr = join((newcodestr, ", "))
newcodestr = join((newcodestr, "*{0}".format(varargs)))
if is_method: # create a new method
args = [result, routine.__self__]
if PY2:
args.append(routine.im_class)
result = MethodType(*args)

if kwargs is not None:
if args or varargs is not None:
newcodestr = join((newcodestr, ", "))
newcodestr = join((newcodestr, "**{0}".format(kwargs)))
return result

# insert impl call
if islambda:
newcodestr = join((newcodestr, ": impl("))
else:
newcodestr = join(
(
newcodestr,
"):\n{0}return impl(".format(indent)
)
)

if args:
newcodestr = join((newcodestr, "{0}".format(args[0])))
for arg in args[1:]:
newcodestr = join((newcodestr, ", {0}".format(arg)))
def _compilecode(function, name, impl, args, varargs, kwargs):
"""Get generated code.
if varargs is not None:
if args:
newcodestr = join((newcodestr, ", "))
newcodestr = join((newcodestr, "*{0}".format(varargs)))
:return: function proxy generated code.
:rtype: str
"""

if kwargs is not None:
if args or varargs is not None:
newcodestr = join((newcodestr, ", "))
newcodestr = join((newcodestr, "**{0}".format(kwargs)))
newcodestr, generatedname = _generatecode(
function=function, name=name, impl=impl,
args=args, varargs=varargs, kwargs=kwargs
)

newcodestr = join((newcodestr, ")\n"))
try:
__file__ = getfile(function)
except TypeError:
__file__ = '<string>'

# compile newcodestr
code = compile(newcodestr, __file__, 'single')
Expand All @@ -260,7 +251,7 @@ def function(*args, **kwargs):
exec(code, _globals)

# get new code
newco = _globals[name].__code__
newco = _globals[generatedname].__code__

# get new consts list
newconsts = list(newco.co_consts)
Expand Down Expand Up @@ -292,7 +283,7 @@ def function(*args, **kwargs):
index += 1

# get code string
codestr = bytes(newcode) if PY3 else join([chr(co) for co in newcode])
codestr = bytes(newcode) if PY3 else "".join([chr(co) for co in newcode])

# get vargs
vargs = [
Expand All @@ -316,51 +307,105 @@ def function(*args, **kwargs):
codeobj, function.__globals__, function.__name__,
function.__defaults__, function.__closure__
)
# set wrapping assignments
for wrapper_assignment in WRAPPER_ASSIGNMENTS:
try:
value = getattr(function, wrapper_assignment)
except AttributeError:
pass
else:
setattr(result, wrapper_assignment, value)
# set proxy module
result.__module__ = proxify_routine.__module__
# update wrapping updating
for wrapper_update in WRAPPER_UPDATES:
try:
value = getattr(function, wrapper_update)
except AttributeError:
pass
else:
getattr(result, wrapper_update).update(value)

# set proxyfied element on proxy
setattr(result, __PROXIFIED__, routine)
return result

if is_method: # create a new method
args = [result, routine.__self__]
if PY2:
args.append(routine.im_class)
result = MethodType(*args)

def _generatecode(function, name, impl, args, varargs, kwargs):

code = ''

# flag for lambda function
islambda = __LAMBDA_NAME__ == name
if islambda:
generatedname = '_{0}'.format(int(time()))
else:
generatedname = name

# get join method for reducing concatenation time execution
join = "".join

# default indentation
indent = ' '

if islambda:
code = "{0} = lambda ".format(generatedname)
else:
code = "def {0}(".format(generatedname)

if args:
code = join((code, "{0}".format(args[0])))
for arg in args[1:]:
code = join((code, ", {0}".format(arg)))

if varargs is not None:
if args:
code = join((code, ", "))
code = join((code, "*{0}".format(varargs)))

if kwargs is not None:
if args or varargs is not None:
code = join((code, ", "))
code = join((code, "**{0}".format(kwargs)))

# insert impl call
if islambda:
code = join((code, ": impl("))
else:
code = join(
(
code,
"):\n{0}return impl(".format(indent)
)
)

if args:
code = join((code, "{0}".format(args[0])))
for arg in args[1:]:
code = join((code, ", {0}".format(arg)))

if varargs is not None:
if args:
code = join((code, ", "))
code = join((code, "*{0}".format(varargs)))

if kwargs is not None:
if args or varargs is not None:
code = join((code, ", "))
code = join((code, "**{0}".format(kwargs)))

code = join((code, ")\n"))

result = code, generatedname

return result


def get_proxy(elt, bases=None, _dict=None):
"""Get proxy from an elt.
If elt implements the proxy generator method (named ``__getproxy__``), use
it instead of using this module functions.
:param elt: elt to proxify.
:type elt: object or function/method
:param bases: base types to enrich in the result cls if not None.
:param _dict: class members to proxify if not None.
"""

if isroutine(elt):
result = proxify_routine(elt)
# try to find an instance proxy generator
proxygenerator = getattr(elt, __GETPROXY__, None)

# if a proxy generator is not found, use this module
if proxygenerator is None:
if isroutine(elt):
result = proxify_routine(elt)

else: # in case of object, result is a Proxy
result = proxify_elt(elt, bases=bases, _dict=_dict)

else: # in case of object, result is a Proxy
result = proxify_elt(elt, bases=bases, _dict=_dict)
else: # otherwise, use the specific proxy generator
result = proxygenerator()

return result

Expand Down
22 changes: 22 additions & 0 deletions b3j0f/utils/test/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,28 @@ def test_elt_bases_dict(self):

self._assert_elt(add_bases=True, add_dict=True)

def test__getproxy__(self):
"""Test the __getproxy__ instance method.
"""

testproxy = 'test'

class Test(object):
"""Test class.
"""
def __getproxy__(self):
"""Specific get_proxy function to use in order to proxify a
Test instance.
"""

return testproxy

test = Test()

proxy = get_proxy(elt=test)

self.assertEqual(proxy, testproxy)


if __name__ == '__main__':
main()
5 changes: 5 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
ChangeLog
=========

0.9.4 (27/06/15)
----------------

- add __getproxy__ instance method name in order to specialize the generation of a proxy from the elt to proxify.

0.9.3 (14/06/15)
----------------

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

setup(
name='b3j0f.utils',
version='0.9.3',
version='0.9.4',
packages=find_packages(exclude=['test.*', '*.test.*']),
author='b3j0f',
author_email='jlabejof@yahoo.fr',
Expand Down

0 comments on commit 461b874

Please sign in to comment.