From f6fe5958c9434aed61c758ba00c6e4a856cc33a1 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 21 Dec 2023 14:18:35 +0100 Subject: [PATCH] gh-113356: Ignore errors in "._ABC.pth" On macOS the system can create a "._" file next to a regular file when the system needs to store metadata that is not supported by the filesystem (such as storing extended attributes on an exFAT filesystem). Those files are binary data and cause startup failures when the base file is a ".pth" file. --- Lib/site.py | 55 +++++++++++-------- Lib/test/test_site.py | 14 +++++ ...-12-21-14-18-24.gh-issue-113356.Vz2osH.rst | 3 + 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/macOS/2023-12-21-14-18-24.gh-issue-113356.Vz2osH.rst diff --git a/Lib/site.py b/Lib/site.py index 2517b7e5f1d22a..10deb3c794e703 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -176,29 +176,40 @@ def addpackage(sitedir, name, known_paths): except OSError: return with f: - for n, line in enumerate(f): - if line.startswith("#"): - continue - if line.strip() == "": - continue - try: - if line.startswith(("import ", "import\t")): - exec(line) + try: + for n, line in enumerate(f): + if line.startswith("#"): continue - line = line.rstrip() - dir, dircase = makepath(sitedir, line) - if not dircase in known_paths and os.path.exists(dir): - sys.path.append(dir) - known_paths.add(dircase) - except Exception as exc: - print("Error processing line {:d} of {}:\n".format(n+1, fullname), - file=sys.stderr) - import traceback - for record in traceback.format_exception(exc): - for line in record.splitlines(): - print(' '+line, file=sys.stderr) - print("\nRemainder of file ignored", file=sys.stderr) - break + if line.strip() == "": + continue + try: + if line.startswith(("import ", "import\t")): + exec(line) + continue + line = line.rstrip() + dir, dircase = makepath(sitedir, line) + if not dircase in known_paths and os.path.exists(dir): + sys.path.append(dir) + known_paths.add(dircase) + except Exception as exc: + print("Error processing line {:d} of {}:\n".format(n+1, fullname), + file=sys.stderr) + import traceback + for record in traceback.format_exception(exc): + for line in record.splitlines(): + print(' '+line, file=sys.stderr) + print("\nRemainder of file ignored", file=sys.stderr) + break + except UnicodeDecodeError: + # MacOS can create files with a "._" prefix in the name + # next to the regular file when the system needs to store + # metadata (such as extended attributes) that the filesystem + # cannot store natively. + # + # Ignore errors when trying to parse these files. + if name.startswith("._") and os.path.exists(os.path.join(sitedir, name[2:])): + return + raise if reset: known_paths = None return known_paths diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 33d0975bda8eaa..d7ec8158bb05ca 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -181,6 +181,20 @@ def test_addpackage_import_bad_pth_file(self): if isinstance(path, str): self.assertNotIn("abc\x00def", path) + def test_addpackage_macOS_resources(self): + # GH-113356 + pth_dir, pth_fn = self.make_pth("dummy\n") + resource_fork = os.path.join(pth_dir, "._" + pth_fn) + with open(resource_fork, "wb") as fp: + self.addCleanup(lambda: os.remove(resource_fork)) + # Some random that that isn't valid UTF-8 + fp.write(b"\xff\xff\xff") + + with captured_stderr() as err_out: + self.assertFalse(site.addpackage(pth_dir, "._" + pth_fn, set())) + self.assertEqual(err_out.getvalue(), "") + + def test_addsitedir(self): # Same tests for test_addpackage since addsitedir() essentially just # calls addpackage() for every .pth file in the directory diff --git a/Misc/NEWS.d/next/macOS/2023-12-21-14-18-24.gh-issue-113356.Vz2osH.rst b/Misc/NEWS.d/next/macOS/2023-12-21-14-18-24.gh-issue-113356.Vz2osH.rst new file mode 100644 index 00000000000000..472e8a2b8bb7d9 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2023-12-21-14-18-24.gh-issue-113356.Vz2osH.rst @@ -0,0 +1,3 @@ +Ignore "._" prefixed pth files when those cannot be parsed. These files are +created on macOS when the system tries store metadata that is not supported +by the filesystem (such as extended attributes on exFAT)