SF patch #1035498: -m option to run a module as a script

(Contributed by Nick Coghlan.)
This commit is contained in:
Raymond Hettinger 2004-10-07 06:46:25 +00:00
parent fb09f0e85c
commit db29e0fe8c
5 changed files with 109 additions and 11 deletions

View file

@ -205,6 +205,11 @@ executes the statement(s) in \var{command}, analogous to the shell's
or other characters that are special to the shell, it is best to quote or other characters that are special to the shell, it is best to quote
\var{command} in its entirety with double quotes. \var{command} in its entirety with double quotes.
Some Python modules are also useful as scripts. These can be invoked using
\samp{\program{python} \programopt{-m} \var{module} [arg] ...}, which
executes the source file for \var{module} as if you had spelled out its
full name on the command line.
Note that there is a difference between \samp{python file} and Note that there is a difference between \samp{python file} and
\samp{python <file}. In the latter case, input requests from the \samp{python <file}. In the latter case, input requests from the
program, such as calls to \function{input()} and \function{raw_input()}, are program, such as calls to \function{input()} and \function{raw_input()}, are
@ -229,9 +234,11 @@ one; when no script and no arguments are given, \code{sys.argv[0]} is
an empty string. When the script name is given as \code{'-'} (meaning an empty string. When the script name is given as \code{'-'} (meaning
standard input), \code{sys.argv[0]} is set to \code{'-'}. When standard input), \code{sys.argv[0]} is set to \code{'-'}. When
\programopt{-c} \var{command} is used, \code{sys.argv[0]} is set to \programopt{-c} \var{command} is used, \code{sys.argv[0]} is set to
\code{'-c'}. Options found after \programopt{-c} \var{command} are \code{'-c'}. When \programopt{-m} \var{module} is used, \code{sys.argv[0]}
not consumed by the Python interpreter's option processing but left in is set to the full name of the located module. Options found after
\code{sys.argv} for the command to handle. \programopt{-c} \var{command} or \programopt{-m} \var{module} are not consumed
by the Python interpreter's option processing but left in \code{sys.argv} for
the command or module to handle.
\subsection{Interactive Mode \label{interactive}} \subsection{Interactive Mode \label{interactive}}

View file

@ -21,6 +21,10 @@ PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
PyAPI_FUNC(void) PyImport_Cleanup(void); PyAPI_FUNC(void) PyImport_Cleanup(void);
PyAPI_FUNC(int) PyImport_ImportFrozenModule(char *); PyAPI_FUNC(int) PyImport_ImportFrozenModule(char *);
PyAPI_FUNC(struct filedescr *) _PyImport_FindModule(
const char *, PyObject *, char *, size_t, FILE **, PyObject **);
PyAPI_FUNC(int) _PyImport_IsScript(struct filedescr *);
PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *); PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *);
PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *); PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *);

View file

@ -12,6 +12,9 @@ What's New in Python 2.4 beta 1?
Core and builtins Core and builtins
----------------- -----------------
- Added a command line option, -m module, which searches sys.path for the
module and then runs it. (Contributed by Nick Coghlan.)
- The bytecode optimizer now folds tuples of constants into a single - The bytecode optimizer now folds tuples of constants into a single
constant. constant.
@ -29,7 +32,9 @@ Extension modules
- ``collections.deque`` objects didn't play quite right with garbage - ``collections.deque`` objects didn't play quite right with garbage
collection, which could lead to a segfault in a release build, or collection, which could lead to a segfault in a release build, or
an assert failure in a debug build. an assert failure in a debug build. Also, added overflow checks,
better detection of mutation during iteration, and shielded deque
comparisons from unusual subclass overrides of the __iter__() method.
Library Library
------- -------

View file

