| | 169 | |
|---|
| | 170 | |
|---|
| | 171 | def path_moduleinfo(path, allowmtypes=(imp.PY_SOURCE, imp.PY_COMPILED)): |
|---|
| | 172 | """Get extended module info for `path` |
|---|
| | 173 | |
|---|
| | 174 | If path identifies a legitemate python file (as determined by |
|---|
| | 175 | inspect.getmoduleinfo) then return a 4 element tuple: |
|---|
| | 176 | |
|---|
| | 177 | (absoloute *parent* of `path`, modname, ext, mode, mtype) |
|---|
| | 178 | |
|---|
| | 179 | modname, ext, mode and mtype are obtained using `inspect.getmoduleinfo` |
|---|
| | 180 | Note that for a legitemate path:: |
|---|
| | 181 | |
|---|
| | 182 | path_moduleinfo(path)[1:] == inspect.moduleinfo(path) |
|---|
| | 183 | |
|---|
| | 184 | """ |
|---|
| | 185 | v = inspect.getmoduleinfo(path) |
|---|
| | 186 | if not v: |
|---|
| | 187 | return |
|---|
| | 188 | modname, ext, mode, mtype = v |
|---|
| | 189 | if mtype not in allowmtypes: |
|---|
| | 190 | return None |
|---|
| | 191 | abs_path = dirname(normpath(abspath(expanduser(path)))) |
|---|
| | 192 | return (abs_path, modname, ext, mode, mtype) |
|---|
| | 193 | |
|---|
| | 194 | # from distutils.util, because fools at debian think its a good idea to remove |
|---|
| | 195 | # distutils from the standard distribution. |
|---|
| | 196 | def convert_path (pathname): |
|---|
| | 197 | """Return 'pathname' as a name that will work on the native filesystem, |
|---|
| | 198 | i.e. split it on '/' and put it back together again using the current |
|---|
| | 199 | directory separator. Needed because filenames in the setup script are |
|---|
| | 200 | always supplied in Unix style, and have to be converted to the local |
|---|
| | 201 | convention before we can actually use them in the filesystem. Raises |
|---|
| | 202 | ValueError on non-Unix-ish systems if 'pathname' either starts or |
|---|
| | 203 | ends with a slash. |
|---|
| | 204 | """ |
|---|
| | 205 | if os.sep == '/': |
|---|
| | 206 | return pathname |
|---|
| | 207 | if not pathname: |
|---|
| | 208 | return pathname |
|---|
| | 209 | if pathname[0] == '/': |
|---|
| | 210 | raise ValueError, "path '%s' cannot be absolute" % pathname |
|---|
| | 211 | if pathname[-1] == '/': |
|---|
| | 212 | raise ValueError, "path '%s' cannot end with '/'" % pathname |
|---|
| | 213 | |
|---|
| | 214 | paths = string.split(pathname, '/') |
|---|
| | 215 | while '.' in paths: |
|---|
| | 216 | paths.remove('.') |
|---|
| | 217 | if not paths: |
|---|
| | 218 | return os.curdir |
|---|
| | 219 | return apply(os.path.join, paths) |
|---|
| | 220 | |
|---|
| | 221 | # find_packages from setuptools |
|---|
| | 222 | def find_packages(where='.', exclude=()): |
|---|
| | 223 | """Return a list all Python packages found within directory 'where' |
|---|
| | 224 | |
|---|
| | 225 | 'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it |
|---|
| | 226 | will be converted to the appropriate local path syntax. 'exclude' is a |
|---|
| | 227 | sequence of package names to exclude; '*' can be used as a wildcard in the |
|---|
| | 228 | names, such that 'foo.*' will exclude all subpackages of 'foo' (but not |
|---|
| | 229 | 'foo' itself). |
|---|
| | 230 | """ |
|---|
| | 231 | out = [] |
|---|
| | 232 | stack=[(convert_path(where), '')] |
|---|
| | 233 | while stack: |
|---|
| | 234 | where,prefix = stack.pop(0) |
|---|
| | 235 | for name in os.listdir(where): |
|---|
| | 236 | fn = os.path.join(where,name) |
|---|
| | 237 | if ('.' not in name and os.path.isdir(fn) and |
|---|
| | 238 | os.path.isfile(os.path.join(fn,'__init__.py')) |
|---|
| | 239 | ): |
|---|
| | 240 | out.append(prefix+name); stack.append((fn,prefix+name+'.')) |
|---|
| | 241 | for pat in list(exclude)+['ez_setup']: |
|---|
| | 242 | from fnmatch import fnmatchcase |
|---|
| | 243 | out = [item for item in out if not fnmatchcase(item,pat)] |
|---|
| | 244 | return out |
|---|
| | 245 | |
|---|
| | 246 | |
|---|
| | 247 | def find_top_packages(where='.'): |
|---|
| | 248 | tops={} |
|---|
| | 249 | out = [] |
|---|
| | 250 | stack=[convert_path(where)] |
|---|
| | 251 | while stack: |
|---|
| | 252 | where = stack.pop(0) |
|---|
| | 253 | for name in os.listdir(where): |
|---|
| | 254 | fn = join(where,name) |
|---|
| | 255 | if isdir(fn): |
|---|
| | 256 | if ('.' not in name and isfile(join(fn,'__init__.py'))): |
|---|
| | 257 | tops.setdefault(where, {})[name]=fn |
|---|
| | 258 | else: |
|---|
| | 259 | stack.append(fn) |
|---|
| | 260 | return tops |
|---|
| 352 | | # main entry point wrappers |
|---|
| | 445 | # main entry point wrappers and helpers |
|---|
| | 446 | |
|---|
| | 447 | def get_module_code(mod_name): |
|---|
| | 448 | """like runpy.run_module but dont run.""" |
|---|
| | 449 | loader = get_loader(mod_name) |
|---|
| | 450 | if loader is None: |
|---|
| | 451 | raise ImportError("No module named " + mod_name) |
|---|
| | 452 | code = loader.get_code(mod_name) |
|---|
| | 453 | if code is None: |
|---|
| | 454 | raise ImportError("No code object available for " + mod_name) |
|---|
| | 455 | filename = _get_filename(loader, mod_name) |
|---|
| | 456 | return filename, code, loader |
|---|
| | 457 | |
|---|
| | 458 | |
|---|
| | 459 | def _path_adder(add_path, added, p, e=None, loginfo=log.info): |
|---|
| | 460 | if e is None: |
|---|
| | 461 | save_path = (p, sys.path[:]) |
|---|
| | 462 | loginfo and loginfo('Adding path: %s' % p) |
|---|
| | 463 | add_path(p) |
|---|
| | 464 | added.add(p) |
|---|
| | 465 | return save_path |
|---|
| | 466 | else: |
|---|
| | 467 | p, save_path = p |
|---|
| | 468 | loginfo and loginfo('Undoing add path: %s' % p) |
|---|
| | 469 | added.remove(p) |
|---|
| | 470 | sys.path[:] = save_path[:] |
|---|
| | 471 | |
|---|
| | 472 | |
|---|
| | 473 | def pth_addeggs(basedir, add_pathitem, allow_links=True): |
|---|
| | 474 | pth = glob(abs_normpath(basedir, '-py'+sys.version[:3]+'*.egg')) |
|---|
| | 475 | if allow_links: |
|---|
| | 476 | pth[0:0] = glob(abs_normpath(basedir, '*.egg-link')) |
|---|
| | 477 | for p in pth: |
|---|
| | 478 | add_pathitem(p) |
|---|
| | 479 | return pth |
|---|
| | 480 | |
|---|
| | 481 | |
|---|
| | 482 | def pth_import(modulename, fallback_paths, promiscuous_eggpaths=True, |
|---|
| | 483 | code_getter=get_module_code, path_adder=None, |
|---|
| | 484 | projectversion=False, pth_filename='pyrun.pth', loginfo=log.info): |
|---|
| | 485 | """Attempt to obtain the module code for modulename. |
|---|
| | 486 | |
|---|
| | 487 | The following heuristic is used to attempt the import from a variety of |
|---|
| | 488 | fallback locations **if the initial get fails**: |
|---|
| | 489 | |
|---|
| | 490 | If any step is successful break out immediately and compute the |
|---|
| | 491 | return path. |
|---|
| | 492 | |
|---|
| | 493 | 1. Make the first attempt without modifying the path. |
|---|
| | 494 | |
|---|
| | 495 | 2. If pth_filename file exists in fallback_paths[0] then extend the |
|---|
| | 496 | path, via ``site.addsitedir`` with the directories listed in that file. |
|---|
| | 497 | |
|---|
| | 498 | 3. Attempt again. |
|---|
| | 499 | |
|---|
| | 500 | 4. If the import failed AND projectversion is not False, |
|---|
| | 501 | contstruct a glob |
|---|
| | 502 | pattern like this:: |
|---|
| | 503 | |
|---|
| | 504 | abs_normpath(fallback_path_item, |
|---|
| | 505 | projectversion+'-py'+sys.version[:3]+'*.egg') |
|---|
| | 506 | |
|---|
| | 507 | and sort the results **in lexically ascending order**. If the result is non |
|---|
| | 508 | empty add it to the path and try again |
|---|
| | 509 | |
|---|
| | 510 | repeat 2., 3., 4. for every remaining path in fallback_paths |
|---|
| | 511 | |
|---|
| | 512 | """ |
|---|
| | 513 | if not path_adder: |
|---|
| | 514 | import site |
|---|
| | 515 | # don't want to muck around with site._init_pathinfo and known_paths |
|---|
| | 516 | added = set([]) |
|---|
| | 517 | def path_adder(p, e=None): |
|---|
| | 518 | return _path_adder(site.addsitedir, added, p, e=e, loginfo=loginfo) |
|---|
| | 519 | |
|---|
| | 520 | try: |
|---|
| | 521 | modcode = code_getter(modulename) |
|---|
| | 522 | return modcode |
|---|
| | 523 | except ImportError: |
|---|
| | 524 | pass |
|---|
| | 525 | for p in fallback_paths: |
|---|
| | 526 | if is_zipfile(p) or isdir(p): |
|---|
| | 527 | adder_key = path_adder(p) |
|---|
| | 528 | try: |
|---|
| | 529 | modcode = code_getter(modulename) |
|---|
| | 530 | return modcode |
|---|
| | 531 | except ImportError, e: |
|---|
| | 532 | path_adder(adder_key, e) |
|---|
| | 533 | if not projectversion and isdir(p): |
|---|
| | 534 | continue |
|---|
| | 535 | # glob patterns only apply when a projectversion is supplied and |
|---|
| | 536 | # the fallback path is a directory. |
|---|
| | 537 | pattern = abs_normpath(p, projectversion+'-py'+sys.version[:3]+'*.egg') |
|---|
| | 538 | loginfo and loginfo('egg glob pattern: %s', pattern) |
|---|
| | 539 | projecteggs = glob(pattern) |
|---|
| | 540 | if not projecteggs or eggpaths: |
|---|
| | 541 | continue |
|---|
| | 542 | projecteggs.sort() |
|---|
| | 543 | adder_key = path_adder(projecteggs[-1]) |
|---|
| | 544 | try: |
|---|
| | 545 | modcode = code_getter(modulename) |
|---|
| | 546 | return modcode |
|---|
| | 547 | except ImportError, e: |
|---|
| | 548 | path_adder(adder_key, e) |
|---|
| | 549 | continue |
|---|
| | 550 | raise ImportError('No module named '+modulename) |
|---|
| | 551 | |
|---|
| | 552 | |
|---|
| | 553 | def module_enter(modulename, argv=None, ns=None, program_name=None): |
|---|
| | 554 | |
|---|
| | 555 | if type(modulename) == types.ModuleType: |
|---|
| | 556 | mod=modulename |
|---|
| | 557 | modulename=mod.__name__ |
|---|
| | 558 | else: |
|---|
| | 559 | ns=ns or globals() |
|---|
| | 560 | mod = __import__( |
|---|
| | 561 | modulename, ns, locals(), |
|---|
| | 562 | modulename.split('.')[-1]) |
|---|
| | 563 | if program_name is None: |
|---|
| | 564 | program_name = modulename |
|---|
| | 565 | log=logging.getLogger(modulename) |
|---|