Skip to content

Commit 3f3018c

Browse files
committed
In restart check whether file is executable
1 parent fc27a46 commit 3f3018c

3 files changed

Lines changed: 68 additions & 13 deletions

File tree

test/unit/lib/test_lib_file.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,59 @@
66

77
import pytest
88

9-
from trepan.lib import file as Mfile
9+
from trepan.lib.file import executable, lookupmodule, readable
1010

1111

1212
def test_lookupmodule():
13-
m, f = Mfile.lookupmodule("os.path")
13+
m, f = lookupmodule("os.path")
1414
assert f
1515
assert m
16-
m, f = Mfile.lookupmodule(__file__)
16+
m, f = lookupmodule(__file__)
1717
assert f
1818
assert m is None
19-
assert (None, None) == Mfile.lookupmodule("fafdsafdsa")
19+
assert (None, None) == lookupmodule("fafdsafdsa")
2020
return
2121

2222

23-
pytest.mark.skipif(sys.platform == "win32", reason="Does not work on MS Windows")
23+
@pytest.mark.skipif(sys.platform == "win32", reason="Does not work on MS Windows")
24+
def test_readable():
25+
assert not readable("fdafdsa")
26+
i = 1
27+
for mode, can_read in [
28+
(stat.S_IRUSR, True), # 1
29+
(stat.S_IWUSR, False), # 2
30+
(stat.S_IRGRP, True), # 3
31+
(stat.S_IWGRP, False), # 4
32+
(stat.S_IROTH, True), # 5
33+
(stat.S_IWOTH, False), # 6
34+
]:
35+
f = tempfile.NamedTemporaryFile()
36+
os.chmod(f.name, mode)
37+
assert can_read == readable(f.name), f"Test {i} file mode {oct(mode)}"
38+
f.close()
39+
i += 1
40+
pass
41+
return
2442

2543

26-
def test_readable():
27-
assert not Mfile.readable("fdafdsa")
28-
for mode, can_read in [(stat.S_IRUSR, True), (stat.S_IWUSR, False)]:
44+
@pytest.mark.skipif(sys.platform == "win32", reason="Does not work on MS Windows")
45+
def test_executable():
46+
i = 1
47+
for mode, can_read in [
48+
(stat.S_IRUSR | stat.S_IXUSR, True), # 1
49+
(stat.S_IRUSR, False), # 2
50+
(stat.S_IWGRP | stat.S_IXGRP, False), # 3
51+
(stat.S_IWGRP, False), # 4
52+
(stat.S_IRGRP | stat.S_IXGRP, True), # 5
53+
(stat.S_IRGRP, False), # 6
54+
(stat.S_IWGRP, False), # 7
55+
(stat.S_IROTH | stat.S_IXOTH, True), # 8
56+
]:
2957
f = tempfile.NamedTemporaryFile()
3058
os.chmod(f.name, mode)
31-
assert can_read == Mfile.readable(f.name)
59+
60+
assert can_read == executable(f.name), f"Test {i} file mode {oct(mode)}"
3261
f.close()
62+
i += 1
3363
pass
3464
return

trepan/lib/file.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import os
1919
import stat
2020
import sys
21+
from typing import Optional
2122

2223
import pyficache
2324

@@ -26,7 +27,7 @@ def file_list():
2627
return list(set(pyficache.cached_files() + list(pyficache.file2file_remap.keys())))
2728

2829

29-
def is_compiled_py(filename):
30+
def is_compiled_py(filename) -> bool:
3031
"""
3132
Given a file name, return True if the suffix is pyo or pyc (an
3233
optimized bytecode file).
@@ -35,9 +36,25 @@ def is_compiled_py(filename):
3536

3637

3738
READABLE_MASK = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
39+
EXECUTABLE_MASK = stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR
3840

3941

40-
def readable(path):
42+
def executable(path: str) -> Optional[bool]:
43+
"""Test whether a path exists and is readable. Returns None for
44+
broken symbolic links or a failing stat() and False if
45+
the file exists but does not have read permission. True is returned
46+
if the file is readable."""
47+
try:
48+
st = os.stat(path)
49+
if 0 == st.st_mode & READABLE_MASK:
50+
return False
51+
return 0 != st.st_mode & EXECUTABLE_MASK
52+
except os.error:
53+
return None
54+
return True
55+
56+
57+
def readable(path: str) -> Optional[bool]:
4158
"""Test whether a path exists and is readable. Returns None for
4259
broken symbolic links or a failing stat() and False if
4360
the file exists but does not have read permission. True is returned

trepan/processor/command/restart.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
2-
# Copyright (C) 2009, 2013, 2015, 2020, 2023 Rocky Bernstein
2+
# Copyright (C) 2009, 2013, 2015, 2020, 2023-2024
3+
# Rocky Bernstein
34
#
45
# This program is free software: you can redistribute it and/or modify
56
# it under the terms of the GNU General Public License as published by
@@ -16,6 +17,7 @@
1617
import atexit
1718
import os
1819

20+
from trepan.lib.file import executable
1921
from trepan.misc import wrapped_lines
2022

2123
# Our local modules
@@ -37,11 +39,17 @@ class RestartCommand(DebuggerCommand):
3739

3840
short_help = "(Hard) restart of program via execv()"
3941

42+
# FIXME: add mechanism for adding python interpreter.
4043
DebuggerCommand.setup(locals(), category="support", max_args=0)
4144

4245
def run(self, args):
4346
sys_argv = self.debugger.restart_argv()
4447
if sys_argv and len(sys_argv) > 0:
48+
program_file = sys_argv[0]
49+
if not executable(sys_argv[0]):
50+
self.errmsg(f'File "{program_file}" is not marked executable.')
51+
return
52+
4553
confirmed = self.confirm("Restart (execv)", False)
4654
if confirmed:
4755
self.msg(
@@ -55,7 +63,7 @@ def run(self, args):
5563
atexit._run_exitfuncs()
5664
except Exception:
5765
pass
58-
os.execvp(sys_argv[0], sys_argv)
66+
os.execvp(program_file, sys_argv)
5967
pass
6068
pass
6169
else:

0 commit comments

Comments
 (0)