Skip to content

Commit

Permalink
apport_python_hook: Fix FileNotFoundError if cwd was deleted
Browse files Browse the repository at this point in the history
Run following Shell script:

```sh
echo "raise ValueError()" > /tmp/failure.py
mkdir /tmp/foo
cd /tmp/foo/
rmdir /tmp/foo/
python3 ../failure.py
```

Then Apport's Python hook fails:

```
Traceback (most recent call last):
  File "../failure.py", line 1, in <module>
    raise ValueError()
ValueError
Error in sys.excepthook:
Traceback (most recent call last):
  File "<frozen importlib._bootstrap_external>", line 1372, in _path_importer_cache
KeyError: '..'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "apport_python_hook.py", line 55, in apport_excepthook
    if not enabled():
  File "apport_python_hook.py", line 24, in enabled
    import re
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1002, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 945, in _find_spec
  File "<frozen importlib._bootstrap_external>", line 1439, in find_spec
  File "<frozen importlib._bootstrap_external>", line 1408, in _get_spec
  File "<frozen importlib._bootstrap_external>", line 1374, in _path_importer_cache
  File "<frozen importlib._bootstrap_external>", line 1350, in _path_hooks
  File "<frozen importlib._bootstrap_external>", line 1632, in path_hook_for_FileFinder
  File "<frozen importlib._bootstrap_external>", line 1505, in __init__
FileNotFoundError: [Errno 2] No such file or directory

Original exception was:
Traceback (most recent call last):
  File "../failure.py", line 1, in <module>
    raise ValueError()
ValueError
```

Bug: https://launchpad.net/bugs/1979637
Signed-off-by: Benjamin Drung <[email protected]>
  • Loading branch information
bdrung committed Jun 23, 2022
1 parent fa91544 commit 97f3e56
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
18 changes: 9 additions & 9 deletions apport_python_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def enabled():

# This doesn't use apport.packaging.enabled() because it is too heavyweight
# See LP: #528355
# pylint: disable=import-outside-toplevel; for Python starup time
import re

try:
# pylint: disable=import-outside-toplevel; for Python starup time
import re

with open(CONFIG) as f:
conf = f.read()
return re.search(r"^\s*enabled\s*=\s*0\s*$", conf, re.M) is None
Expand Down Expand Up @@ -55,15 +55,15 @@ def apport_excepthook(exc_type, exc_obj, exc_tb):
if not enabled():
return

import io
import os
import re
import traceback

try:
import io
import os
import re
import traceback

import apport.report
from apport.fileutils import get_recent_crashes, likely_packaged
except ImportError:
except (ImportError, IOError):
return

# apport will look up the package from the executable path.
Expand Down
55 changes: 55 additions & 0 deletions tests/integration/test_python_crashes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import atexit
import os
import re
import shutil
import stat
import subprocess
Expand All @@ -26,6 +27,8 @@


class T(unittest.TestCase):
maxDiff = None

@classmethod
def setUpClass(cls):
cls.env = os.environ | local_test_environment()
Expand Down Expand Up @@ -295,6 +298,58 @@ def _assert_no_reports(self):
len(reports), 0, "no crash reports present (cwd: %s)" % os.getcwd()
)

def test_deleted_working_directory(self):
"""Relative Python script from deleted working directory."""
orig_cwd = os.getcwd()
try:
with tempfile.TemporaryDirectory(dir="/var/tmp") as tmpdir:
deleted_dir = os.path.join(tmpdir, "gone")
os.mkdir(deleted_dir)
os.chdir(deleted_dir)
os.rmdir(deleted_dir)

with open(os.path.join(tmpdir, "script.py"), "w") as script:
script.write(
"import apport_python_hook\n"
"apport_python_hook.install()\n"
"raise ValueError()\n"
)

env = os.environ.copy()
env["PYTHONPATH"] = orig_cwd
process = subprocess.run(
["python3", "../script.py"],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
finally:
os.chdir(orig_cwd)

# Workaround for pytest bug.
# See https://github.com/pytest-dev/pytest-cov/issues/541
stderr = re.sub(
r"^Error processing [A-Za-z0-9 /-]*init_cov_core.pth:\n[\S\W]*\n"
r"pytest-cov: Failed to setup subprocess coverage.*\n",
"",
process.stderr.decode(),
)

self.assertEqual(
stderr,
textwrap.dedent(
"""\
Traceback (most recent call last):
File "../script.py", line 3, in <module>
raise ValueError()
ValueError
"""
),
)
self.assertEqual(process.stdout.decode(), "")
self.assertEqual(process.returncode, 1)
self._assert_no_reports()

def test_interactive(self):
"""interactive Python sessions never generate a report."""

Expand Down

0 comments on commit 97f3e56

Please sign in to comment.