def _cleanup(): """Free all tracked instances. Closes and destroys all currently allocated resources. This gets called from atexit handler just before :term:`ImageMagick` gets uninitialized. """ msg = formattable('Tracked weakrefs: {0}') logger.debug(msg.format(len(_registry))) alive = 0 unclosed = 0 with __lock: for obj in _registry.values(): alive += 1 if not obj.closed: unclosed += 1 obj.close(untrack=False) msg = formattable('Alive weakrefs: {0}') logger.debug(msg.format(alive)) msg = formattable('Unclosed resources: {0}') logger.debug(msg.format(unclosed)) logger.debug('Finished cleanup')
def memoized(f, *args, **kw): """Decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated. This decorator performs proper synchronization to make it thread-safe. """ key = f, args, frozenset(kw.items()) if key not in __cache: with __lock: if key not in __cache: __cache[key] = {'lock': RLock()} key_cache = __cache[key] if 'value' not in key_cache: with key_cache['lock']: if 'value' not in key_cache: info = key[0].__name__, key[1] msg = formattable('Memoizing {0} args={1}').format(*info) logger.debug(msg) result = f(*args, **kw) key_cache['value'] = result msg = formattable('Memoized {0} args={1}').format(*info) logger.debug(msg) return key_cache['value']
def _lock(self, key): with self.__lock: if key in self.__locked: msg = formattable("Key '{0}' has been already locked") raise PystaciaException(msg.format(key)) self.__locked.append(key)
def __delattr__(self, key): with self.__lock: if key in self.__locked: msg = formattable("Key '{0}' has been locked") raise PystaciaException(msg.format(key)) del self.__dict__[key]
def init_dll(dll): def shutdown(): logger.debug('Cleaning up traced instances') _cleanup() c_call(None, 'terminus') if jython: from java.lang import System # @UnresolvedImport System.exit(0) logger.debug('Critical section - init MagickWand') with __lock: if not dll.__inited: c_call(None, 'genesis', __init=False) logger.debug('Registering atexit handler') atexit.register(shutdown) dll.__inited = True version = magick.get_version() if version < min_version: msg = formattable('Unsupported version of MagickWand {0}') warn(msg.format(version))
def get_dll(init=True, environ=None, isolated=False): """Find ImageMagick DLL and initialize it. Searches available paths with :func:`find_library` and then fallbacks to standard :func:`ctypes.util.find_liblrary`. Loads the DLL into memory, initializes it and warns if it has unsupported API and ABI versions. """ if not hasattr(get_dll, '__dll') or isolated: logger.debug('Critical section - load MagickWand') with __lock: if not hasattr(get_dll, '__dll') or isolated: if not environ: environ = os.environ path = find_library(name, abis, environ=environ) if not path: msg = 'Could not find or load MagickWand' raise PystaciaException(msg) msg = formattable('Loading MagickWand from {0}') logger.debug(msg.format(path)) dll = CDLL(path) if not isolated: get_dll.__dll = dll get_dll.__dll.__inited = False else: return dll dll = get_dll.__dll if init and not dll.__inited: init_dll(dll) return dll
def get_string(self): """Return string representation of color. :rtype: ``str`` Returns standard CSS string representation of color either ``rgb(r, g, b)`` or ``rgba(r, g, b, a)`` when color is not fully opaque. """ if self.alpha == 1: template = formattable('rgb({0}, {1}, {2})') else: template = formattable('rgba({0}, {1}, {2}, {3})') rgb = tuple(int(x * 255) for x in self.get_rgb()) return template.format(*(rgb + (self.alpha, )))
def get_string(self): """Return string representation of color. :rtype: ``str`` Returns standard CSS string representation of color either ``rgb(r, g, b)`` or ``rgba(r, g, b, a)`` when color is not fully opaque. """ if self.alpha == 1: template = formattable('rgb({0}, {1}, {2})') else: template = formattable('rgba({0}, {1}, {2}, {3})') rgb = tuple(int(x * 255) for x in self.get_rgb()) return template.format(*(rgb + (self.alpha,)))
def __getattr__(self, key): if key in self.__defaults: with self.__lock: if key in self.__defaults: return self.__defaults[key] msg = '{0} object has no attribute {1}, neither found in defaults' raise AttributeError(formattable(msg).format(self.__class__, key))
def copy(self): """Get independent copy of this resource.""" resource = self._clone() if resource is None: tmpl = formattable('{0} _clone method returned None') raise PystaciaException(tmpl.format(self.__class__.__name__)) return self.__class__(resource)
def __init__(self, resource=None): """Construct new instance of resource.""" self.__resource = resource if resource is not None else self._alloc() if self.__resource is None: tmpl = formattable('{0} _alloc method returned None') raise PystaciaException(tmpl.format(self.__class__.__name__)) _track(self)
def __repr__(self): template = "<{class_}(w={w},h={h},{depth}bit" ",{colorspace},{type}) object at {addr}>" w, h = self.size depth, type = self.depth, self.type.name # @ReservedAssignment colorspace, addr = self.colorspace.name, id(self) class_ = self.__class__.__name__ return formattable(template).format( class_=class_, w=w, h=h, depth=depth, colorspace=colorspace, addr=hex(addr), type=type )
def c_call(obj, method, *args, **kw): if hasattr(obj.__class__, '_api_type'): api_type = obj.__class__._api_type else: api_type = obj msg = formattable('Translating method {0}.{1}') logger.debug(msg.format(api_type, method)) method_name, c_method = get_c_method(api_type, method) try: init = kw.pop('__init') except KeyError: init = True if init: get_dll() # if objects are casted here and then # there is only their resource passed # there is a risk that GC will collect # them and __del__ will be called between # driving Imagick to SIGSEGV # lets keep references to them keep_, args_, should_lock = prepare_args(c_method, obj, args) msg = formattable('Calling {0}') logger.debug(msg.format(method_name)) if pypy and should_lock: __lock.acquire() result = c_method(*args_) if pypy and should_lock: __lock.release() del keep_ return handle_result( result, c_method.restype, args_, c_method.argtypes)
def test_repr(self): for x in [color.from_string(x) for x in ['red', 'green', 'blue', 'gray']]: rgba = x.get_rgba() float_re = '(\d(\.\d+)?)' regexp = (formattable('<Color\(r={0},g={0},b={0},a={0}\)'). format(float_re)) result = match(regexp, repr(x)) groups = tuple(float(v) for i, v in enumerate(result.groups()) if not i % 2) self.assertEqual(groups, rgba)
def cast(enum_, name): if isinstance(enum_, Enum): pass elif isinstance(enum_, string_types): enum_ = enum(enum_) else: msg = formattable('Cannot cast {0} to Enum') raise CastException(msg.format(enum_)) if isinstance(name, EnumValue): if name.enum != enum_: msg = formattable('Attempted to cast {0} to unrelated Enum {1}') raise CastException(msg.format(str(name), str(enum_))) return name elif isinstance(name, string_types): return enum_value(enum_, name) else: msg = formattable('Cannot cast {0} to EnumValue with Enum {1}') raise CastException(msg.format(str(name), str(enum_)))
def test_repr(self): for x in [ color.from_string(x) for x in ['red', 'green', 'blue', 'gray'] ]: rgba = x.get_rgba() float_re = '(\d(\.\d+)?)' regexp = (formattable('<Color\(r={0},g={0},b={0},a={0}\)').format( float_re)) result = match(regexp, repr(x)) groups = tuple( float(v) for i, v in enumerate(result.groups()) if not i % 2) self.assertEqual(groups, rgba)
def resource(self): """Get underlying C resource. You can use this method to get access to raw C struct that you can use with :term:`ctypes` calls directly. It can be useful when you want to perform custom operations. """ if self.__resource is None: tmpl = formattable('{0} already closed.') raise PystaciaException(tmpl.format(self.__class__.__name__)) return self.__resource
def find_in_path(path, name, abis, osname, factory): depends_path = join(path, 'depends.txt') if exists(depends_path): process_depends(depends_path, path, osname, factory) for abi in abis: template = dll_template(osname, abi) if not template: continue template = formattable(template) dll_path = join(path, template.format(name=name, abi=abi)) logger.debug('Trying: ' + dll_path) if exists(dll_path): logger.debug('Found: ' + dll_path) if osname == 'windows': old_path = getcwd() chdir(path) try: factory(dll_path) except: from sys import exc_info msg = formattable('Caught exception while loading ' '{0}: {1}. Rolling back') logger.debug(msg.format(dll_path, exc_info()[1])) if osname == 'windows': chdir(old_path) else: if osname == 'windows': chdir(old_path) return dll_path
def process_depends(depends_path, path, osname, factory): depends = open(depends_path) for line in depends: depname, depabi = line.split() template = formattable(dll_template(osname, depabi)) dll_path = join(path, template.format(name=depname, abi=depabi)) try: factory(dll_path) except: pass depends.close()
def _make_component(name): doc = formattable("""Set or get {0} channel information. The value ought to be a float between 0 and 1. :rtype: ``float`` or ``int`` """).format(name) def fget(self): return getattr(impl, 'get_' + name)(self) def fset(self, value): getattr(impl, 'set_' + name)(self, value) return property(fget, fset, doc=doc)
def __repr__(self): template = '<{class_}(w={w},h={h},{depth}bit'\ ',{colorspace},{type}) object at {addr}>' w, h = self.size depth, type = self.depth, self.type.name # @ReservedAssignment colorspace, addr = self.colorspace.name, id(self) class_ = self.__class__.__name__ return formattable(template).format(class_=class_, w=w, h=h, depth=depth, colorspace=colorspace, addr=hex(addr), type=type)
def cast(value): if isinstance(value, Color): return value elif isinstance(value, integer_types): return from_int24(value) elif isinstance(value, string_types): return from_string(value) elif value.__len__: if len(value) == 3: return from_rgb(*value) elif len(value) == 4: return from_rgba(*value) template = formattable('Cannot cast {0} to Color instance.') raise PystaciaException(template.format(value))
def read(filename, factory=None): """Read :class:`Image` from filename. :param filename: file name to read :type filename: ``str`` :param factory: Image subclass to use when instantiating objects :rtype: :class:`Image` Reads file, determines its format and returns an :class:`Image` representing it. You can optionally pass factory parameter to use alternative :class:`Image` subclass. >>> read('example.jpg') <Image(w=512,h=512,8bit,rgb,truecolor) object at 0x10302ee00L> """ if not exists(filename): template = formattable('No such file or directory: {0}') raise IOError((2, template.format(filename))) return io.read(filename, factory=factory)
def read(filename, factory=None): """Read :class:`Image` from filename. :param filename: file name to read :type filename: ``str`` :param factory: Image subclass to use when instantiating objects :rtype: :class:`Image` Reads file, determines its format and returns an :class:`Image` representing it. You can optionally pass factory parameter to use alternative :class:`Image` subclass. >>> read('example.jpg') <Image(w=512,h=512,8bit,rgb,truecolor) object at 0x10302ee00L> """ if not exists(filename): template = formattable("No such file or directory: {0}") raise IOError((2, template.format(filename))) return io.read(filename, factory=factory)
def setUp(self): self.environ = {'PYSTACIA_SKIP_SYSTEM': '1', 'PYSTACIA_SKIP_PACKAGE': '1'} self.olddir = getcwd() tmproot = self.tmproot = mkdtemp() for subdir in '', 'cdll', 'lib', 'dll', 'depends': path = join(tmproot, subdir) if not exists(path): mkdir(path) for osname in 'macos', 'linux', 'windows': for abi in 2, 1, None: template = formattable(dll_template(osname, abi)) libpath = join(path, template.format(name='Foo', abi=abi)) open(libpath, 'w').close() depends = open(join(tmproot, 'depends', 'depends.txt'), 'w') depends.write('Depends 18\n') depends.close()
def setUp(self): self.environ = { 'PYSTACIA_SKIP_SYSTEM': '1', 'PYSTACIA_SKIP_PACKAGE': '1' } self.olddir = getcwd() tmproot = self.tmproot = mkdtemp() for subdir in '', 'cdll', 'lib', 'dll', 'depends': path = join(tmproot, subdir) if not exists(path): mkdir(path) for osname in 'macos', 'linux', 'windows': for abi in 2, 1, None: template = formattable(dll_template(osname, abi)) libpath = join(path, template.format(name='Foo', abi=abi)) open(libpath, 'w').close() depends = open(join(tmproot, 'depends', 'depends.txt'), 'w') depends.write('Depends 18\n') depends.close()
def lookup(mnemonic, enum=None, version=None, throw=True): if enum: mnemonic = enum.cast(mnemonic) if not version: version = get_version() value = None for entry in data.get(mnemonic.enum.name, []): if entry['_version'] > version: break value = entry.get(mnemonic.name) if value is None and throw: template = "Enumeration '{enum}' cannot map mnemonic '{mnemonic}'" template = formattable(template) enum = mnemonic.enum.name mnemonic = mnemonic.name raise PystaciaException(template.format(enum=enum, mnemonic=mnemonic)) return value
def get_c_method(api_type, method, throw=True): type_data = metadata[api_type] method_name = type_data['format'](method) if not throw and not hasattr(get_dll(False), method_name): return False c_method = getattr(get_dll(False), method_name) msg = formattable('Annoting {0}') logger.debug(msg.format(method_name)) method_data = type_data['symbols'][method] argtypes = method_data[0] if 'arg' in type_data: argtypes = (type_data['arg'],) + argtypes c_method.argtypes = argtypes restype = type_data.get('result', None) if len(method_data) == 2: restype = method_data[1] c_method.restype = restype return method_name, c_method
def __repr__(self): template = formattable("pystacia.lazyenum.enum('{0}')") return template.format(self.name)
rose = lazy_imported('rose') wizard = lazy_imported('wizard') granite = lazy_imported('granite') netscape = lazy_imported('netscape') Image = lazy_imported('Image') composites = really_lazy_enum('composites') types = really_lazy_enum('types') filters = really_lazy_enum('filters') colorspaces = really_lazy_enum('colorspaces') compressions = really_lazy_enum('compressions') axes = really_lazy_enum('axes') __all__ = [ 'read', 'read_blob', 'read_raw', 'blank', 'checkerboard', 'lena', 'magick_logo', 'rose', 'wizard', 'granite', 'netscape', 'composites', 'types', 'filters', 'colorspaces', 'compressions', 'axes', 'color', 'colors', 'Image', 'registry'] from zope.deprecation import deprecated # @UnresolvedImport from pystacia.compat import formattable msg = formattable('Use pystacia.image.{0} instead') for symbol in set(__all__) - set(['color', 'colors', 'registry']): deprecated(symbol, msg.format(symbol))
def __repr__(self): template = ('<{class_}(r={0},g={1},b={2},a={3})' ' object at {id}>') kw = dict(id=hex(id(self)), class_=self.__class__.__name__) return formattable(template).format(*self.get_rgba(), **kw)