#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
**library.py**
**Platform:**
Windows, Linux, Mac Os X.
**Description:**
Provides objects for C / C++ libraries binding.
**Others:**
"""
#**********************************************************************************************************************
#*** Future imports.
#**********************************************************************************************************************
from __future__ import unicode_literals
#**********************************************************************************************************************
#*** External imports.
#**********************************************************************************************************************
import ctypes
import os
import platform
#**********************************************************************************************************************
#*** Internal imports.
#**********************************************************************************************************************
import foundations.common
import foundations.dataStructures
import foundations.exceptions
import foundations.verbose
#**********************************************************************************************************************
#*** Module attributes.
#**********************************************************************************************************************
__author__ = "Thomas Mansencal"
__copyright__ = "Copyright (C) 2008 - 2014 - Thomas Mansencal"
__license__ = "GPL V3.0 - http://www.gnu.org/licenses/"
__maintainer__ = "Thomas Mansencal"
__email__ = "[email protected]"
__status__ = "Production"
__all__ = ["LOGGER", "LibraryHook", "Library"]
LOGGER = foundations.verbose.installLogger()
#**********************************************************************************************************************
#*** Module classes and definitions.
#**********************************************************************************************************************
[docs]class LibraryHook(foundations.dataStructures.Structure):
"""
Defines a library hook used by the :class:`Library` class to bind target library functions.
"""
def __init__(self, **kwargs):
"""
Initializes the class.
Usage::
LibraryHook(name="FreeImage_GetVersion", argumentsTypes=None, returnValue=ctypes.c_char_p)
:param name: Name of the target library function to bind.
:type name: unicode
:param argumentsTypes: Required function arguments type (Refer to Python `ctypes - 15.17.1.7
<http://docs.python.org/library/ctypes.html#specifying-the-required-argument-types-function-prototypes>`_
module for more informations). ( List )
:param returnValue: Function return type (Refer to Python `ctypes - 15.17.1.8
<http://docs.python.org/library/ctypes.html#return-types>`_ module for more informations). ( Object )
"""
LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
foundations.dataStructures.Structure.__init__(self, **kwargs)
[docs]class Library(object):
"""
| Defines methods to bind a C / C++ Library.
| The class is a singleton and will bind only one time a given library.
Each unique library instance is stored in :attr:`Library.instances` attribute
and get returned if the library is requested again through a new instantiation.
"""
__instances = {}
"""
:param __instances: Libraries instances.
:type __instances: dict
"""
callback = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p)
"""
:param callback: callback.
:type callback: ctypes.CFUNCTYPE
"""
# Oncilla: Statement commented by auto-documentation process:
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.LibraryInstantiationError)
def __new__(cls, *args, **kwargs):
"""
Constructor of the class.
:param \*args: Arguments.
:type \*args: \*
:param \*\*kwargs: Keywords arguments.
:type \*\*kwargs: \*\*
:return: Class instance.
:rtype: Library
"""
path = foundations.common.getFirstItem(args)
if foundations.common.pathExists(path):
if not path in cls._Library__instances:
cls._Library__instances[path] = object.__new__(cls)
return cls._Library__instances[path]
else:
raise foundations.exceptions.LibraryInstantiationError(
"{0} | '{1}' library path doesn't exists!".format(cls.__class__.__name__, path))
# Oncilla: Statement commented by auto-documentation process:
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.LibraryInitializationError)
def __init__(self, path, functions=None, bindLibrary=True):
"""
Initializes the class.
Usage::
>>> import ctypes
>>> path = "FreeImage.dll"
>>> functions = (LibraryHook(name="FreeImage_GetVersion", argumentsTypes=None, returnValue=ctypes.c_char_p),)
>>> library = Library(path, functions)
>>> library.FreeImage_GetVersion()
'3.15.1'
:param path: Library path.
:type path: unicode
:param functions: Binding functions list.
:type functions: tuple
:param bindLibrary: Library will be binded on initialization.
:type bindLibrary: bool
"""
if hasattr(self.instances[path], "_Library__initialized"):
return
LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__))
# --- Setting class attributes. ---
self.__initialized = True
self.__path = None
self.path = path
self.__functions = None
self.functions = functions
self.__library = None
if platform.system() == "Windows" or platform.system() == "Microsoft":
loadingFunction = ctypes.windll
else:
loadingFunction = ctypes.cdll
if self.path:
self.__library = loadingFunction.LoadLibrary(path)
else:
raise foundations.exceptions.LibraryInitializationError("{0} | '{1}' library not found!".format(
self.__class__.__name__, path))
bindLibrary and self.bindLibrary()
#******************************************************************************************************************
#*** Attributes properties.
#******************************************************************************************************************
@property
def instances(self):
"""
Property for **self.__instances** attribute.
:return: self.__instances.
:rtype: WeakValueDictionary
"""
return self.__instances
@instances.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def instances(self, value):
"""
Setter for **self.__instances** attribute.
:param value: Attribute value.
:type value: WeakValueDictionary
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "instances"))
@instances.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def instances(self):
"""
Deleter for **self.__instances** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "instances"))
@property
def initialized(self):
"""
Property for **self.__initialized** attribute.
:return: self.__initialized.
:rtype: unicode
"""
return self.__initialized
@initialized.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
def initialized(self, value):
"""
Setter for **self.__initialized** attribute.
:param value: Attribute value.
:type value: unicode
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "initialized"))
@initialized.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def initialized(self):
"""
Deleter for **self.__initialized** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "initialized"))
@property
def path(self):
"""
Property for **self.__path** attribute.
:return: self.__path.
:rtype: unicode
"""
return self.__path
@path.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(AssertionError)
def path(self, value):
"""
Setter for **self.__path** attribute.
:param value: Attribute value.
:type value: unicode
"""
if value is not None:
assert type(value) is unicode, "'{0}' attribute: '{1}' type is not 'unicode'!".format(
"path", value)
assert os.path.exists(value), "'{0}' attribute: '{1}' file doesn't exists!".format("path", value)
self.__path = value
@path.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def path(self):
"""
Deleter for **self.__path** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "path"))
@property
def functions(self):
"""
Property for **self.__functions** attribute.
:return: self.__functions.
:rtype: tuple
"""
return self.__functions
@functions.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(AssertionError)
def functions(self, value):
"""
Setter for **self.__functions** attribute.
:param value: Attribute value.
:type value: tuple
"""
if value is not None:
assert type(value) is tuple, "'{0}' attribute: '{1}' type is not 'tuple'!".format("functions", value)
for element in value:
assert type(element) is LibraryHook, "'{0}' attribute: '{1}' type is not 'LibraryHook'!".format(
"functions", element)
self.__functions = value
@functions.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def functions(self):
"""
Deleter for **self.__functions** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "functions"))
@property
def library(self):
"""
Property for **self.__library** attribute.
:return: self.__library.
:rtype: object
"""
return self.__library
@library.setter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(AssertionError)
def library(self, value):
"""
Setter for **self.__library** attribute.
:param value: Attribute value.
:type value: object
"""
self.__library = value
@library.deleter
# Oncilla: Statement commented by auto-documentation process: @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError)
[docs] def library(self):
"""
Deleter for **self.__library** attribute.
"""
raise foundations.exceptions.ProgrammingError(
"{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "library"))
#******************************************************************************************************************
#*** Class methods.
#******************************************************************************************************************
[docs] def bindFunction(self, function):
"""
Binds given function to a class object attribute.
Usage::
>>> import ctypes
>>> path = "FreeImage.dll"
>>> function = LibraryHook(name="FreeImage_GetVersion", argumentsTypes=None, returnValue=ctypes.c_char_p)
>>> library = Library(path, bindLibrary=False)
>>> library.bindFunction(function)
True
>>> library.FreeImage_GetVersion()
'3.15.1'
:param function: Function to bind.
:type function: LibraryHook
:return: Method success.
:rtype: bool
"""
LOGGER.debug("> Binding '{0}' library '{1}' function.".format(self.__class__.__name__, function.name))
functionObject = getattr(self.__library, function.name)
setattr(self, function.name, functionObject)
if function.argumentsTypes:
functionObject.argtypes = function.argumentsTypes
if function.returnValue:
functionObject.restype = function.returnValue
return True
[docs] def bindLibrary(self):
"""
Binds the Library using functions registered in the **self.__functions** attribute.
Usage::
>>> import ctypes
>>> path = "FreeImage.dll"
>>> functions = (LibraryHook(name="FreeImage_GetVersion", argumentsTypes=None, returnValue=ctypes.c_char_p),)
>>> library = Library(path, functions, bindLibrary=False)
>>> library.bindLibrary()
True
>>> library.FreeImage_GetVersion()
'3.15.1'
:return: Method success.
:rtype: bool
"""
if self.__functions:
for function in self.__functions:
self.bindFunction(function)
return True