.. availability:: Windows.
-.. function:: startfile(path[, operation])
+.. function:: startfile(path, [operation], [arguments], [cwd], [show_cmd])
Start a file with its associated application.
``'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
.. 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)
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
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()
#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"
"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"
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;
}
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 */
#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;
}
#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]*/
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.
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.
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;
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) {