# coding: utf-8
#
# Copyright (c) 2012-2013, Niklas Rosenstein
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be interpreted
# as representing official policies, either expressed or implied, of
# the FreeBSD Project.
r"""
c4dtools.library
~~~~~~~~~~~~~~~~
This module implements functionality for making it possible for
plugins to interface with each other, i.e. one plugin may depend
on the functionality of another plugin (even developed from different
developers).
A Python plugin may create a Library class that will be (automatically)
registered to the c4dtools.library mmodule. This library can then be
loaded from another plugin to establish a Python-level communication
between these plugins. As the plugin initialization order can not be
controlled from Python, and there may also exist a circular reference
to libraries of two or more plugins, references to libraries are always
lazy and loaded on-request.
Example library:
.. code-block:: python
import c4dtools
class MyLibrary(c4dtools.library.Library):
class Meta:
# Here comes meta information.
pass
def on_create(self):
print "MyLibrary has been created."
def on_install(self, name):
print "MyLibrary has been installed under the name %s." % name
def get_stuff(self):
return "This is from MyLibrary!"
Nothing more needs to be done. The library is automatically registered
with the name ``'MyLibrary'``. The name the library is registered with
can be changed by defining `name='libname'` in the Meta section. When
registration should be prevented, one needs to define ``abstract=True``
in the Meta section.
This library can now be loaded from another Python unit.
.. code-block:: python
import c4dtools
MyLibrary = c4dtools.load_library('MyLibrary')
print MyLibrary.get_stuff()
"""
import copy
import types
from c4dtools.utils import clsname
from c4dtools.helpers import Attributor
[docs]class LibraryNotFound(Exception):
r"""
This exception is thrown when a requested library was not found.
"""
[docs]class Library(object):
r"""
This is the base class for library objects. It's metaclass
``LibraryMeta`` will automatically turn methods into classmethods
and will disallow the instantiaton of the class.
"""
__metaclass__ = LibraryMeta
[docs]class LazyLibrary(object):
r"""
This class is representing a lazy reference to a library. The actual
library will be loaded on the first request.
"""
def __init__(self, libname):
super(LazyLibrary, self).__init__()
self.__libname = libname
@property
def __library(self):
self.__library = load_library(self.__libname)
# Write each value from the loaded library into the
# LazyLibrary so __getattr__ does not need to be called
# anymore.
for attrname in dir(self.__library):
setattr(self, attrname, getattr(self.__library, attrname))
def __getattr__(self, name):
return getattr(self.__library, name)
[docs]def load_library(name, lazy=False):
r"""
Load a library. Returns a :class:`Library` instance unless *lazy*
does not evaluate to True. If *lazy* is True, a :class:`LazyLibrary`
instance will be returned.
"""
if lazy:
return LazyLibrary(name)
else:
return LibraryMeta.get_library(name)