Skip to content

Commit 12650d3

Browse files
committed
Remove current directory from the default used when PATH is unset
* dialog.py (_find_in_path): in case PATH was unset, the default value used to start with a colon, which caused pythondialog to look for the executable in the current directory if (1) the specified executable didn't contain a '/' and (2) the PATH environment variable was unset. This behavior was conformant to what glibc's execvp() function did until version 2.24 from 2016, but wasn't ideal (if a pythondialog-based program had its current directory world-writable, this could be used to trap users whose PATH is unset---which is rather unlikely, fortunately). This suboptimal default behavior when PATH is unset was changed in glibc 2.24, and we are following suit here, i.e., the new default is "/bin:/usr/bin". Also clarify the docstring. * dialog.py (_path_to_executable): return the result after applying os.path.realpath() and clarify the docstring. Using os.path.realpath() at this point ensures that the path to the dialog-like executable is resolved at the time the Dialog instance is created. This way, a pythondialog-based program can change its current directory after creating a Dialog instance without fearing that this might cause subsequent pythondialog calls to fail or to invoke an executable from a different directory. * dialog.py (Dialog.__init__): adapt the docstring to reflect the aforementioned change when PATH is unset (as mentioned above for _find_in_path()). (cherry picked from commit 94c2702)
1 parent 1a3b087 commit 12650d3

File tree

2 files changed

+23
-22
lines changed

2 files changed

+23
-22
lines changed

‎dialog.py‎

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -429,20 +429,18 @@ def _find_in_path(prog_name):
429429
"""Search an executable in the :envvar:`PATH`.
430430
431431
If :envvar:`PATH` is not defined, the default path
432-
``:/bin:/usr/bin`` is used.
432+
``/bin:/usr/bin`` is used.
433433
434-
Return a path to the file or ``None`` if no readable and executable
435-
file is found.
434+
Return a path to the file, or ``None`` if no file with a matching
435+
basename as well as read and execute permissions is found.
436436
437437
Notable exception:
438438
439439
:exc:`PythonDialogOSError`
440440
441441
"""
442442
with _OSErrorHandling():
443-
# Note that the leading empty component in the default value for PATH
444-
# could lead to the returned path not being absolute.
445-
PATH = os.getenv("PATH", ":/bin:/usr/bin") # see the execvp(3) man page
443+
PATH = os.getenv("PATH", "/bin:/usr/bin") # see the execvp(3) man page
446444
for d in PATH.split(os.pathsep):
447445
file_path = os.path.join(d, prog_name)
448446
if os.path.isfile(file_path) \
@@ -455,14 +453,17 @@ def _path_to_executable(f):
455453
"""Find a path to an executable.
456454
457455
Find a path to an executable, using the same rules as the POSIX
458-
exec*p functions (see execvp(3) for instance).
456+
exec*p() functions (see execvp(3) for instance).
459457
460-
If *f* contains a ``/``, it is assumed to be a path and is simply
461-
checked for read and write permissions; otherwise, it is looked for
462-
according to the contents of the :envvar:`PATH` environment
463-
variable, which defaults to ``:/bin:/usr/bin`` if unset.
458+
If *f* contains a ``/`` character, it must be a relative or absolute
459+
path to a file that has read and execute permissions. If *f* does
460+
not contain a ``/`` character, it is looked for according to the
461+
contents of the :envvar:`PATH` environment variable, which defaults
462+
to ``/bin:/usr/bin`` if unset.
464463
465-
The returned path is not necessarily absolute.
464+
The return value is the result of calling :func:`os.path.realpath`
465+
on the path found according to the rules described in the previous
466+
paragraph.
466467
467468
Notable exceptions:
468469
@@ -472,8 +473,7 @@ def _path_to_executable(f):
472473
"""
473474
with _OSErrorHandling():
474475
if '/' in f:
475-
if os.path.isfile(f) and \
476-
os.access(f, os.R_OK | os.X_OK):
476+
if os.path.isfile(f) and os.access(f, os.R_OK | os.X_OK):
477477
res = f
478478
else:
479479
raise ExecutableNotFound("%s cannot be read and executed" % f)
@@ -484,7 +484,7 @@ def _path_to_executable(f):
484484
"can't find the executable for the dialog-like "
485485
"program")
486486

487-
return res
487+
return os.path.realpath(res)
488488

489489

490490
def _to_onoff(val):
@@ -902,10 +902,11 @@ def __init__(self, dialog="dialog", DIALOGRC=None,
902902
903903
:param str dialog:
904904
name of (or path to) the :program:`dialog`-like program to
905-
use; if it contains a ``'/'``, it is assumed to be a path and
906-
is used as is; otherwise, it is looked for according to the
907-
contents of the :envvar:`PATH` environment variable, which
908-
defaults to ``":/bin:/usr/bin"`` if unset.
905+
use. If it contains a slash (``/``), it must be a relative or
906+
absolute path to a file that has read and execute permissions,
907+
and is used as is; otherwise, it is looked for according to
908+
the contents of the :envvar:`PATH` environment variable, which
909+
defaults to ``/bin:/usr/bin`` if unset.
909910
:param str DIALOGRC:
910911
string to pass to the :program:`dialog`-like program as the
911912
:envvar:`DIALOGRC` environment variable, or ``None`` if no

‎doc/intro/intro.rst‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ The *dialog* parameter indicates the executable to use to invoke the backend
1414
(which must be compatible with dialog_). For instance, one might use something
1515
like ``dialog="/home/dave/src/dialog-1.2-20140219/dialog"``. The default value
1616
is ``"dialog"``, and since it does not contain any slash (``/``), it is looked
17-
up in the :envvar:`PATH` environment variable. See :meth:`Dialog.__init__` for
18-
a description of all parameters that can be passed to the :class:`Dialog`
19-
constructor.
17+
up according to the :envvar:`PATH` environment variable. See
18+
:meth:`Dialog.__init__` for a description of all parameters that can be passed
19+
to the :class:`Dialog` constructor.
2020

2121
.. _dialog: http://invisible-island.net/dialog/dialog.html
2222

0 commit comments

Comments
 (0)