@ -3,6 +3,7 @@
#include "Python.h" #include "Python.h"
#include "osdefs.h" #include "osdefs.h"
#include "compile.h" /* For CO_FUTURE_DIVISION */ #include "compile.h" /* For CO_FUTURE_DIVISION */
#include "import.h"
#ifdef __VMS #ifdef __VMS
#include <unixlib.h> #include <unixlib.h>
@ -33,7 +34,7 @@ static char **orig_argv;
static int orig_argc; static int orig_argc;
/* command line options */ /* command line options */
#define BASE_OPTS "c:dEhiOQ:StuUvVW:xX" #define BASE_OPTS "c:dEhim:OQ:StuUvVW:xX"
#ifndef RISCOS #ifndef RISCOS
#define PROGRAM_OPTS BASE_OPTS #define PROGRAM_OPTS BASE_OPTS
@ -47,7 +48,7 @@ extern int Py_RISCOSWimpFlag;
/* Short usage message (with %s for argv0) */ /* Short usage message (with %s for argv0) */
static char *usage_line = static char *usage_line =
"usage: %s [option] ... [-c cmd | file | -] [arg] ...\n"; "usage: %s [option] ... [-c cmd | -m mod | file | -] [arg] ...\n";
/* Long usage message, split into parts < 512 bytes */ /* Long usage message, split into parts < 512 bytes */
static char *usage_1 = "\ static char *usage_1 = "\
@ -60,15 +61,16 @@ Options and arguments (and corresponding environment variables):\n\
and force prompts, even if stdin does not appear to be a terminal\n\ and force prompts, even if stdin does not appear to be a terminal\n\
"; ";
static char *usage_2 = "\ static char *usage_2 = "\
-m mod : run library module as a script (terminates option list)\n\
-O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\ -O : optimize generated bytecode (a tad; also PYTHONOPTIMIZE=x)\n\
-OO : remove doc-strings in addition to the -O optimizations\n\ -OO : remove doc-strings in addition to the -O optimizations\n\
-Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n\ -Q arg : division options: -Qold (default), -Qwarn, -Qwarnall, -Qnew\n\
-S : don't imply 'import site' on initialization\n\ -S : don't imply 'import site' on initialization\n\
-t : issue warnings about inconsistent tab usage (-tt: issue errors)\n\ -t : issue warnings about inconsistent tab usage (-tt: issue errors)\n\
-u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\ -u : unbuffered binary stdout and stderr (also PYTHONUNBUFFERED=x)\n\
see man page for details on internal buffering relating to '-u'\n\
"; ";
static char *usage_3 = "\ static char *usage_3 = "\
see man page for details on internal buffering relating to '-u'\n\
-v : verbose (trace import statements) (also PYTHONVERBOSE=x)\n\ -v : verbose (trace import statements) (also PYTHONVERBOSE=x)\n\
-V : print the Python version number and exit\n\ -V : print the Python version number and exit\n\
-W arg : warning control (arg is action:message:category:module:lineno)\n\ -W arg : warning control (arg is action:message:category:module:lineno)\n\
@ -130,6 +132,28 @@ static void RunStartupFile(PyCompilerFlags *cf)
} }
} }
/* Get the path to a top-level module */
static struct filedescr * FindModule(const char *module,
FILE **fp, char **filename)
{
struct filedescr *fdescr = NULL;
*fp = NULL;
*filename = malloc(MAXPATHLEN);
if (*filename == NULL)
return NULL;
/* Find the actual module source code */
fdescr = _PyImport_FindModule(module, NULL,
*filename, MAXPATHLEN, fp, NULL);
if (fdescr == NULL) {
free(*filename);
*filename = NULL;
}
return fdescr;
}
/* Main program */ /* Main program */
@ -140,6 +164,7 @@ Py_Main(int argc, char **argv)
int sts; int sts;
char *command = NULL; char *command = NULL;
char *filename = NULL; char *filename = NULL;
char *module = NULL;
FILE *fp = stdin; FILE *fp = stdin;
char *p; char *p;
int inspect = 0; int inspect = 0;
@ -177,6 +202,18 @@ Py_Main(int argc, char **argv)
break; break;
} }
if (c == 'm') {
/* -m is the last option; following arguments
that look like options are left for the
module to interpret. */
module = malloc(strlen(_PyOS_optarg) + 2);
if (module == NULL)
Py_FatalError(
"not enough memory to copy -m argument");
strcpy(module, _PyOS_optarg);
break;
}
switch (c) { switch (c) {
case 'd': case 'd':
@ -289,7 +326,7 @@ Py_Main(int argc, char **argv)
(p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
unbuffered = 1; unbuffered = 1;
if (command == NULL && _PyOS_optind < argc && if (command == NULL && module == NULL && _PyOS_optind < argc &&
strcmp(argv[_PyOS_optind], "-") != 0) strcmp(argv[_PyOS_optind], "-") != 0)
{ {
#ifdef __VMS #ifdef __VMS
@ -381,7 +418,7 @@ Py_Main(int argc, char **argv)
Py_Initialize(); Py_Initialize();
if (Py_VerboseFlag || if (Py_VerboseFlag ||
(command == NULL && filename == NULL && stdin_is_interactive)) { (command == NULL && filename == NULL && module == NULL && stdin_is_interactive)) {
fprintf(stderr, "Python %s on %s\n", fprintf(stderr, "Python %s on %s\n",
Py_GetVersion(), Py_GetPlatform()); Py_GetVersion(), Py_GetPlatform());
if (!Py_NoSiteFlag) if (!Py_NoSiteFlag)
@ -394,9 +431,34 @@ Py_Main(int argc, char **argv)
argv[_PyOS_optind] = "-c"; argv[_PyOS_optind] = "-c";
} }
if (module != NULL) {
/* Backup _PyOS_optind and find the real file */
struct filedescr *fdescr = NULL;
_PyOS_optind--;
if ((fdescr = FindModule(module, &fp, &filename))) {
argv[_PyOS_optind] = filename;
} else {
fprintf(stderr, "%s: module %s not found\n",
argv[0], module);
return 2;
}
if (!fp) {
fprintf(stderr,
"%s: module %s has no associated file\n",
argv[0], module);
return 2;
}
if (!_PyImport_IsScript(fdescr)) {
fprintf(stderr,
"%s: module %s not usable as script\n (%s)\n",
argv[0], module, filename);
return 2;
}
}
PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind);
if ((inspect || (command == NULL && filename == NULL)) && if ((inspect || (command == NULL && filename == NULL && module == NULL)) &&
isatty(fileno(stdin))) { isatty(fileno(stdin))) {
PyObject *v; PyObject *v;
v = PyImport_ImportModule("readline"); v = PyImport_ImportModule("readline");
@ -409,6 +471,10 @@ Py_Main(int argc, char **argv)
if (command) { if (command) {
sts = PyRun_SimpleStringFlags(command, &cf) != 0; sts = PyRun_SimpleStringFlags(command, &cf) != 0;
free(command); free(command);
} else if (module) {
sts = PyRun_AnyFileExFlags(fp, filename, 1, &cf) != 0;
free(module);
free(filename);
} }
else { else {
if (filename == NULL && stdin_is_interactive) { if (filename == NULL && stdin_is_interactive) {
@ -431,7 +497,7 @@ Py_Main(int argc, char **argv)
} }
if (inspect && stdin_is_interactive && if (inspect && stdin_is_interactive &&
(filename != NULL || command != NULL)) (filename != NULL || command != NULL || module != NULL))
/* XXX */ /* XXX */
sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0; sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;

View file

@ -1334,6 +1334,22 @@ find_module(char *fullname, char *subname, PyObject *path, char *buf,
return fdp; return fdp;
} }
/* Helpers for main.c
* Find the source file corresponding to a named module
*/
struct filedescr *
_PyImport_FindModule(const char *name, PyObject *path, char *buf,
size_t buflen, FILE **p_fp, PyObject **p_loader)
{
return find_module((char *) name, (char *) name, path,
buf, buflen, p_fp, p_loader);
}
PyAPI_FUNC(int) _PyImport_IsScript(struct filedescr * fd)
{
return fd->type == PY_SOURCE || fd->type == PY_COMPILED;
}
/* case_ok(char* buf, int len, int namelen, char* name) /* case_ok(char* buf, int len, int namelen, char* name)
* The arguments here are tricky, best shown by example: * The arguments here are tricky, best shown by example:
* /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0