jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1354003?usp=email )
Change subject: family: Fix ZIP discovery and duplicate import paths
......................................................................
family: Fix ZIP discovery and duplicate import paths
Find parent ZIP archives before rejecting family folders as missing.
Preserve archive member paths and restrict subdirectory discovery to
the requested prefix so registered families can be imported.
Only append a family directory to sys.path when it is not already
present, avoiding duplicate entries when loading distinct families.
Change-Id: If0ec6843cd092071f6b8ddefd5c323d69f70eeb7
---
M pywikibot/config.py
M pywikibot/family.py
M tests/config_tests.py
3 files changed, 76 insertions(+), 12 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/config.py b/pywikibot/config.py
index 7e47bfa..c5e2f09 100644
--- a/pywikibot/config.py
+++ b/pywikibot/config.py
@@ -434,12 +434,6 @@
suffix = '_family.py'
folder = Path(folder_path)
- if not folder.exists():
- if not_exists_ok:
- return
- raise FileNotFoundError(
- f'Family folder {folder_path!r} does not exist')
-
if folder.is_dir():
for file_path in folder.iterdir():
if file_path.name.endswith(suffix):
@@ -452,20 +446,33 @@
path = folder
if not is_zipfile(path):
for parent in path.parents:
- if is_zipfile(path):
+ if is_zipfile(parent):
path = parent
break
else:
+ if not folder.exists():
+ if not_exists_ok:
+ return
+ raise FileNotFoundError(
+ f'Family folder {folder_path!r} does not exist')
raise NotADirectoryError('20', 'Not a directory', folder_path)
# read the family files from zip folder
- # assume that all files ending with suffix reside in family folder
- zip_file = ZipFile(path)
- for file_name in zip_file.namelist():
+ with ZipFile(path) as zip_file:
+ file_names = zip_file.namelist()
+ if folder != path:
+ prefix = folder.relative_to(path).as_posix() + '/'
+ file_names = [name for name in file_names if name.startswith(prefix)]
+ if not file_names:
+ if not_exists_ok:
+ return
+ raise FileNotFoundError(
+ f'Family folder {folder_path!r} does not exist')
+ for file_name in file_names:
if file_name.endswith(suffix):
file_path = Path(file_name)
family_name = file_path.name.removesuffix(suffix)
- family_files[family_name] = str(folder / file_path.name)
+ family_files[family_name] = str(path / file_path)
# Get the names of all known families, and initialize with empty dictionaries.
diff --git a/pywikibot/family.py b/pywikibot/family.py
index 66afc3d..b5613ae 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -385,7 +385,9 @@
# RuntimeWarning's while loading.
with warnings.catch_warnings():
warnings.simplefilter('ignore', RuntimeWarning)
- sys.path.append(dirname(family_file))
+ family_dir = dirname(family_file)
+ if family_dir not in sys.path:
+ sys.path.append(family_dir)
mod = import_module(splitext(basename(family_file))[0])
except ImportError:
raise UnknownFamilyError(f'Family {fam} does not exist')
diff --git a/tests/config_tests.py b/tests/config_tests.py
index 870be2b..91b7043 100755
--- a/tests/config_tests.py
+++ b/tests/config_tests.py
@@ -7,12 +7,16 @@
"""Test cases for the :mod:`config` module."""
from __future__ import annotations
+import sys
import unittest
from contextlib import suppress
from pathlib import Path, PurePosixPath, PureWindowsPath
+from tempfile import TemporaryDirectory
from unittest.mock import patch
+from zipfile import ZipFile
from pywikibot import config
+from pywikibot.family import Family
from tests.aspects import TestCase
@@ -22,6 +26,57 @@
net = False
+ def test_register_families_zip(self) -> None:
+ """Discover and load archived families without duplicate paths."""
+ with TemporaryDirectory() as tmp, \
+ patch.dict(config.family_files, {}, clear=True), \
+ patch.dict(Family._families, {}, clear=True), \
+ patch.dict(sys.modules), \
+ patch.object(sys, 'path', sys.path.copy()):
+ archive = Path(tmp) / 'families.zip'
+ folder = archive / 'families'
+ expected = {}
+ with ZipFile(archive, 'w') as zip_file:
+ for name in ('zipalpha', 'zipbeta'):
+ filename = name + '_family.py'
+ zip_file.writestr(
+ 'families/' + filename,
+ 'from pywikibot.family import Family as BaseFamily\n'
+ 'class Family(BaseFamily):\n'
+ f' name = {name!r}\n'
+ " langs = {'en': 'example.invalid'}\n")
+ expected[name] = str(folder / filename)
+ zip_file.writestr('other/unused_family.py', '')
+
+ config.register_families_folder(folder, not_exists_ok=True)
+ self.assertEqual(config.family_files, expected)
+ for name in expected:
+ self.assertEqual(Family.load(name).name, name)
+ self.assertEqual(sys.path.count(str(folder)), 1)
+
+ config.register_families_folder(archive)
+ expected['unused'] = str(archive / 'other/unused_family.py')
+ self.assertEqual(config.family_files, expected)
+
+ def test_register_families_missing_folder(self) -> None:
+ """Retain missing-folder and non-directory error handling."""
+ with TemporaryDirectory() as tmp, \
+ patch.dict(config.family_files, {}, clear=True):
+ archive = Path(tmp) / 'families.zip'
+ with ZipFile(archive, 'w') as zip_file:
+ zip_file.writestr('families/example_family.py', '')
+ for folder in (Path(tmp) / 'missing', archive / 'missing'):
+ with self.subTest(folder=folder):
+ with self.assertRaises(FileNotFoundError):
+ config.register_families_folder(folder)
+ config.register_families_folder(folder,
+ not_exists_ok=True)
+ file_path = Path(tmp) / 'plain.txt'
+ file_path.touch()
+ with self.assertRaises(NotADirectoryError):
+ config.register_families_folder(file_path)
+ self.assertEqual(config.family_files, {})
+
def test_shortpath_path_containment(self) -> None:
"""Test shortpath uses path components for containment."""
base_dir = Path.cwd() / 'pywikibot'
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1354003?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.wikimedia.org/r/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: If0ec6843cd092071f6b8ddefd5c323d69f70eeb7
Gerrit-Change-Number: 1354003
Gerrit-PatchSet: 2
Gerrit-Owner: Mahveotm <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]