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