You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
6.3 KiB
217 lines
6.3 KiB
#!/usr/bin/env python |
|
# Copyright 2010 Google Inc. All Rights Reserved. |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS-IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
"""Script for running Google-style applications. |
|
|
|
Unlike normal scripts run through setuptools console_script entry points, |
|
Google-style applications must be run as top-level scripts. |
|
|
|
Given an already-imported module, users can use the RunScriptModule function to |
|
set up the appropriate executable environment to spawn a new Python process to |
|
run the module as a script. |
|
|
|
To use this technique in your project, first create a module called e.g. |
|
stubs.py with contents like: |
|
|
|
from google.apputils import run_script_module |
|
|
|
def RunMyScript(): |
|
import my.script |
|
run_script_module.RunScriptModule(my.script) |
|
|
|
def RunMyOtherScript(): |
|
import my.other_script |
|
run_script_module.RunScriptModule(my.other_script) |
|
|
|
Then, set up entry points in your setup.py that point to the functions in your |
|
stubs module: |
|
|
|
setup( |
|
... |
|
entry_points = { |
|
'console_scripts': [ |
|
'my_script = my.stubs:RunMyScript', |
|
'my_other_script = my.stubs.RunMyOtherScript', |
|
], |
|
}, |
|
) |
|
|
|
When your project is installed, setuptools will generate minimal wrapper scripts |
|
to call your stub functions, which in turn execv your script modules. That's it! |
|
""" |
|
|
|
__author__ = 'dborowitz@google.com (Dave Borowitz)' |
|
|
|
import os |
|
import re |
|
import sys |
|
|
|
|
|
def FindEnv(progname): |
|
"""Find the program in the system path. |
|
|
|
Args: |
|
progname: The name of the program. |
|
|
|
Returns: |
|
The full pathname of the program. |
|
|
|
Raises: |
|
AssertionError: if the program was not found. |
|
""" |
|
for path in os.environ['PATH'].split(':'): |
|
fullname = os.path.join(path, progname) |
|
if os.access(fullname, os.X_OK): |
|
return fullname |
|
raise AssertionError( |
|
"Could not find an executable named '%s' in the system path" % progname) |
|
|
|
|
|
def GetPdbArgs(python): |
|
"""Try to get the path to pdb.py and return it in a list. |
|
|
|
Args: |
|
python: The full path to a Python executable. |
|
|
|
Returns: |
|
A list of strings. If a relevant pdb.py was found, this will be |
|
['/path/to/pdb.py']; if not, return ['-m', 'pdb'] and hope for the best. |
|
(This latter technique will fail for Python 2.2.) |
|
""" |
|
# Usually, python is /usr/bin/pythonxx and pdb is /usr/lib/pythonxx/pdb.py |
|
components = python.split('/') |
|
if len(components) >= 2: |
|
pdb_path = '/'.join(components[0:-2] + ['lib'] + |
|
components[-1:] + ['pdb.py']) |
|
if os.access(pdb_path, os.R_OK): |
|
return [pdb_path] |
|
|
|
# No pdb module found in the python path, default to -m pdb |
|
return ['-m', 'pdb'] |
|
|
|
|
|
def StripDelimiters(s, beg, end): |
|
if s[0] == beg: |
|
assert s[-1] == end |
|
return (s[1:-1], True) |
|
else: |
|
return (s, False) |
|
|
|
|
|
def StripQuotes(s): |
|
(s, stripped) = StripDelimiters(s, '"', '"') |
|
if not stripped: |
|
(s, stripped) = StripDelimiters(s, "'", "'") |
|
return s |
|
|
|
|
|
def PrintOurUsage(): |
|
"""Print usage for the stub script.""" |
|
print 'Stub script %s (auto-generated). Options:' % sys.argv[0] |
|
print ('--helpstub ' |
|
'Show help for stub script.') |
|
print ('--debug_binary ' |
|
'Run python under debugger specified by --debugger.') |
|
print ('--debugger=<debugger> ' |
|
"Debugger for --debug_binary. Default: 'gdb --args'.") |
|
print ('--debug_script ' |
|
'Run wrapped script with python debugger module (pdb).') |
|
print ('--show_command_and_exit ' |
|
'Print command which would be executed and exit.') |
|
print ('These options must appear first in the command line, all others will ' |
|
'be passed to the wrapped script.') |
|
|
|
|
|
def RunScriptModule(module): |
|
"""Run a module as a script. |
|
|
|
Locates the module's file and runs it in the current interpreter, or |
|
optionally a debugger. |
|
|
|
Args: |
|
module: The module object to run. |
|
""" |
|
args = sys.argv[1:] |
|
|
|
debug_binary = False |
|
debugger = 'gdb --args' |
|
debug_script = False |
|
show_command_and_exit = False |
|
|
|
while args: |
|
if args[0] == '--helpstub': |
|
PrintOurUsage() |
|
sys.exit(0) |
|
if args[0] == '--debug_binary': |
|
debug_binary = True |
|
args = args[1:] |
|
continue |
|
if args[0] == '--debug_script': |
|
debug_script = True |
|
args = args[1:] |
|
continue |
|
if args[0] == '--show_command_and_exit': |
|
show_command_and_exit = True |
|
args = args[1:] |
|
continue |
|
matchobj = re.match('--debugger=(.+)', args[0]) |
|
if matchobj is not None: |
|
debugger = StripQuotes(matchobj.group(1)) |
|
args = args[1:] |
|
continue |
|
break |
|
|
|
# Now look for my main python source file |
|
# TODO(dborowitz): This will fail if the module was zipimported, which means |
|
# no egg depending on this script runner can be zip_safe. |
|
main_filename = module.__file__ |
|
assert os.path.exists(main_filename), ('Cannot exec() %r: file not found.' % |
|
main_filename) |
|
assert os.access(main_filename, os.R_OK), ('Cannot exec() %r: file not' |
|
' readable.' % main_filename) |
|
|
|
args = [main_filename] + args |
|
|
|
if debug_binary: |
|
debugger_args = debugger.split() |
|
program = debugger_args[0] |
|
# If pathname is not absolute, determine full path using PATH |
|
if not os.path.isabs(program): |
|
program = FindEnv(program) |
|
python_path = sys.executable |
|
command_vec = [python_path] |
|
if debug_script: |
|
command_vec.extend(GetPdbArgs(python_path)) |
|
args = [program] + debugger_args[1:] + command_vec + args |
|
|
|
elif debug_script: |
|
args = [sys.executable] + GetPdbArgs(program) + args |
|
|
|
else: |
|
program = sys.executable |
|
args = [sys.executable] + args |
|
|
|
if show_command_and_exit: |
|
print 'program: "%s"' % program |
|
print 'args:', args |
|
sys.exit(0) |
|
|
|
try: |
|
sys.stdout.flush() |
|
os.execv(program, args) |
|
except EnvironmentError as e: |
|
if not getattr(e, 'filename', None): |
|
e.filename = program # Add info to error message |
|
raise
|
|
|