| | 156 | |
|---|
| | 157 | # This is taken directly from setuptools pkg_resources |
|---|
| | 158 | pkg_resources_EGG_NAME = re.compile( |
|---|
| | 159 | r"(?P<name>[^-]+)" |
|---|
| | 160 | r"( -(?P<ver>[^-]+) (-py(?P<pyver>[^-]+) (-(?P<plat>.+))? )? )?", |
|---|
| | 161 | re.VERBOSE | re.IGNORECASE |
|---|
| | 162 | ).match |
|---|
| | 163 | |
|---|
| | 164 | pkg_resources_component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) |
|---|
| | 165 | pkg_resources_ver_replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get |
|---|
| | 166 | |
|---|
| | 167 | def pkg_resources_parse_version_parts(s): |
|---|
| | 168 | for part in pkg_resources_component_re.split(s): |
|---|
| | 169 | part = pkg_resources_ver_replace(part,part) |
|---|
| | 170 | if not part or part=='.': |
|---|
| | 171 | continue |
|---|
| | 172 | if part[:1] in '0123456789': |
|---|
| | 173 | yield part.zfill(8) # pad for numeric comparison |
|---|
| | 174 | else: |
|---|
| | 175 | yield '*'+part |
|---|
| | 176 | |
|---|
| | 177 | yield '*final' # ensure that alpha/beta/candidate are before final |
|---|
| | 178 | |
|---|
| | 179 | |
|---|
| | 180 | def filter_best_eggs(source, remove_ifnoteggmatch=False): |
|---|
| | 181 | |
|---|
| | 182 | eggs = {} |
|---|
| | 183 | keep = [] |
|---|
| | 184 | prune_2ndpass = [] |
|---|
| | 185 | |
|---|
| | 186 | for s in source: |
|---|
| | 187 | mo = pkg_resources_EGG_NAME(basename(s)) |
|---|
| | 188 | if not mo: |
|---|
| | 189 | if not remove_ifnoteggmatch: |
|---|
| | 190 | keep.append(s) |
|---|
| | 191 | continue |
|---|
| | 192 | |
|---|
| | 193 | pkgname, ver, pyver = ( |
|---|
| | 194 | mo.group('name'), mo.group('ver'), mo.group('pyver') |
|---|
| | 195 | ) |
|---|
| | 196 | if not (pkgname and ver and pyver): |
|---|
| | 197 | if pkgname and not remove_ifnoteggmatch: |
|---|
| | 198 | keep.append(s) |
|---|
| | 199 | continue |
|---|
| | 200 | |
|---|
| | 201 | if not pyver.startswith(sys.version[:3]): |
|---|
| | 202 | continue |
|---|
| | 203 | |
|---|
| | 204 | ver = tuple(pkg_resources_parse_version_parts(ver)) |
|---|
| | 205 | |
|---|
| | 206 | if pkgname not in eggs: |
|---|
| | 207 | eggs[pkgname] = (pkgname, ver, s) |
|---|
| | 208 | keep.append(s) |
|---|
| | 209 | elif ver >= eggs[pkgname][1]: |
|---|
| | 210 | prune_2ndpass.append(s) |
|---|
| | 211 | elif ver < eggs[pkgname][1]: |
|---|
| | 212 | prune_2ndpass.append(eggs[pkgname][2]) |
|---|
| | 213 | keep.append(s) |
|---|
| | 214 | |
|---|
| | 215 | prune_2ndpass = frozenset(prune_2ndpass) |
|---|
| | 216 | |
|---|
| | 217 | return [s for s in keep if s not in prune_2ndpass] |
|---|
| | 218 | |
|---|
| | 219 | |
|---|
| | 220 | |
|---|
| 255 | | |
|---|
| 256 | | def find_top_packages(where='.', evaluate_packagelocation=evaluate_packagedir): |
|---|
| 257 | | """Find all directories under `where` which contain an __init__.py""" |
|---|
| | 326 | def isegg_path(path, allow_links=False): |
|---|
| | 327 | bn = basename(path) |
|---|
| | 328 | if not pkg_resources_EGG_NAME(bn): |
|---|
| | 329 | return False |
|---|
| | 330 | if path.endswith('.egg'): |
|---|
| | 331 | return True |
|---|
| | 332 | if path.endswith('.egg-link'): |
|---|
| | 333 | if not allow_links: |
|---|
| | 334 | return False |
|---|
| | 335 | return True |
|---|
| | 336 | return False |
|---|
| | 337 | |
|---|
| | 338 | |
|---|
| | 339 | def find_top_packages(where='.', |
|---|
| | 340 | evaluate_packagelocation=evaluate_packagepath, |
|---|
| | 341 | allow_descent=lambda path: isdir(path) and not isegg_path(path)): |
|---|
| | 342 | |
|---|
| | 343 | """Find all top level package directories under `where` |
|---|
| | 344 | |
|---|
| | 345 | |
|---|
| | 346 | If `where` is a relative path then, by default all resulting paths will |
|---|
| | 347 | also be relative. If `where` is an absoloute path then the result paths |
|---|
| | 348 | will also be absoloute. This behaviour can be modified by providing a |
|---|
| | 349 | custom `evaluate_packagelocation` function. |
|---|
| | 350 | |
|---|
| | 351 | Note that by default, egg directories and egg archive files are found as |
|---|
| | 352 | top level package paths but the find process will not decends below the level |
|---|
| | 353 | of an egg directory. This behaviour can be modified by judicious use of |
|---|
| | 354 | `evaluate_packagelocation` and `allow_descent`. |
|---|
| | 355 | |
|---|
| | 356 | """ |
|---|
| | 372 | |
|---|
| | 373 | def find_package_paths(*rootpaths, **kw): |
|---|
| | 374 | |
|---|
| | 375 | pthset = set(kw.pop('pth', [])) |
|---|
| | 376 | pth = [] |
|---|
| | 377 | |
|---|
| | 378 | evpp = lambda p,name='': evaluate_packagepath(p) |
|---|
| | 379 | |
|---|
| | 380 | for rp in rootpaths: |
|---|
| | 381 | ep = evpp(rp) |
|---|
| | 382 | if ep and ep not in pthset: |
|---|
| | 383 | pthset.add(ep) |
|---|
| | 384 | pth.append(ep) |
|---|
| | 385 | continue |
|---|
| | 386 | if not isdir(rp): |
|---|
| | 387 | continue |
|---|
| | 388 | for top in find_top_packages(rp, |
|---|
| | 389 | evaluate_packagelocation=evpp |
|---|
| | 390 | ): |
|---|
| | 391 | for p in top[1]: |
|---|
| | 392 | if p not in pthset: |
|---|
| | 393 | pth.append(p) |
|---|
| | 394 | pthset.add(p) |
|---|
| | 395 | |
|---|
| | 396 | return filter_best_eggs(pth) |
|---|
| | 397 | |
|---|
| | 398 | |
|---|
| | 399 | |
|---|
| | 400 | #----------------------------------------------------------------------------- |
|---|
| | 401 | # python code execution |
|---|
| | 402 | |
|---|
| | 403 | |
|---|
| | 404 | def run_magic_syspath(argv=None, |
|---|
| | 405 | import_modules=False, run_last_module=True): |
|---|
| | 406 | """Run a module using a magicaly extended sys.path. |
|---|
| | 407 | |
|---|
| | 408 | Discovers additional package paths by searching under directories listed as |
|---|
| | 409 | non option arguments. Discovery is egg aware: it takes care to include only |
|---|
| | 410 | the *best* version of each egg and only those eggs that match the python |
|---|
| | 411 | interpreters version. |
|---|
| | 412 | |
|---|
| | 413 | If the last argument names a legitemate python module then sys.path is |
|---|
| | 414 | extended with the discovered paths, sys.argv is adjusted to reflect the |
|---|
| | 415 | module name and the remaining arguments. Then runpy.run_module is used to |
|---|
| | 416 | execute the module in this magicaly discovered path. |
|---|
| | 417 | |
|---|
| | 418 | If any non option argument, other than the last, identifies a python module |
|---|
| | 419 | then sys.path is updated and an attempt to import the module is made. This |
|---|
| | 420 | feature allows import of packages whose import time behaviour would be |
|---|
| | 421 | adversly affected by any paths added on behalf of subsequent modules. Note |
|---|
| | 422 | that you can disable this by setting `import_modules=False` and |
|---|
| | 423 | `run_last_module=True`. This will defer all import behaviour to the module |
|---|
| | 424 | you chose to *run*. |
|---|
| | 425 | |
|---|
| | 426 | If the last argument is not a python package then two lists are returned. |
|---|
| | 427 | The first list contains the discovered path and the second list contains |
|---|
| | 428 | any modules that were imported as it was discovered. The second list will |
|---|
| | 429 | allways be empty if `import_modules` is False. |
|---|
| | 430 | |
|---|
| | 431 | TODO: examples |
|---|
| | 432 | |
|---|
| | 433 | """ |
|---|
| | 434 | |
|---|
| | 435 | argv = argv or sys.argv[:] |
|---|
| | 436 | lenargv = len(argv) |
|---|
| | 437 | if lenargv < 2: |
|---|
| | 438 | sys.exit(-1) |
|---|
| | 439 | |
|---|
| | 440 | ia=1 |
|---|
| | 441 | for i in range(lenargv - 1): |
|---|
| | 442 | ia = i + 1 |
|---|
| | 443 | a = argv[ia] |
|---|
| | 444 | if a == '--': |
|---|
| | 445 | del argv[ia] |
|---|
| | 446 | break |
|---|
| | 447 | elif a[:2] == '--' or a[:1] == '-': |
|---|
| | 448 | break |
|---|
| | 449 | else: |
|---|
| | 450 | ia=lenargv |
|---|
| | 451 | |
|---|
| | 452 | minfo = None |
|---|
| | 453 | |
|---|
| | 454 | searchpaths = [] |
|---|
| | 455 | pthset = set([]) |
|---|
| | 456 | pathsadded = [] |
|---|
| | 457 | pthextend = [] |
|---|
| | 458 | sys_path_insertpos = 0 |
|---|
| | 459 | |
|---|
| | 460 | imported = [] # hold on to the references. |
|---|
| | 461 | |
|---|
| | 462 | for i, a in enumerate(argv[1:ia]): |
|---|
| | 463 | |
|---|
| | 464 | minfo = path_moduleinfo(a) |
|---|
| | 465 | if minfo: |
|---|
| | 466 | # if its a legitemate python module file, import it with the |
|---|
| | 467 | # path we have discovered so far. |
|---|
| | 468 | if minfo[1] in sys.modules: |
|---|
| | 469 | continue |
|---|
| | 470 | |
|---|
| | 471 | if minfo[0] not in pthset: |
|---|
| | 472 | pthextend.append(minfo[0]) |
|---|
| | 473 | pthset.add(minfo[0]) |
|---|
| | 474 | |
|---|
| | 475 | # Don't import modules or update sys.path if import_modules is |
|---|
| | 476 | # False |
|---|
| | 477 | if not import_modules: |
|---|
| | 478 | continue |
|---|
| | 479 | |
|---|
| | 480 | # If the last argument is a module, then dont import it. We will |
|---|
| | 481 | # run it instead. |
|---|
| | 482 | if i+2 == ia: |
|---|
| | 483 | continue |
|---|
| | 484 | |
|---|
| | 485 | newpaths = find_package_paths(pth=pthset, |
|---|
| | 486 | *searchpaths |
|---|
| | 487 | ) |
|---|
| | 488 | pthextend.extend(newpaths) |
|---|
| | 489 | |
|---|
| | 490 | sys.path[sys_path_insertpos:sys_path_insertpos |
|---|
| | 491 | ] = pthextend[:] |
|---|
| | 492 | sys_path_insertpos += len(pthextend) |
|---|
| | 493 | |
|---|
| | 494 | pathsadded.extend(newpaths) |
|---|
| | 495 | pthextend[:] = [] |
|---|
| | 496 | searchpaths[:] = [] |
|---|
| | 497 | |
|---|
| | 498 | imported.append(__import__(minfo[1])) |
|---|
| | 499 | |
|---|
| | 500 | minfo = None |
|---|
| | 501 | continue |
|---|
| | 502 | |
|---|
| | 503 | else: |
|---|
| | 504 | searchpaths.append(a) |
|---|
| | 505 | |
|---|
| | 506 | |
|---|
| | 507 | newpaths = find_package_paths(pth=pthset, |
|---|
| | 508 | *searchpaths |
|---|
| | 509 | ) |
|---|
| | 510 | |
|---|
| | 511 | pthextend.extend(newpaths) |
|---|
| | 512 | |
|---|
| | 513 | if import_modules or run_last_module: |
|---|
| | 514 | sys.path[sys_path_insertpos:sys_path_insertpos |
|---|
| | 515 | ] = pthextend[:] |
|---|
| | 516 | |
|---|
| | 517 | pathsadded.extend(newpaths) |
|---|
| | 518 | |
|---|
| | 519 | if not run_last_module: |
|---|
| | 520 | return pathsadded, imported |
|---|
| | 521 | |
|---|
| | 522 | if minfo: |
|---|
| | 523 | sys.argv[:] = [] |
|---|
| | 524 | sys.argv.append(minfo[1]) |
|---|
| | 525 | sys.argv.extend(argv[ia:]) |
|---|
| | 526 | run_module(sys.argv[0], run_name='__main__', alter_sys=True) |
|---|
| | 527 | |
|---|
| | 528 | return pathsadded, imported |
|---|