*** empty log message ***
[pli.git] / pli / importutils.py
blob26e4c950333e5133c3421ec90469367092d3c473
1 #=======================================================================
3 __version__ = '''0.0.19'''
4 __sub_version__ = '''20040501170358'''
5 __copyright__ = '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
10 from __future__ import generators
12 import os
13 import sys
14 import imp
17 #-----------------------------------------------------------------------
19 PY_SUFFIXES = imp.get_suffixes()
22 #-----------------------------------------------------------------------
23 #-----------------------------------------------ImportDependencyError---
24 class ImportDependencyError(Exception):
25 '''
26 '''
30 #-----------------------------------------------------------------------
31 #--------------------------------------------------------_load_module---
32 # NOTE: this is a potential log/aspect point...
33 def _load_module(package_dir, mod_name, name_prefix=None):
34 '''
35 '''
36 mod_dat = ()
37 mod_legal_name = name_prefix in (None, '') \
38 and mod_name or name_prefix + '.' + mod_name
39 # import...
40 try:
41 # restrict the import path to avoid uncontrolled imports...
42 mod_dat = imp.find_module(mod_name, [package_dir])
43 # check if the module is loaded...
44 if mod_legal_name in sys.modules:
45 return mod_name, sys.modules[mod_name]
46 # load the module...
47 ## module = imp.load_module(mod_name, *mod_dat)
48 module = imp.load_module(mod_legal_name, *mod_dat)
49 # cleanup...
50 if len(mod_dat) > 1 and mod_dat[0] != None:
51 mod_dat[0].close()
52 return mod_name, module
53 except ImportError:
54 # cleanup... (is this needed???)
55 if len(mod_dat) > 1 and mod_dat[0] != None:
56 mod_dat[0].close()
57 return mod_name, None
60 #--------------------------------------------------ispythonimportable---
61 def ispythonimportable(package_dir, name):
62 '''
63 this will test if the module name is importable from package_dir.
64 '''
65 mod_path = os.path.join(package_dir, name)
66 # is directory and contains __init__.py?
67 if os.path.isdir(mod_path) and \
68 True in [ os.path.exists(os.path.join(mod_path, '__init__' + ext[0])) \
69 for ext in PY_SUFFIXES ]:
70 return True
71 else:
72 # is file -> is .py[cod]...
73 return True in [ os.path.exists(os.path.join(mod_path, ext[0])) \
74 for ext in PY_SUFFIXES ]
75 return False
78 #---------------------------------------------------------packageiter---
79 def packageiter(package_dir, disable_file='disabled.txt', \
80 disabled_packages=None, notimportable=None, ignore_modules=()):
81 '''\
83 This will return importable (and enabled) package names (without importing).
84 this will return the names of all packages/modules under a given path (package_dir),
85 not containing the "disabled.txt" file (disable_file), one per iteration.
87 optional side-effects:
88 disabled_packages : if given, will contain the names of disabled modules
89 (e.g. modules that contained the "disabled.txt" file
90 (see above)).
91 NOTE: the above two parameters MUST either be of list type or None.
92 '''
93 # some sanity checks...
94 if disabled_packages != None and type(disabled_packages) != list:
95 raise TypeError, 'disabled_packages must either be of list type or None (got type "%s").' % type(disabled_packages)
96 if notimportable != None and type(notimportable) != list:
97 raise TypeError, 'notimportable must either be of list type or None (got type "%s").' % type(notimportable)
98 # start the work...
99 loaded_packages = {}
100 for mod in os.listdir(package_dir):
101 ## # skip __init__.*
102 ## if mod.startswith('__init__.'):
103 ## continue
104 # get mod name...
105 mod_name = mod.split('.', 1)[0]
107 if mod_name in ignore_modules:
108 continue
109 # include each name only once...
110 if mod_name not in loaded_packages.keys() + ['']:
111 # skip disabled plugins...
112 if os.path.exists(os.path.join(package_dir, mod_name, disable_file)):
113 if disabled_packages != None:
114 disabled_packages += [mod_name]
115 continue
116 if ispythonimportable(package_dir, mod_name):
117 yield mod_name
118 elif notimportable != None:
119 notimportable += [mod_name]
120 ## yield mod_name
123 #---------------------------------------------------getpackagedepends---
124 def getpackagedepends(package_dir, mod_name, dependency_file='depends.txt', forbidden_chars=' -+=&*()^%$#@!~`,.'):
127 # see if the dependency file exists...
128 if not os.path.exists(os.path.join(package_dir, mod_name, dependency_file)):
129 return []
130 dep_file = open(os.path.join(package_dir, mod_name, dependency_file))
131 # parse dependency file...
132 deps = [ s.lstrip().split('#')[0].rstrip() \
133 for s in dep_file \
134 if s.split('#')[0].lstrip() != '' ]
135 dep_file.close()
136 # check for correctness...
137 if [ s for s in deps if True in [ c in s for c in forbidden_chars ] ]:
138 raise ImportDependencyError, 'dependency file format error.'
139 return deps
143 #-----------------------------------------------------------------------
144 #---------------------------------------------------importpackageiter---
145 # TODO make this return more specific error data (e.g. name, exception,
146 # traceback...).
147 def importpackageiter(package_dir, disable_file='disabled.txt', err_names=None,\
148 disabled_packages=None, notimportable=None, \
149 ignore_modules=(), name_prefix=None):
150 '''\
152 This is an import iterator.
153 this will import all packages/modules under a given path (package_dir),
154 not containing the "disabled.txt" file (disable_file), one per iteration, and
155 will return a tuple containing the module name and its object.
157 optional side-effects:
158 err_names : if given, will contain the names of modules that
159 generated ImportError.
160 disabled_packages : if given, will contain the names of disabled modules
161 (e.g. modules that contained the "disabled.txt" file
162 (see above)).
163 NOTE: the above two parameters MUST either be of list type or None.
165 # some sanity checks...
166 if err_names != None and type(err_names) != list:
167 raise TypeError, 'err_names must either be of list type or None (got type "%s").' % type(err_names)
168 # start the work...
169 for mod_name in packageiter(package_dir, disable_file, disabled_packages, notimportable, ignore_modules):
170 mod_name, module = _load_module(package_dir, mod_name, name_prefix)
171 if module != None:
172 yield mod_name, module
173 elif err_names != None:
174 err_names += [mod_name]
178 #-------------------------------------------importdependspackagesiter---
179 # TODO make this a generic dependency checker (objutils ???)
180 def importdependspackagesiter(package_dir, disable_file='disabled.txt',\
181 dependency_file='depends.txt', err_names=None,\
182 disabled_packages=None, notimportable=None, \
183 ignore_modules=(), name_prefix=None):
184 '''\
186 This will import the modules in order of dependency.
188 # helper function...
189 def _loaddependsiter(path, name, wait_lst, loaded_packages, err_names=None):
192 if name not in loaded_packages:
193 name, mod = _load_module(path, name, name_prefix)
194 if mod == None and err_names != None:
195 err_names += [name]
196 else:
197 loaded_packages[name] = mod
198 yield name, mod
199 dependees = wait_lst.pop(name, [])
200 for name in dependees:
201 # if module does not need anything else load
202 if [ m for m in wait_lst.values() if name in m ]:
203 # skip...
204 continue
205 for n, m in _loaddependsiter(path, name, wait_lst, loaded_packages, err_names):
206 yield n, m
207 # start the work...
208 loaded_packages = {}
209 wait_lst = {}
210 for mod_name in packageiter(package_dir, disable_file, disabled_packages, notimportable, ignore_modules):
211 # get deps...
212 deps = getpackagedepends(package_dir, mod_name, dependency_file)
213 # if all dependencies are loaded load package...
214 needs = [ i for i in deps if i not in loaded_packages ]
215 # check if we depend on a faulty package...
216 err_needs = [ i for i in needs if i in err_names ]
217 if err_names != None and err_names:
218 raise ImportDependencyError, 'failure in dependencies (failed: %s).' % err_names
219 if needs:
220 # wait list...
221 for mod in needs:
222 if mod in wait_lst:
223 wait_lst[mod] += [mod_name]
224 else:
225 wait_lst[mod] = [mod_name]
226 else:
227 for n, m in _loaddependsiter(package_dir, mod_name, wait_lst, loaded_packages, err_names):
228 yield n, m
229 # check if the remainig data in wait list is looping deps...
230 if wait_lst:
231 # TODO do a more thorough check...
232 raise ImportDependencyError, 'cyclic or unresolved demendencies in: %s.' % wait_lst
236 #=======================================================================
237 # vim:set ts=4 sw=4 nowrap :