Changeset 761

Show
Ignore:
Timestamp:
07/21/07 23:21:58 (1 year ago)
Author:
robin
Message:

Default pyrun method (runex) provides help and extended features.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • pyrun/trunk/pyrun.html

    r760 r761  
    337337<li><a class="reference" href="#pyrun" id="id1" name="id1">pyrun</a></li> 
    338338<li><a class="reference" href="#installation" id="id2" name="id2">Installation</a></li> 
    339 <li><a class="reference" href="#pyrun-module-api" id="id3" name="id3">pyrun - module api</a><ul> 
    340 <li><a class="reference" href="#discover-and-run" id="id4" name="id4">discover_and_run</a></li> 
     339<li><a class="reference" href="#pyrun-command-line-usage" id="id3" name="id3">pyrun - command line usage</a><ul> 
     340<li><a class="reference" href="#module-discovery-and-implicit-selection" id="id4" name="id4">module discovery and implicit selection</a></li> 
     341<li><a class="reference" href="#simple-examples" id="id5" name="id5">simple examples</a></li> 
    341342</ul> 
    342343</li> 
    343 <li><a class="reference" href="#changelog" id="id5" name="id5">ChangeLog</a></li> 
     344<li><a class="reference" href="#pyrun-module-api" id="id6" name="id6">pyrun - module api</a><ul> 
     345<li><a class="reference" href="#discover-and-run" id="id7" name="id7">discover_and_run</a></li> 
     346</ul> 
     347</li> 
     348<li><a class="reference" href="#changelog" id="id8" name="id8">ChangeLog</a></li> 
    344349</ul> 
    345350</div> 
     
    407412</div> 
    408413<div class="section"> 
    409 <h1><a class="toc-backref" href="#id3" id="pyrun-module-api" name="pyrun-module-api">pyrun - module api</a></h1> 
    410 <div class="section"> 
    411 <h2><a class="toc-backref" href="#id4" id="discover-and-run" name="discover-and-run">discover_and_run</a></h2> 
     414<h1><a class="toc-backref" href="#id3" id="pyrun-command-line-usage" name="pyrun-command-line-usage">pyrun - command line usage</a></h1> 
     415<p>pyrun [PYRUN-OPTIONS] [DISCOVERYPATH] [TARGET-OPTIONS]</p> 
     416<div class="section"> 
     417<h2><a class="toc-backref" href="#id4" id="module-discovery-and-implicit-selection" name="module-discovery-and-implicit-selection">module discovery and implicit selection</a></h2> 
     418<p>If you include an explicit reference to a python source file in the discovery 
     419path you are acheiving two things:</p> 
     420<ol class="arabic simple"> 
     421<li>You select it as the <cite>implicit</cite> run target</li> 
     422<li>You <cite>discover</cite> the appropriate path to add to sys.path in order for the 
     423identified module to be imported using its <em>fully</em> qualified module name.</li> 
     424</ol> 
     425<p>All modules referenced on the command line like this are considered for path 
     426discovery. The implicit <cite>pyrun target</cite> is <em>always</em> the <em>first</em> module 
     427reference.</p> 
     428<p>To <em>explicitly</em> select the module to run, terminate the pyrun arguments with 
     429<tt class="docutils literal"><span class="pre">-m</span> <span class="pre">fully.qualified.module.name</span></tt>. Replacing <cite>fully.qualified.module.name</cite> 
     430with the module you wish to run.</p> 
     431</div> 
     432<div class="section"> 
     433<h2><a class="toc-backref" href="#id5" id="simple-examples" name="simple-examples">simple examples</a></h2> 
     434<p>Given:</p> 
     435<pre class="literal-block"> 
     436~/foo/__init__.py 
     437~/foo/bar/__init__.py 
     438~/foo/mod.py 
     439</pre> 
     440<p>Implicit selection of a sub module:</p> 
     441<pre class="literal-block"> 
     442pyrun ~/foo/bar/__init__.py 
     443           ^              ^ 
     444           |              | 
     445           |              . 
     446           |               ------- * module file 
     447           . 
     448            -----------------------* `sys.path` entry 
     449 
     450pyrun ~/foo/mod.py 
     451           ^     ^ 
     452           |     | 
     453           |     . 
     454           |      ---------------- * module file 
     455           . 
     456            -----------------------* `sys.path` entry 
     457</pre> 
     458<p>The <tt class="docutils literal"><span class="pre">python</span> <span class="pre">-m</span></tt> form of the above examples would be:</p> 
     459<pre class="literal-block"> 
     460PYTHONPATH=~/foo python -m foo.bar 
     461 
     462PYTHONPATH=~/foo python -m foo.mod 
     463</pre> 
     464<p>For examples as simple as this one the only potential advantage of pyrun is the 
     465convenience of not having to work with <cite>SHELL</cite> environment variables.</p> 
     466<p>Things get more interesting when your <cite>discovery</cite> path includes more elements.</p> 
     467</div> 
     468</div> 
     469<div class="section"> 
     470<h1><a class="toc-backref" href="#id6" id="pyrun-module-api" name="pyrun-module-api">pyrun - module api</a></h1> 
     471<div class="section"> 
     472<h2><a class="toc-backref" href="#id7" id="discover-and-run" name="discover-and-run">discover_and_run</a></h2> 
    412473<p>All of the command line programs provided by this package are essentially thin 
    413474wrappers around the <cite>pyrun.discover_and_run</cite> function. This section provides 
     
    559620</div> 
    560621<div class="section"> 
    561 <h1><a class="toc-backref" href="#id5" id="changelog" name="changelog">ChangeLog</a></h1> 
     622<h1><a class="toc-backref" href="#id8" id="changelog" name="changelog">ChangeLog</a></h1> 
    562623<p>NEXTREVISION</p> 
    563624<blockquote> 
  • pyrun/trunk/pyrun.py

    r756 r761  
    1 import os, sys, re, traceback, inspect, imp 
     1import os, sys, types, re, traceback, inspect, imp 
    22 
    33from os.path import abspath, normpath, join, expanduser, expandvars, dirname 
     
    232232        ia = i + startpos 
    233233        a = argv[ia] 
    234         if a == '--': 
    235             yield ia + 1, None 
    236             return 
    237         elif a[:2] == '--' or a[:1] == '-': 
     234        #if a == '--': 
     235        #    yield ia + 1, None 
     236        #    return 
     237        #elif a[:2] == '--' or a[:1] == '-': 
     238        if a[:2] == '--' or a[:1] == '-': 
    238239            return 
    239240        else: 
     
    284285    pthset = set([]) 
    285286 
    286     minfo = None 
     287    ia = offset 
     288 
    287289    for ia, a in enumerate_argv_args(args, offset): 
    288         if a is None: 
    289             continue 
     290 
    290291        minfo = path_moduleinfo(a) 
    291292        if minfo: 
    292             # if its a legitemate python module file, import it with the 
    293             # path we have discovered so far. 
    294             if minfo[1] in sys.modules: 
    295                 continue 
    296  
    297293            minfos.append(minfo) 
    298294            a = minfo[0] 
     
    308304    pthextend.extend(newpaths) 
    309305 
    310     return pthextend, minfos, ia 
    311  
    312  
    313 def discover_and_run(argv=None, run_module=True): 
     306    return pthextend, minfos, ia + 1 
     307 
     308 
     309def discover_and_run(argv=None, run_module=True, modify_sys=True): 
    314310    """Discover package paths and run the last module listed in argv. 
    315311 
     
    319315    Uses runpy.run_module to run the module as though it was '__main__'. 
    320316 
    321     """ 
    322     if run_module is True: 
    323         from runpy import run_module 
     317    :Paramaters: 
     318        run_module 
     319            If False the module will *not* be executed. Otherwise it should 
     320            be the either: The explicit name of the module to run or any 
     321            value that evalutes True. If its true and its *not* a string 
     322            then the module name corresponding to the *first* module file 
     323            referenced in `argv` is implied. 
     324        modify_sys 
     325            If False sys.argv and sys.path will *not* be changed. Otherwise 
     326            sys.argv and sys.path will be updated appropriately only if 
     327            run_module is not False. 
     328 
     329    :Returns: 
     330        If a module is run the return value is the result of 
     331        `runpy.run_module` otherwise it is a 3 element tuple: 
     332 
     333        (pthextend, minfos, ia) 
     334 
     335        * `pthextend` is a list of the paths that were discovered. 
     336        * `minfos` is a list of tuples describing the results of examining 
     337          any module files. 
     338        * `ia` is index of the first option argument encountered after the 
     339          discovery paths. 
     340 
     341    """ 
     342 
     343    if run_module: 
     344        import runpy 
    324345 
    325346    argv = argv or sys.argv[:] 
    326347 
    327348    pthextend, minfos, ia = discover_path(1, *argv) 
    328     if run_module and minfos: 
    329         sys.path[0:0] = pthextend[:] 
    330         sys.argv[:] = [] 
    331         sys.argv.append(minfos[0][1]) 
    332         sys.argv.extend(argv[ia:]) 
    333         return run_module(sys.argv[0], run_name='__main__', alter_sys=True) 
     349 
     350    if run_module: 
     351 
     352        if not isinstance(run_module, types.StringTypes): 
     353            run_module = minfos[0][1] 
     354 
     355        if modify_sys: 
     356            # We are running the module, eat the artificial `--` delimiter 
     357            # if it artificially terminated the argv 
     358            if ia < len(argv) and argv[ia] == '--': 
     359                del argv[ia] 
     360            sys.path[0:0] = pthextend[:] 
     361            sys.argv[:] = [] 
     362            sys.argv.append(run_module) 
     363            sys.argv.extend(argv[ia:]) 
     364 
     365        return runpy.run_module( 
     366                run_module, run_name='__main__', alter_sys=True 
     367                ) 
     368 
    334369    return pthextend, minfos, ia 
    335370 
    336371 
     372def runex(argv=None): 
     373    """Provides Extened `pyrun` features on the command line.""" 
     374 
     375    # Process the command line arguments. 
     376    import optparse 
     377    parser = optparse.OptionParser(usage=USAGE_runex) 
     378    default_opts = {} 
     379    for shrt, kw in OPTIONS_runex: 
     380        default_opts[shrt[1]] = kw['default'] 
     381        parser.add_option(shrt, **kw) 
     382 
     383    parser.disable_interspersed_args() 
     384 
     385    opts, args = parser.parse_args() 
     386    argv = args[:] 
     387    argv.insert(0, None) 
     388 
     389    try: 
     390        pthextend, minfos, ia = discover_path(1, *argv) 
     391 
     392        # If there are no unconsumed arguments: We have already determined 
     393        # the user does not want to run a module. So we are done. 
     394 
     395        assert ia <= len(argv), ( 
     396                'ia=%s, argv="%s"' 
     397                ) % (ia, str(argv) 
     398                        ) 
     399        if ia == len(argv): 
     400            argv.append('--') 
     401 
     402        # Unconsumed arguments exist and start at index `ia` in `argv` As a 
     403        # convenience we allow any *one* of the pyrun options to be used as to 
     404        # delimit the end of the discovery path. This means that we need to 
     405        # examine the first remaining option and determine whether its intended 
     406        # for pyrun and if so whether it means we have more to do. But, and 
     407        # this is *IMPORTANT*, if any option was set before the discovery path 
     408        # it is assumed to be for the target module and hence is ignored. For 
     409        # example ``pyrun -m bar ~/foo -m 12`` means search under `~/foo` for 
     410        # package paths, then run module `bar` with an argv of: 
     411        #   ``['bar', '-m', '12']`` 
     412        # 
     413 
     414        if argv[ia] == '--': 
     415            del argv[ia] 
     416        else: 
     417            # All pyrun options are short options. 
     418            nextopt = argv[ia][1:] 
     419            if nextopt in default_opts: 
     420 
     421                # It's possibly a pyrun option. 
     422 
     423                # Distinguish between those arguments that require values and 
     424                # those that don't 
     425                if nextopt in flagOPTIONS_runex: 
     426 
     427                    setattr(opts, nextopt, not default_opts[nextopt]) 
     428                    del argv[ia] 
     429 
     430                else: 
     431                    # if its not a flag and it is a pyrun option then it 
     432                    # takes a single argument. 
     433                    if len(argv) < ia+1: 
     434                        print ('The `-%s` pyrun option requires a module name' 
     435                                ) % nextopt 
     436                        return -1 
     437 
     438                    setattr(opts, nextopt, argv[ia+1]) 
     439                    del argv[ia:ia+2] 
     440 
     441            # else: its definitely *not* a pyrun option 
     442 
     443        modname = opts.m or (minfos and minfos[0][1]) or '' 
     444 
     445        target_argv = [] 
     446        if modname: 
     447            target_argv.append(modname) 
     448            target_argv.extend(argv[ia:]) 
     449        def run_module(): 
     450            if opts.n: 
     451                print 'execution of module disabled by user options' 
     452                return 0 
     453            if not target_argv: 
     454                print 'argv is empty' 
     455                return 0 
     456            import runpy 
     457            sys.argv[:] = target_argv[:] 
     458            sys.path[0:0] = pthextend[:] 
     459            return runpy.run_module( 
     460                    modname, run_name='__main__', alter_sys=True 
     461                    ) 
     462        if opts.d: 
     463            import pdb 
     464            pdb.set_trace() 
     465        if opts.i: 
     466            banner='' 
     467            if opts.p: 
     468                banner += 'Discovered paths:\n\t%s' % '\n\t'.join( 
     469                        pthextend 
     470                        ) 
     471            if opts.P: 
     472                banner += '\n\nPYTONPATH=%s' % os.pathsep.join( 
     473                        pthextend 
     474                        ) 
     475            if opts.p or opts.P: 
     476                banner += '\n\n' 
     477            banner += """\ 
     478Call `run_module` with no arguments to execute the implied or explicit 
     479module. You can safely manipulate the target sys.path and sys.argv before 
     480calling `run_module` by updating `target_argv` and `pthextend` in place. 
     481`minfos` is a list of tuples describing any module files you listed in 
     482the discovery path.""" 
     483            import code 
     484            code.interact(banner=banner, local=locals()) 
     485        if pthextend and opts.p and not opts.i: 
     486            for p in pthextend: 
     487                print p 
     488        if pthextend and opts.P and not opts.i: 
     489            print os.pathsep.join(pthextend) 
     490 
     491        if not (opts.n or opts.d or opts.i) and opts.m: 
     492            return run_module() 
     493        return 0 
     494 
     495    except SystemExit: 
     496        raise 
     497    except: 
     498        einfo = sys.exc_info() 
     499        msg = exc_string(einfo=einfo) 
     500        print msg 
     501        if opts.d: 
     502            import pdb 
     503            pdb.post_mortem(einfo[2]) 
     504        return -1 
     505 
     506 
    337507def run(argv=None): 
    338     """setuptools compatible entry point - basic features only. 
    339  
    340     Command line help is not provided by this entry point and all 
    341     options are expected to be for the target module. 
     508    """Basic features for command line use, 
     509 
     510    `--` may be used to artificially terminate the pyrun discovery 
     511    path. All other options are passed to the *implicitly* selected 
     512    target module. 
     513 
     514    NOTE: Command line help is not provided by this entry point and all options 
     515    are expected to be for the target module. 
     516 
    342517    """ 
    343518 
     
    345520        rval = discover_and_run(argv) 
    346521        if not isinstance(rval, int): 
     522            if isinstance(rval, tuple) and isinstance(rval[0], list): 
     523                for p in rval[0]: 
     524                    print p 
    347525            rval = 0 
    348526    except SystemExit: 
     
    354532    return rval 
    355533 
    356 def runex(argv=None): 
    357     return run(argv) 
    358  
     534OPTIONS_runex=[ 
     535 
     536('-p', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
     537"Print the discovered path")), 
     538 
     539('-P', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
     540"Print the discovered path in a PYTHONPATH compatible format")), 
     541 
     542 
     543('-n', dict(default=False, action='store_true', metavar='NORUN', help= 
     544'''NORUN. Don't run any of the modules implied by module file references in 
     545 the discovery path.''')), 
     546 
     547('-m', dict(default='', metavar='MODULE', help= 
     548"Explicitly select a module to run.")), 
     549 
     550('-d', dict(default=False, action='store_true', metavar='DEBUG', help= 
     551"""DEBUG session. Prepare for execution, but start a pdb session using 
     552`pdb.set_trace`.""")), 
     553 
     554('-i', dict(default=False, action='store_true', metavar='INTERACTIVE', help= 
     555"INTERACTIVE session with prepared sys.argv and sys.path.")), 
     556 
     557('-c', dict(default=False, metavar='STATEMENT', help= 
     558"""Update sys.argv and sys.path then execute statement in a new 
     559python module. [NYI]""")), 
     560 
     561('-C', dict(default=False, metavar='STATEMENT', help= 
     562"""Update sys.argv and sys.path then execute statement in the context of the 
     563selected module. [NYI]""")) 
     564 
     565    ] 
     566 
     567flagOPTIONS_runex=tuple([o[1:] for o, kw in OPTIONS_runex 
     568    if kw.get('action', None) in ('store_true', 'store_false')] 
     569    ) 
     570 
     571USAGE_runex="""\ 
     572%prog  [-nidP] [BASEPATH 1] [BASEPATH 2] ... [BASEPATH n] 
     573    [-m mod.name | -c STATEMENT | -C STATEMENT | '--'] [TARGET-OPTIONS] 
     574 
     575In most cases the solo '--' is not required. It tends to be useful when you 
     576implicitly select the module to run AND you want to pass a non option argument 
     577as the first value in the command line for that module. It can also be 
     578necessary when the target module has short options, without long-name 
     579alternatives, which collide with those defined for pyrun. 
     580 
     581Discover python packages and modules under PATH. Run the first module file 
     582named in `PATH` *OR* explicitly nominated using the `-m` option. 
     583 
     584NOTE: Any option that is marked [NYI] is Not Yet Implemented.""" 
     585  
    359586if __name__=='__main__': 
    360     sys.exit(run()) 
    361  
     587    sys.exit(runex()) 
     588 
  • pyrun/trunk/pyrun.rst

    r759 r761  
    1010.. include:: INSTALL.txt 
    1111 
     12.. include:: examples/command_line_use.rst 
    1213.. include:: examples/discover_and_run.rst 
    1314