mirror of https://github.com/python/cpython
bpo-43538: Add extra arguments to os.startfile (GH-25538)
This commit is contained in:
parent
3513d55a61
commit
019e9e8168
|
@ -4155,7 +4155,7 @@ written in Python, such as a mail server's external command delivery program.
|
|||
.. availability:: Windows.
|
||||
|
||||
|
||||
.. function:: startfile(path[, operation])
|
||||
.. function:: startfile(path, [operation], [arguments], [cwd], [show_cmd])
|
||||
|
||||
Start a file with its associated application.
|
||||
|
||||
|
@ -4169,13 +4169,25 @@ written in Python, such as a mail server's external command delivery program.
|
|||
``'print'`` and ``'edit'`` (to be used on files) as well as ``'explore'`` and
|
||||
``'find'`` (to be used on directories).
|
||||
|
||||
When launching an application, specify *arguments* to be passed as a single
|
||||
string. This argument may have no effect when using this function to launch a
|
||||
document.
|
||||
|
||||
The default working directory is inherited, but may be overridden by the *cwd*
|
||||
argument. This should be an absolute path. A relative *path* will be resolved
|
||||
against this argument.
|
||||
|
||||
Use *show_cmd* to override the default window style. Whether this has any
|
||||
effect will depend on the application being launched. Values are integers as
|
||||
supported by the Win32 :c:func:`ShellExecute` function.
|
||||
|
||||
:func:`startfile` returns as soon as the associated application is launched.
|
||||
There is no option to wait for the application to close, and no way to retrieve
|
||||
the application's exit status. The *path* parameter is relative to the current
|
||||
directory. If you want to use an absolute path, make sure the first character
|
||||
is not a slash (``'/'``); the underlying Win32 :c:func:`ShellExecute` function
|
||||
doesn't work if it is. Use the :func:`os.path.normpath` function to ensure that
|
||||
the path is properly encoded for Win32.
|
||||
directory or *cwd*. If you want to use an absolute path, make sure the first
|
||||
character is not a slash (``'/'``) Use :mod:`pathlib` or the
|
||||
:func:`os.path.normpath` function to ensure that paths are properly encoded for
|
||||
Win32.
|
||||
|
||||
To reduce interpreter startup overhead, the Win32 :c:func:`ShellExecute`
|
||||
function is not resolved until this function is first called. If the function
|
||||
|
@ -4183,8 +4195,14 @@ written in Python, such as a mail server's external command delivery program.
|
|||
|
||||
.. audit-event:: os.startfile path,operation os.startfile
|
||||
|
||||
.. audit-event:: os.startfile/2 path,operation,arguments,cwd,show_cmd os.startfile
|
||||
|
||||
.. availability:: Windows.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Added the *arguments*, *cwd* and *show_cmd* arguments, and the
|
||||
``os.startfile/2`` audit event.
|
||||
|
||||
|
||||
.. function:: system(command)
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@ from os import path
|
|||
startfile = support.get_attribute(os, 'startfile')
|
||||
|
||||
|
||||
@unittest.skipIf(platform.win32_is_iot(), "starting files is not supported on Windows IoT Core or nanoserver")
|
||||
class TestCase(unittest.TestCase):
|
||||
def test_nonexisting(self):
|
||||
self.assertRaises(OSError, startfile, "nonexisting.vbs")
|
||||
|
||||
@unittest.skipIf(platform.win32_is_iot(), "starting files is not supported on Windows IoT Core or nanoserver")
|
||||
def test_empty(self):
|
||||
# We need to make sure the child process starts in a directory
|
||||
# we're not about to delete. If we're running under -j, that
|
||||
|
@ -32,6 +32,14 @@ class TestCase(unittest.TestCase):
|
|||
empty = path.join(path.dirname(__file__), "empty.vbs")
|
||||
startfile(empty)
|
||||
startfile(empty, "open")
|
||||
startfile(empty, cwd=path.dirname(sys.executable))
|
||||
|
||||
def test_python(self):
|
||||
# Passing "-V" ensures that it closes quickly, though still not
|
||||
# quickly enough that we can run in the test directory
|
||||
cwd, name = path.split(sys.executable)
|
||||
startfile(name, arguments="-V", cwd=cwd)
|
||||
startfile(name, arguments="-V", cwd=cwd, show_cmd=0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Adds additional arguments to :func:`os.startfile` function.
|
|
@ -7125,7 +7125,8 @@ os_abort(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|||
#if defined(MS_WINDOWS)
|
||||
|
||||
PyDoc_STRVAR(os_startfile__doc__,
|
||||
"startfile($module, /, filepath, operation=<unrepresentable>)\n"
|
||||
"startfile($module, /, filepath, operation=<unrepresentable>,\n"
|
||||
" arguments=<unrepresentable>, cwd=None, show_cmd=1)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Start a file with its associated application.\n"
|
||||
|
@ -7137,6 +7138,16 @@ PyDoc_STRVAR(os_startfile__doc__,
|
|||
"When another \"operation\" is given, it specifies what should be done with\n"
|
||||
"the file. A typical operation is \"print\".\n"
|
||||
"\n"
|
||||
"\"arguments\" is passed to the application, but should be omitted if the\n"
|
||||
"file is a document.\n"
|
||||
"\n"
|
||||
"\"cwd\" is the working directory for the operation. If \"filepath\" is\n"
|
||||
"relative, it will be resolved against this directory. This argument\n"
|
||||
"should usually be an absolute path.\n"
|
||||
"\n"
|
||||
"\"show_cmd\" can be used to override the recommended visibility option.\n"
|
||||
"See the Windows ShellExecute documentation for values.\n"
|
||||
"\n"
|
||||
"startfile returns as soon as the associated application is launched.\n"
|
||||
"There is no option to wait for the application to close, and no way\n"
|
||||
"to retrieve the application\'s exit status.\n"
|
||||
|
@ -7150,20 +7161,24 @@ PyDoc_STRVAR(os_startfile__doc__,
|
|||
|
||||
static PyObject *
|
||||
os_startfile_impl(PyObject *module, path_t *filepath,
|
||||
const Py_UNICODE *operation);
|
||||
const Py_UNICODE *operation, const Py_UNICODE *arguments,
|
||||
path_t *cwd, int show_cmd);
|
||||
|
||||
static PyObject *
|
||||
os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = {"filepath", "operation", NULL};
|
||||
static const char * const _keywords[] = {"filepath", "operation", "arguments", "cwd", "show_cmd", NULL};
|
||||
static _PyArg_Parser _parser = {NULL, _keywords, "startfile", 0};
|
||||
PyObject *argsbuf[2];
|
||||
PyObject *argsbuf[5];
|
||||
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
|
||||
path_t filepath = PATH_T_INITIALIZE("startfile", "filepath", 0, 0);
|
||||
const Py_UNICODE *operation = NULL;
|
||||
const Py_UNICODE *arguments = NULL;
|
||||
path_t cwd = PATH_T_INITIALIZE("startfile", "cwd", 1, 0);
|
||||
int show_cmd = 1;
|
||||
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 5, 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
|
@ -7173,20 +7188,54 @@ os_startfile(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
|
|||
if (!noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
if (!PyUnicode_Check(args[1])) {
|
||||
_PyArg_BadArgument("startfile", "argument 'operation'", "str", args[1]);
|
||||
goto exit;
|
||||
if (args[1]) {
|
||||
if (!PyUnicode_Check(args[1])) {
|
||||
_PyArg_BadArgument("startfile", "argument 'operation'", "str", args[1]);
|
||||
goto exit;
|
||||
}
|
||||
#if USE_UNICODE_WCHAR_CACHE
|
||||
operation = _PyUnicode_AsUnicode(args[1]);
|
||||
#else /* USE_UNICODE_WCHAR_CACHE */
|
||||
operation = PyUnicode_AsWideCharString(args[1], NULL);
|
||||
#endif /* USE_UNICODE_WCHAR_CACHE */
|
||||
if (operation == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
}
|
||||
#if USE_UNICODE_WCHAR_CACHE
|
||||
operation = _PyUnicode_AsUnicode(args[1]);
|
||||
#else /* USE_UNICODE_WCHAR_CACHE */
|
||||
operation = PyUnicode_AsWideCharString(args[1], NULL);
|
||||
#endif /* USE_UNICODE_WCHAR_CACHE */
|
||||
if (operation == NULL) {
|
||||
if (args[2]) {
|
||||
if (!PyUnicode_Check(args[2])) {
|
||||
_PyArg_BadArgument("startfile", "argument 'arguments'", "str", args[2]);
|
||||
goto exit;
|
||||
}
|
||||
#if USE_UNICODE_WCHAR_CACHE
|
||||
arguments = _PyUnicode_AsUnicode(args[2]);
|
||||
#else /* USE_UNICODE_WCHAR_CACHE */
|
||||
arguments = PyUnicode_AsWideCharString(args[2], NULL);
|
||||
#endif /* USE_UNICODE_WCHAR_CACHE */
|
||||
if (arguments == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
}
|
||||
if (args[3]) {
|
||||
if (!path_converter(args[3], &cwd)) {
|
||||
goto exit;
|
||||
}
|
||||
if (!--noptargs) {
|
||||
goto skip_optional_pos;
|
||||
}
|
||||
}
|
||||
show_cmd = _PyLong_AsInt(args[4]);
|
||||
if (show_cmd == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
skip_optional_pos:
|
||||
return_value = os_startfile_impl(module, &filepath, operation);
|
||||
return_value = os_startfile_impl(module, &filepath, operation, arguments, &cwd, show_cmd);
|
||||
|
||||
exit:
|
||||
/* Cleanup for filepath */
|
||||
|
@ -7195,6 +7244,12 @@ exit:
|
|||
#if !USE_UNICODE_WCHAR_CACHE
|
||||
PyMem_Free((void *)operation);
|
||||
#endif /* USE_UNICODE_WCHAR_CACHE */
|
||||
/* Cleanup for arguments */
|
||||
#if !USE_UNICODE_WCHAR_CACHE
|
||||
PyMem_Free((void *)arguments);
|
||||
#endif /* USE_UNICODE_WCHAR_CACHE */
|
||||
/* Cleanup for cwd */
|
||||
path_cleanup(&cwd);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
@ -9208,4 +9263,4 @@ exit:
|
|||
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
||||
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
||||
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
|
||||
/*[clinic end generated code: output=ede310b1d316d2b2 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=65a85d7d3f2c487e input=a9049054013a1b77]*/
|
||||
|
|
|
@ -12485,6 +12485,9 @@ check_ShellExecute()
|
|||
os.startfile
|
||||
filepath: path_t
|
||||
operation: Py_UNICODE = NULL
|
||||
arguments: Py_UNICODE = NULL
|
||||
cwd: path_t(nullable=True) = None
|
||||
show_cmd: int = 1
|
||||
|
||||
Start a file with its associated application.
|
||||
|
||||
|
@ -12495,6 +12498,16 @@ application (if any) its extension is associated.
|
|||
When another "operation" is given, it specifies what should be done with
|
||||
the file. A typical operation is "print".
|
||||
|
||||
"arguments" is passed to the application, but should be omitted if the
|
||||
file is a document.
|
||||
|
||||
"cwd" is the working directory for the operation. If "filepath" is
|
||||
relative, it will be resolved against this directory. This argument
|
||||
should usually be an absolute path.
|
||||
|
||||
"show_cmd" can be used to override the recommended visibility option.
|
||||
See the Windows ShellExecute documentation for values.
|
||||
|
||||
startfile returns as soon as the associated application is launched.
|
||||
There is no option to wait for the application to close, and no way
|
||||
to retrieve the application's exit status.
|
||||
|
@ -12506,8 +12519,9 @@ the underlying Win32 ShellExecute function doesn't work if it is.
|
|||
|
||||
static PyObject *
|
||||
os_startfile_impl(PyObject *module, path_t *filepath,
|
||||
const Py_UNICODE *operation)
|
||||
/*[clinic end generated code: output=66dc311c94d50797 input=c940888a5390f039]*/
|
||||
const Py_UNICODE *operation, const Py_UNICODE *arguments,
|
||||
path_t *cwd, int show_cmd)
|
||||
/*[clinic end generated code: output=3baa4f9795841880 input=8248997b80669622]*/
|
||||
{
|
||||
HINSTANCE rc;
|
||||
|
||||
|
@ -12521,10 +12535,15 @@ os_startfile_impl(PyObject *module, path_t *filepath,
|
|||
if (PySys_Audit("os.startfile", "Ou", filepath->object, operation) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (PySys_Audit("os.startfile/2", "OuuOi", filepath->object, operation,
|
||||
arguments, cwd->object ? cwd->object : Py_None,
|
||||
show_cmd) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
rc = Py_ShellExecuteW((HWND)0, operation, filepath->wide,
|
||||
NULL, NULL, SW_SHOWNORMAL);
|
||||
arguments, cwd->wide, show_cmd);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (rc <= (HINSTANCE)32) {
|
||||
|
|
Loading…
Reference in New Issue