*** empty log message ***
[pli.git] / pli / importutils.py
blob716da8aa74ceb38dbe2fe228c99cc92a6ca7b477
1 #=======================================================================
3 __version__ = '''0.0.19'''
4 __sub_version__ = '''20061124145335'''
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,\
148 disable_file='disabled.txt',\
149 err_names=None,\
150 disabled_packages=None,\
151 notimportable=None,\
152 ignore_modules=(),\
153 name_prefix=None):
154 '''\
156 This is an import iterator.
157 this will import all packages/modules under a given path (package_dir),
158 not containing the "disabled.txt" file (disable_file), one per iteration, and
159 will return a tuple containing the module name and its object.
161 optional side-effects:
162 err_names : if given, will contain the names of modules that
163 generated ImportError.
164 disabled_packages : if given, will contain the names of disabled modules
165 (e.g. modules that contained the "disabled.txt" file
166 (see above)).
167 NOTE: the above two parameters MUST either be of list type or None.
169 # some sanity checks...
170 if err_names != None and type(err_names) != list:
171 raise TypeError, 'err_names must either be of list type or None (got type "%s").' % type(err_names)
172 # start the work...
173 for mod_name in packageiter(package_dir, disable_file, disabled_packages, notimportable, ignore_modules):
174 mod_name, module = _load_module(package_dir, mod_name, name_prefix)
175 if module != None:
176 yield mod_name, module
177 elif err_names != None:
178 err_names += [mod_name]
181 #-------------------------------------------importdependspackagesiter---
182 # TODO make this a generic dependency checker (objutils ???)
183 def importdependspackagesiter(package_dir,\
184 disable_file='disabled.txt',\
185 dependency_file='depends.txt',\
186 err_names=None,\
187 disabled_packages=None,\
188 notimportable=None, \
189 ignore_modules=(),\
190 name_prefix=None):
191 '''\
193 This will import the modules in order of dependency.
195 # helper function...
196 def _loaddependsiter(path, name, wait_lst, loaded_packages, err_names=None):
199 if name not in loaded_packages:
200 name, mod = _load_module(path, name, name_prefix)
201 if mod == None and err_names != None:
202 err_names += [name]
203 else:
204 loaded_packages[name] = mod
205 yield name, mod
206 dependees = wait_lst.pop(name, [])
207 for name in dependees:
208 # if module does not need anything else load
209 if [ m for m in wait_lst.values() if name in m ]:
210 # skip...
211 continue
212 for n, m in _loaddependsiter(path, name, wait_lst, loaded_packages, err_names):
213 yield n, m
214 # start the work...
215 loaded_packages = {}
216 wait_lst = {}
217 for mod_name in packageiter(package_dir, disable_file, disabled_packages, notimportable, ignore_modules):
218 # get deps...
219 deps = getpackagedepends(package_dir, mod_name, dependency_file)
220 # if all dependencies are loaded load package...
221 needs = [ i for i in deps if i not in loaded_packages ]
222 # check if we depend on a faulty package...
223 err_needs = [ i for i in needs if i in err_names ]
224 if err_names != None and err_names:
225 raise ImportDependencyError, 'failure in dependencies (failed: %s).' % err_names
226 if needs:
227 # wait list...
228 for mod in needs:
229 if mod in wait_lst:
230 wait_lst[mod] += [mod_name]
231 else:
232 wait_lst[mod] = [mod_name]
233 else:
234 for n, m in _loaddependsiter(package_dir, mod_name, wait_lst, loaded_packages, err_names):
235 yield n, m
236 # check if the remainig data in wait list is looping deps...
237 if wait_lst:
238 # TODO do a more thorough check...
239 raise ImportDependencyError, 'cyclic or unresolved demendencies in: %s.' % wait_lst
243 #=======================================================================
244 # vim:set ts=4 sw=4 nowrap :