| 1 |
"""Load modules, classes, functions, and attributes by dotted package names. |
|---|
| 2 |
|
|---|
| 3 |
This work, including the source code, documentation |
|---|
| 4 |
and related data, is placed into the public domain. |
|---|
| 5 |
|
|---|
| 6 |
The orginal author is Robert Brewer. |
|---|
| 7 |
|
|---|
| 8 |
THIS SOFTWARE IS PROVIDED AS-IS, WITHOUT WARRANTY |
|---|
| 9 |
OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF |
|---|
| 10 |
MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE |
|---|
| 11 |
ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE |
|---|
| 12 |
RESULTING FROM THE USE, MODIFICATION, OR |
|---|
| 13 |
REDISTRIBUTION OF THIS SOFTWARE. |
|---|
| 14 |
|
|---|
| 15 |
Usage: |
|---|
| 16 |
|
|---|
| 17 |
desiredClass = xray.classes("myapp.stringtools.alwayslowercase", str) |
|---|
| 18 |
newInstance = desiredClass() |
|---|
| 19 |
|
|---|
| 20 |
or |
|---|
| 21 |
|
|---|
| 22 |
for f in startup_function_names: |
|---|
| 23 |
func_object = xray.functions(f) |
|---|
| 24 |
func_object() |
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
Rationale: |
|---|
| 28 |
|
|---|
| 29 |
I try to create systems that are configurable by deployers; mostly becase I |
|---|
| 30 |
hate it when an application forces me to use the database, filesystem, |
|---|
| 31 |
communication protocol, algorithm, or operating system du jour. Abstracting |
|---|
| 32 |
these basic components of an application is its own study (look up the Gang |
|---|
| 33 |
of Four "Strategy" pattern for a start); this is often accomplished with |
|---|
| 34 |
classes which share signatures, often via subclassing. However, the deployer |
|---|
| 35 |
then needs to specify the class they wish to use. This module gives them |
|---|
| 36 |
the ability to specify that with a single string (for example, in a text |
|---|
| 37 |
configuration file). |
|---|
| 38 |
|
|---|
| 39 |
""" |
|---|
| 40 |
|
|---|
| 41 |
import sys |
|---|
| 42 |
|
|---|
| 43 |
def modules(modulePath): |
|---|
| 44 |
"""Load a module and retrieve a reference to that module.""" |
|---|
| 45 |
try: |
|---|
| 46 |
aMod = sys.modules[modulePath] |
|---|
| 47 |
if aMod is None: |
|---|
| 48 |
raise KeyError |
|---|
| 49 |
except KeyError: |
|---|
| 50 |
|
|---|
| 51 |
aMod = __import__(modulePath, globals(), locals(), ['']) |
|---|
| 52 |
return aMod |
|---|
| 53 |
|
|---|
| 54 |
def attributes(fullAttributeName): |
|---|
| 55 |
"""Load a module and retrieve an attribute of that module.""" |
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
lastDot = fullAttributeName.rfind(u".") |
|---|
| 59 |
attrName = fullAttributeName[lastDot + 1:] |
|---|
| 60 |
modPath = fullAttributeName[:lastDot] |
|---|
| 61 |
|
|---|
| 62 |
aMod = modules(modPath) |
|---|
| 63 |
|
|---|
| 64 |
try: |
|---|
| 65 |
anAttr = getattr(aMod, attrName) |
|---|
| 66 |
except AttributeError: |
|---|
| 67 |
raise AttributeError("'%s' object has no attribute '%s'" |
|---|
| 68 |
% (modPath, attrName)) |
|---|
| 69 |
|
|---|
| 70 |
|
|---|
| 71 |
return anAttr |
|---|
| 72 |
|
|---|
| 73 |
def functions(fullFuncName): |
|---|
| 74 |
"""Load a module and retrieve a function object.""" |
|---|
| 75 |
|
|---|
| 76 |
aFunc = attributes(fullFuncName) |
|---|
| 77 |
|
|---|
| 78 |
|
|---|
| 79 |
if not callable(aFunc): |
|---|
| 80 |
raise TypeError(u"'%s' is not callable." % fullFuncName) |
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
return aFunc |
|---|
| 85 |
|
|---|
| 86 |
def classes(fullClassName, parentClass=None): |
|---|
| 87 |
"""Load a module and retrieve a class (NOT an instance). |
|---|
| 88 |
|
|---|
| 89 |
If the parentClass is supplied, className must be of parentClass |
|---|
| 90 |
or a subclass of parentClass (or None is returned). |
|---|
| 91 |
""" |
|---|
| 92 |
aClass = functions(fullClassName) |
|---|
| 93 |
|
|---|
| 94 |
|
|---|
| 95 |
if parentClass is not None: |
|---|
| 96 |
if not issubclass(aClass, parentClass): |
|---|
| 97 |
raise TypeError(u"'%s' is not a subclass of %s" % |
|---|
| 98 |
(fullClassName, parentClass.__name__)) |
|---|
| 99 |
|
|---|
| 100 |
|
|---|
| 101 |
return aClass |
|---|
| 102 |
|
|---|