jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1330618?usp=email )
Change subject: Use pathlib for path containment
......................................................................
Use pathlib for path containment
Use Path.is_relative_to when checking configuration and module paths.
This avoids treating similarly prefixed sibling directories as children and
uses platform-aware POSIX and Windows path semantics.
Change-Id: I987f9ced796504559f27e104f61b0f4703ab6477
---
M pywikibot/config.py
M pywikibot/version.py
M tests/__init__.py
A tests/config_tests.py
M tests/version_tests.py
5 files changed, 128 insertions(+), 6 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/config.py b/pywikibot/config.py
index 4830dd2..f60988d 100644
--- a/pywikibot/config.py
+++ b/pywikibot/config.py
@@ -913,9 +913,16 @@
def shortpath(path: str) -> str:
- """Return a file path relative to config.base_dir."""
- if path.startswith(base_dir):
- return path[len(base_dir) + len(os.path.sep):]
+ """Return a file path relative to config.base_dir.
+
+ .. version-changed:: 11.8
+ Path components are used to determine whether *path* is inside
+ :data:`base_dir`.
+ """
+ path_obj = Path(path)
+ if path_obj.is_relative_to(base_dir):
+ relative_path = path_obj.relative_to(base_dir)
+ return '' if relative_path == Path('.') else str(relative_path)
return path
diff --git a/pywikibot/version.py b/pywikibot/version.py
index 486c706..8d76fe8 100644
--- a/pywikibot/version.py
+++ b/pywikibot/version.py
@@ -251,14 +251,18 @@
:param module: The module instance.
:type module: module
:return: The filename if it's a pywikibot module otherwise None.
+
+ .. version-changed:: 11.8
+ Path components are used to determine whether the module is
+ inside the Pywikibot program directory.
"""
if hasattr(module, '__file__'):
filename = module.__file__
if not filename or not os.path.exists(filename):
return None
- program_dir = _get_program_dir()
- if filename.startswith(program_dir):
+ program_dir = Path(_get_program_dir())
+ if Path(filename).is_relative_to(program_dir):
return filename
return None
diff --git a/tests/__init__.py b/tests/__init__.py
index 4813220..b38fddb 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -96,6 +96,7 @@
'bot',
'category',
'collections',
+ 'config',
'cosmetic_changes',
'date',
'datasite',
diff --git a/tests/config_tests.py b/tests/config_tests.py
new file mode 100755
index 0000000..870be2b
--- /dev/null
+++ b/tests/config_tests.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+#
+# (C) Pywikibot team, 2026
+#
+# Distributed under the terms of the MIT license.
+#
+"""Test cases for the :mod:`config` module."""
+from __future__ import annotations
+
+import unittest
+from contextlib import suppress
+from pathlib import Path, PurePosixPath, PureWindowsPath
+from unittest.mock import patch
+
+from pywikibot import config
+from tests.aspects import TestCase
+
+
+class ConfigPathTestCase(TestCase):
+
+ """Test configuration path helpers."""
+
+ net = False
+
+ def test_shortpath_path_containment(self) -> None:
+ """Test shortpath uses path components for containment."""
+ base_dir = Path.cwd() / 'pywikibot'
+ child = base_dir / 'data' / 'file.txt'
+ sibling = base_dir.with_name(base_dir.name + '-extra') / 'file.txt'
+
+ with patch.object(config, 'base_dir', str(base_dir)):
+ self.assertEqual(config.shortpath(str(base_dir)), '')
+ self.assertEqual(config.shortpath(str(child)),
+ str(Path('data') / 'file.txt'))
+ self.assertEqual(config.shortpath(str(sibling)), str(sibling))
+
+ def test_shortpath_path_flavours(self) -> None:
+ """Test POSIX, Windows drive, and Windows UNC path handling."""
+ cases = (
+ (PurePosixPath,
+ '/srv/pywikibot',
+ '/srv/pywikibot/data/file.txt',
+ '/srv/pywikibot-extra/file.txt',
+ 'data/file.txt'),
+ (PureWindowsPath,
+ r'C:\Users\bot\pywikibot',
+ r'C:\Users\bot\pywikibot\data\file.txt',
+ r'C:\Users\bot\pywikibot-extra\file.txt',
+ r'data\file.txt'),
+ (PureWindowsPath,
+ r'\\server\share\pywikibot',
+ r'\\server\share\pywikibot\data\file.txt',
+ r'\\server\share\pywikibot-extra\file.txt',
+ r'data\file.txt'),
+ )
+
+ for path_type, base, child, sibling, relative_child in cases:
+ with self.subTest(path_type=path_type, base=base), \
+ patch.object(config, 'Path', path_type), \
+ patch.object(config, 'base_dir', base):
+ self.assertEqual(config.shortpath(base), '')
+ self.assertEqual(config.shortpath(child), relative_child)
+ self.assertEqual(config.shortpath(sibling), sibling)
+
+ def test_shortpath_different_windows_drive(self) -> None:
+ """Test paths on another Windows drive are not shortened."""
+ base = r'C:\Users\bot\pywikibot'
+ path = r'D:\pywikibot\file.txt'
+ with patch.object(config, 'Path', PureWindowsPath), \
+ patch.object(config, 'base_dir', base):
+ self.assertEqual(config.shortpath(path), path)
+
+
+if __name__ == '__main__':
+ with suppress(SystemExit):
+ unittest.main()
diff --git a/tests/version_tests.py b/tests/version_tests.py
index c34aa0c..16dd2ba 100755
--- a/tests/version_tests.py
+++ b/tests/version_tests.py
@@ -8,9 +8,11 @@
from __future__ import annotations
import time
+import types
import unittest
from contextlib import suppress
-from pathlib import Path
+from pathlib import Path, PurePosixPath, PureWindowsPath
+from unittest.mock import patch
from pywikibot import version
from tests.aspects import TestCase
@@ -41,6 +43,38 @@
self.assertEqual(hsh, '')
self.assertEqual(dummy, [])
+ def test_module_filename_path_containment(self) -> None:
+ """Test module filenames are contained by path components."""
+ cases = (
+ (PurePosixPath,
+ '/srv/pywikibot',
+ '/srv/pywikibot/module.py',
+ '/srv/pywikibot-extra/module.py'),
+ (PureWindowsPath,
+ r'C:\Users\bot\pywikibot',
+ r'C:\Users\bot\pywikibot\module.py',
+ r'C:\Users\bot\pywikibot-extra\module.py'),
+ (PureWindowsPath,
+ r'\\server\share\pywikibot',
+ r'\\server\share\pywikibot\module.py',
+ r'\\server\share\pywikibot-extra\module.py'),
+ )
+
+ for path_type, program_dir, module_path, sibling_path in cases:
+ module = types.SimpleNamespace(__file__=module_path)
+ with self.subTest(path_type=path_type,
+ program_dir=program_dir), \
+ patch.object(version, 'Path', path_type), \
+ patch.object(version, '_get_program_dir',
+ return_value=program_dir), \
+ patch.object(version.os.path, 'exists',
+ return_value=True):
+ self.assertEqual(version.get_module_filename(module),
+ module_path)
+
+ module.__file__ = sibling_path
+ self.assertIsNone(version.get_module_filename(module))
+
class RemoteVersionTestCase(TestCase):
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1330618?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: I987f9ced796504559f27e104f61b0f4703ab6477
Gerrit-Change-Number: 1330618
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]