Changeset 783

Show
Ignore:
Timestamp:
08/01/07 23:30:20 (1 year ago)
Author:
robin
Message:

* feature: compile and run an explicitly nominated python script, works

irrespective of file extention provide the file *is* legal python code.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • pyrun/trunk/ChangeLog

    r781 r783  
    33 
    440.1d 
     5  * `-S` option to explicitly run a python script. uses compiler.compile to 
     6    load and compile the code from an arbitrary file. uses 
     7    runpy._run_module_code to execute the resulting module as __main__. 
     8    NOTE No discovery or sys.path manipulation is performed on the file 
     9    specified with this option. works for plain old python scripts and 
     10    executable scripts that lack an extension. 
     11    . 
     12    The chief motivation for this is to support python projects that 
     13    distribute substantive scripts rather than using the setuptools 
     14    entry_points 'wrapper' script approach. (For example see the 
     15    0.2.1 source tarbal for pyflakes). 
     16 
    517  * documentation tidy up 
    618 
  • pyrun/trunk/command_line_interface.rst

    r781 r783  
    1818  -n            NORUN. Don't run any of the modules implied by module file 
    1919                references in  the discovery path. 
    20   -m MODULE     Explicitly select a module to run. 
     20  -S SCRIPT     Identify a *python* SCRIPT to execute. The script need not 
     21                have file extension but it must contain leagal python code. 
     22                This option trumps -m. This option should only be necessary 
     23                when the launcher for the python program you wish to run 
     24                contains significant functionality. No additions are made to 
     25                the discovery path or sys.path as a result of using this 
     26                option. If the target script imports a related package you 
     27                will need to include additional non option arguments to 
     28                discover its path. 
     29  -m MODULE     Explicitly select a module to run. (trumped by -S) 
    2130  -d            DEBUG session. Prepare for execution, but start a pdb session 
    2231                using `pdb.set_trace`. 
  • pyrun/trunk/examples/examples-as-tests.txt

    r767 r783  
    44  cd pyrun-checkoutdir && python pyrun.py -m nose.core nose/and/setuptools/under/here -v --with-doctest --doctest-extension=.rst -w examples 
    55 
     6  # or 
     7  cd pyrun-trunk 
     8  python pyrun.py -S `which nosetests` lib/ -v --with-doctest --doctest-extension=.rst -w examples/ 
     9  
     10 
    611The equivelent, without using pyrun, would be something like:: 
    712 
  • pyrun/trunk/pkg-info.rst

    r782 r783  
    11:License: MIT 
    22:Name: pyrun 
    3 :Version: 0.1d.dev 
     3:Version: 0.1d 
    44:Author: Robin Bryce 
    55:Author-email: robinbryce@gmail.com 
  • pyrun/trunk/pyrun.html

    r782 r783  
    300300</tr> 
    301301<tr><th class="docinfo-name">Version:</th> 
    302 <td>0.1d.dev</td></tr> 
     302<td>0.1d</td></tr> 
    303303<tr><th class="docinfo-name">Author:</th> 
    304304<td>Robin Bryce</td></tr> 
     
    465465references in  the discovery path.</td></tr> 
    466466<tr><td class="option-group"> 
     467<kbd><span class="option">-S <var>SCRIPT</var></span></kbd></td> 
     468<td>Identify a <em>python</em> SCRIPT to execute. The script need not 
     469have file extension but it must contain leagal python code. 
     470This option trumps -m. This option should only be necessary 
     471when the launcher for the python program you wish to run 
     472contains significant functionality. No additions are made to 
     473the discovery path or sys.path as a result of using this 
     474option. If the target script imports a related package you 
     475will need to include additional non option arguments to 
     476discover its path.</td></tr> 
     477<tr><td class="option-group"> 
    467478<kbd><span class="option">-m <var>MODULE</var></span></kbd></td> 
    468 <td>Explicitly select a module to run.</td></tr> 
     479<td>Explicitly select a module to run. (trumped by -S)</td></tr> 
    469480<tr><td class="option-group"> 
    470481<kbd><span class="option">-d</span></kbd></td> 
     
    645656<dt>0.1d</dt> 
    646657<dd><ul class="first last simple"> 
     658<li><cite>-S</cite> option to explicitly run a python script. uses compiler.compile to 
     659load and compile the code from an arbitrary file. uses 
     660runpy._run_module_code to execute the resulting module as __main__. 
     661NOTE No discovery or sys.path manipulation is performed on the file 
     662specified with this option. works for plain old python scripts and 
     663executable scripts that lack an extension. 
     664. 
     665The chief motivation for this is to support python projects that 
     666distribute substantive scripts rather than using the setuptools 
     667entry_points 'wrapper' script approach. (For example see the 
     6680.2.1 source tarbal for pyflakes).</li> 
    647669<li>documentation tidy up</li> 
    648670</ul> 
  • pyrun/trunk/pyrun.py

    r781 r783  
     1#!/usr/bin/env python 
    12# Copyright (c) 2007 Robin Bryce 
    2 # All rights reserved 
    3 #  
    4 # Permission is hereby granted, free of charge, to any person obtaining a 
    5 # copy of this software and associated documentation files (the "Software"), 
    6 # to deal in the Software without restriction, including without limitation 
    7 # the rights to use, copy, modify, merge, publish, distribute, sublicense, 
    8 # and/or sell copies of the Software, and to permit persons to whom the 
    9 # Software is furnished to do so, subject to the following conditions: 
    10 #  
    11 # The above copyright notice and this permission notice shall be included in 
    12 # all copies or substantial portions of the Software. 
    13 #  
    14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
    15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
    16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
    17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
    18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
    19 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
    20 # DEALINGS IN THE SOFTWARE.  
    21  
    22 import os, sys, types, re, traceback, inspect, imp 
    23  
    24 from os.path import abspath, normpath, join, expanduser, expandvars, dirname 
    25 from os.path import basename, isabs, exists, isfile, isdir 
     3# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 
     4 
     5import os, sys, types, re, traceback, inspect, imp, compiler 
     6 
     7from os.path import join, dirname, basename, isfile, isdir 
     8 
     9have_runpy=False 
     10try: 
     11    import runpy 
     12    have_runpy=True 
     13except ImportError: 
     14    pass 
     15 
    2616 
    2717def exc_string(einfo=None): 
     
    125115    return (p, '.'.join(packages), ext, mode, mtype) 
    126116 
    127  
    128 def run_pkg_minfo(): 
    129     print path_pkg_moduleinfo(sys.argv[1]) 
    130     return 0 
    131117 
    132118# from distutils.util, because fools at debian think its a good idea to remove 
     
    368354            minfos.append(minfo) 
    369355            a = minfo[0] 
     356 
     357            # Currently ~/xxx/foo.py will put ~/xxx in the path irespective 
     358            # of whether there is a ~/xxx/__init__.py. I am very tempted 
     359            # to reject directories which are not discovered from genuine 
     360            # packages and just rely on cwd for the setup.py use case. 
     361            # 
     362            # The reason I'm tempted is that I'm not sure I like the fact that 
     363            # , with the current implementation, python files collected 
     364            # together in a 'scripts' directory will be able to do sibling 
     365            # imports ( and hence shadow things for each other) becuase their 
     366            # common parent directory gets put on the path here. 
    370367            if a not in pthset: 
    371368                pthextend.append(a) 
     
    416413    """ 
    417414 
    418     if run_module: 
    419         import runpy 
    420  
    421415    argv = argv or sys.argv[:] 
    422416 
    423417    pthextend, minfos, ia = discover_path(1, *argv) 
    424418 
    425     if run_module: 
    426  
    427         if not isinstance(run_module, types.StringTypes): 
    428             run_module = minfos[0][1] 
    429  
    430         if modify_sys: 
    431             # We are running the module, eat the artificial `--` delimiter 
    432             # if it artificially terminated the argv 
    433             if ia < len(argv) and argv[ia] == '--': 
    434                 del argv[ia] 
    435             sys.path[0:0] = pthextend[:] 
    436             sys.argv[:] = [] 
    437             sys.argv.append(run_module) 
    438             sys.argv.extend(argv[ia:]) 
    439  
     419    # We are running the module, eat the artificial `--` delimiter 
     420    # if it artificially terminated the argv 
     421    if ia < len(argv) and argv[ia] == '--': 
     422        del argv[ia] 
     423 
     424    if run_module and not isinstance(run_module, types.StringTypes): 
     425        run_module = minfos[0][1] 
     426 
     427 
     428    if modify_sys: 
     429        sys.path[0:0] = pthextend[:] 
     430        sys.argv[:] = [] 
     431        sys.argv.append(run_module) 
     432        sys.argv.extend(argv[ia:]) 
     433 
     434    if run_module and have_runpy is not False: 
    440435        return runpy.run_module( 
    441436                run_module, run_name='__main__', alter_sys=True 
     
    443438 
    444439    return pthextend, minfos, ia 
     440 
     441 
     442def compile_and_run(filename, mod_name='__main__', 
     443    init_globals=None, mod_fname=None, mod_loader=None, alter_sys=True): 
     444    """compile `filename` and run the code using runpy._run_module_code.""" 
     445    global runpy 
     446    if not have_runpy: 
     447        runpy = __import__('runpy') 
     448 
     449    if mod_fname is None: 
     450        mod_fname = filename 
     451    code = compiler.compile(file(filename).read(), filename, 'exec') 
     452    return runpy._run_module_code(code, 
     453            mod_name=mod_name, 
     454            init_globals=init_globals, 
     455            mod_fname=mod_fname, 
     456            mod_loader=mod_loader, 
     457            alter_sys=alter_sys 
     458            ) 
    445459 
    446460 
     
    517531            # else: its definitely *not* a pyrun option 
    518532 
    519         modname = opts.m or (minfos and minfos[0][1]) or '' 
     533        modname = not opts.S and (opts.m or (minfos and minfos[0][1]) or '') 
    520534 
    521535        # Allways update the sys path. pthextend is the record of what we have 
     
    528542 
    529543        target_argv = [] 
    530         if modname
    531             target_argv.append(modname
     544        if modname or opts.S
     545            target_argv.append(modname or opts.S
    532546            target_argv.extend(argv[ia:]) 
    533547 
     
    539553                print 'argv is empty' 
    540554                return 0 
    541             import runpy 
    542555            sys.argv[:] = target_argv[:] 
    543             return runpy.run_module( 
    544                     modname, run_name='__main__', alter_sys=True 
    545                     ) 
     556            if runpy is not False: 
     557                if not opts.S: 
     558                    return runpy.run_module( 
     559                            modname, run_name='__main__', alter_sys=True 
     560                            ) 
     561                else: 
     562                    return compile_and_run(opts.S) 
     563 
    546564        if opts.d: 
    547565            import pdb 
     
    581599            print os.pathsep.join(pthextend) 
    582600 
    583         if not (opts.n or opts.d or opts.i) and modname
     601        if not (opts.n or opts.d or opts.i) and (modname or opts.S)
    584602            exitval = run_module() 
    585603            if not isinstance(exitval, int): 
     
    627645    return rval 
    628646 
    629 OPTIONS_runex=[ 
    630  
    631 ('-p', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
    632 "Print the discovered path")), 
    633  
    634 ('-P', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
    635 "Print the discovered path in a PYTHONPATH compatible format")), 
    636  
    637  
    638 ('-n', dict(default=False, action='store_true', metavar='NORUN', help= 
    639 '''NORUN. Don't run any of the modules implied by module file references in 
    640  the discovery path.''')), 
    641  
    642 ('-m', dict(default='', metavar='MODULE', help= 
    643 "Explicitly select a module to run.")), 
    644  
    645 ('-d', dict(default=False, action='store_true', metavar='DEBUG', help= 
    646 """DEBUG session. Prepare for execution, but start a pdb session using 
    647 `pdb.set_trace`.""")), 
    648  
    649 ('-D', dict(default=False, action='store_true', metavar='DEBUG', help= 
    650 """POSTMORTEM debugging. If the target raises an exception, start a 
    651     postmortem pdb debugging session.""")), 
    652  
    653 ('-i', dict(default=False, action='store_true', metavar='INTERACTIVE', help= 
    654 "INTERACTIVE session with prepared sys.argv and sys.path.")), 
    655  
    656 ('-c', dict(default=False, metavar='STATEMENT', help= 
    657 """Update sys.argv and sys.path then execute statement in a new 
    658 python module. [NYI]""")), 
    659  
    660 ('-C', dict(default=False, metavar='STATEMENT', help= 
    661 """Update sys.argv and sys.path then execute statement in the context of the 
    662 selected module. [NYI]""")) 
    663  
    664     ] 
    665  
    666 flagOPTIONS_runex=tuple([o[1:] for o, kw in OPTIONS_runex 
    667     if kw.get('action', None) in ('store_true', 'store_false')] 
    668     ) 
    669  
    670647USAGE_runex="""\ 
    671648%prog  [-nidDpP] [BASEPATH(s)][-m mod.name | '--'] [TARGET-OPTIONS] 
     
    682659NOTE: Any option that is marked [NYI] is Not Yet Implemented.""" 
    683660 
     661OPTIONS_runex=[ 
     662 
     663('-p', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
     664"Print the discovered path")), 
     665 
     666('-P', dict(default=False, action='store_true', metavar='PRINTPATH', help= 
     667"Print the discovered path in a PYTHONPATH compatible format")), 
     668 
     669 
     670('-n', dict(default=False, action='store_true', metavar='NORUN', help= 
     671"""NORUN. Don't run any of the modules implied by module file references in 
     672 the discovery path.""")), 
     673 
     674('-S', dict(default=None, type='string', metavar='SCRIPT', help= 
     675"""Identify a *python* SCRIPT to execute. The script need not have file 
     676extension but it must contain leagal python code. This option trumps -m. This 
     677option should only be necessary when the launcher for the python program you 
     678wish to run contains significant functionality. No additions are made to the 
     679discovery path or sys.path as a result of using this option. If the target 
     680script imports a related package you will need to include additional non option 
     681arguments to discover its path.""")), 
     682 
     683('-m', dict(default='', metavar='MODULE', help= 
     684"Explicitly select a module to run. (trumped by -S)")), 
     685 
     686('-d', dict(default=False, action='store_true', metavar='DEBUG', help= 
     687"""DEBUG session. Prepare for execution, but start a pdb session using 
     688`pdb.set_trace`.""")), 
     689 
     690('-D', dict(default=False, action='store_true', metavar='DEBUG', help= 
     691"""POSTMORTEM debugging. If the target raises an exception, start a 
     692    postmortem pdb debugging session.""")), 
     693 
     694('-i', dict(default=False, action='store_true', metavar='INTERACTIVE', help= 
     695"INTERACTIVE session with prepared sys.argv and sys.path.")), 
     696 
     697('-c', dict(default=False, metavar='STATEMENT', help= 
     698"""Update sys.argv and sys.path then execute statement in a new 
     699python module. [NYI]""")), 
     700 
     701('-C', dict(default=False, metavar='STATEMENT', help= 
     702"""Update sys.argv and sys.path then execute statement in the context of the 
     703selected module. [NYI]""")) 
     704 
     705    ] 
     706 
     707flagOPTIONS_runex=tuple([o[1:] for o, kw in OPTIONS_runex 
     708    if kw.get('action', None) in ('store_true', 'store_false')] 
     709    ) 
     710 
     711 
    684712if __name__=='__main__': 
    685     #sys.exit(run_top_minfo()) 
    686713    sys.exit(runex()) 
    687714