appypod-rattail/appy/__init__.py
2015-10-27 22:36:51 +01:00

126 lines
5.3 KiB
Python

'''Appy allows you to create easily complete applications in Python.'''
# ------------------------------------------------------------------------------
import os.path
# ------------------------------------------------------------------------------
def getPath(): return os.path.dirname(__file__)
def versionIsGreaterThanOrEquals(version):
'''This method returns True if the current Appy version is greater than or
equals p_version. p_version must have a format like "0.5.0".'''
import appy.version
if appy.version.short == 'dev':
# We suppose that a developer knows what he is doing, so we return True.
return True
else:
paramVersion = [int(i) for i in version.split('.')]
currentVersion = [int(i) for i in appy.version.short.split('.')]
return currentVersion >= paramVersion
# ------------------------------------------------------------------------------
class Object:
'''At every place we need an object, but without any requirement on its
class (methods, attributes,...) we will use this minimalist class.'''
def __init__(self, **fields):
for k, v in fields.items():
setattr(self, k, v)
def __repr__(self):
res = '<Object '
for attrName, attrValue in self.__dict__.items():
v = attrValue
if hasattr(v, '__repr__'):
v = v.__repr__()
try:
res += '%s=%s ' % (attrName, v)
except UnicodeDecodeError:
res += '%s=<encoding problem> ' % attrName
res = res.strip() + '>'
return res.encode('utf-8')
def __bool__(self):
return bool(self.__dict__)
def get(self, name, default=None): return getattr(self, name, default)
def __getitem__(self, k): return getattr(self, k)
def update(self, other):
'''Includes information from p_other into p_self.'''
for k, v in other.__dict__.items():
setattr(self, k, v)
def clone(self):
res = Object()
res.update(self)
return res
# ------------------------------------------------------------------------------
class Hack:
'''This class proposes methods for patching some existing code with
alternative methods.'''
@staticmethod
def patch(method, replacement, klass=None):
'''This method replaces m_method with a p_replacement method, but
keeps p_method on its class under name
"_base_<initial_method_name>_". In the patched method, one may use
Hack.base to call the base method. If p_method is static, you must
specify its class in p_klass.'''
# Get the class on which the surgery will take place.
isStatic = klass
klass = klass or method.__self__.__class__
# On this class, store m_method under its "base" name.
name = isStatic and method.__name__ or method.__func__.__name__
baseName = '_base_%s_' % name
if isStatic:
# If "staticmethod" isn't called hereafter, the static functions
# will be wrapped in methods.
method = staticmethod(method)
replacement = staticmethod(replacement)
setattr(klass, baseName, method)
setattr(klass, name, replacement)
@staticmethod
def base(method, klass=None):
'''Allows to call the base (replaced) method. If p_method is static,
you must specify its p_klass.'''
isStatic = klass
klass = klass or method.__self__.__class__
name = isStatic and method.__name__ or method.__func__.__name__
return getattr(klass, '_base_%s_' % name)
@staticmethod
def inject(patchClass, klass, verbose=False):
'''Injects any method or attribute from p_patchClass into klass.'''
patched = []
added = []
for name, attr in patchClass.__dict__.iteritems():
if name.startswith('__'): continue # Ignore special methods
# Unwrap functions from static methods
if attr.__class__.__name__ == 'staticmethod':
attr = attr.__get__(attr)
static = True
else:
static = False
# Is this name already defined on p_klass ?
if hasattr(klass, name):
hasAttr = True
klassAttr = getattr(klass, name)
else:
hasAttr = False
klassAttr = None
if hasAttr and callable(attr) and callable(klassAttr):
# Patch this method via Hack.patch
if static:
Hack.patch(klassAttr, attr, klass)
else:
Hack.patch(klassAttr, attr)
patched.append(name)
else:
# Simply replace the static attr or add the new static
# attribute or method.
setattr(klass, name, attr)
added.append(name)
if verbose:
pName = patchClass.__name__
cName = klass.__name__
print('%d method(s) patched from %s to %s (%s)' % \
(len(patched), pName, cName, str(patched)))
print('%d method(s) and/or attribute(s) added from %s to %s (%s)'%\
(len(added), pName, cName, str(added)))
# ------------------------------------------------------------------------------