jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1149831?usp=email )
Change subject: IMPR: replace codecs.open with pathlib.Path in
LoginManager.readPassword
......................................................................
IMPR: replace codecs.open with pathlib.Path in LoginManager.readPassword
- use pathlib.Path in LoginManager.readPassword
- update login_tests
- update type hint for file_mode_checker
Bug: T395187
Change-Id: I25af80fd37b55e84f33fc78ad86ceacfacd6476b
---
M pywikibot/login.py
M pywikibot/tools/__init__.py
M tests/login_tests.py
3 files changed, 55 insertions(+), 25 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/login.py b/pywikibot/login.py
index b73776f..f24b4fc 100644
--- a/pywikibot/login.py
+++ b/pywikibot/login.py
@@ -1,17 +1,17 @@
"""Library to log the bot in to a wiki account."""
#
-# (C) Pywikibot team, 2003-2024
+# (C) Pywikibot team, 2003-2025
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations
-import codecs
import datetime
import os
import re
import webbrowser
from enum import IntEnum
+from pathlib import Path
from typing import Any
from warnings import warn
@@ -19,7 +19,12 @@
from pywikibot import __url__, config
from pywikibot.comms import http
from pywikibot.exceptions import APIError, NoUsernameError
-from pywikibot.tools import deprecated, file_mode_checker, normalize_username
+from pywikibot.tools import (
+ PYTHON_VERSION,
+ deprecated,
+ file_mode_checker,
+ normalize_username,
+)
try:
@@ -208,23 +213,41 @@
('wikipedia', 'my_wikipedia_user', 'my_wikipedia_pass')
('en', 'wikipedia', 'my_en_wikipedia_user', 'my_en_wikipedia_pass')
('my_username', BotPassword('my_suffix', 'my_password'))
+
+ .. versionchanged:: 10.2
+ raises ValueError instead of AttributeError if password_file
+ is not set
+
+ :raises ValueError: `password_file` is not set in the user-config.py
+ :raises FileNotFoundError: password file does not exist
"""
+ if config.password_file is None:
+ raise ValueError('password_file is not set in the user-config.py')
+
# Set path to password file relative to the user_config
# but fall back on absolute path for backwards compatibility
- assert config.base_dir is not None and config.password_file is not None
- password_file = os.path.join(config.base_dir, config.password_file)
- if not os.path.isfile(password_file):
- password_file = config.password_file
+ password_path = Path(config.base_dir, config.password_file)
+
+ params = {} if PYTHON_VERSION < (3, 13) else {'follow_symlinks': False}
+ # test for symlink required for Python < 3.13
+ if not password_path.is_file(**params) or password_path.is_symlink:
+ password_path = Path(config.password_file)
+
+ # ignore this check when running tests
+ if os.environ.get('PYWIKIBOT_TEST_RUNNING', '0') == '0':
+ if not password_path.is_file(**params) or password_path.is_symlink:
+ raise FileNotFoundError(
+ f'Password file {password_path.name} does not exist in '
+ f'{password_path.parent}'
+ )
# We fix password file permission first.
- file_mode_checker(password_file, mode=config.private_files_permission)
+ file_mode_checker(password_path, mode=config.private_files_permission)
- with codecs.open(password_file, encoding='utf-8') as f:
- lines = f.readlines()
+ lines = password_path.read_text('utf-8').splitlines()
+ line_len = len(lines)
- line_nr = len(lines) + 1
- for line in reversed(lines):
- line_nr -= 1
+ for n, line in enumerate(reversed(lines)):
if not line.strip() or line.startswith('#'):
continue
@@ -234,17 +257,19 @@
entry = None
if not isinstance(entry, tuple):
- warn(f'Invalid tuple in line {line_nr}',
+ warn(f'Invalid tuple in line {line_len - n}',
_PasswordFileWarning)
continue
- if not 2 <= len(entry) <= 4:
- warn(f'The length of tuple in line {line_nr} should be 2 to 4 '
- f'({entry} given)', _PasswordFileWarning)
+ if not 2 <= (entry_len := len(entry)) <= 4:
+ warn(f'The length of tuple in line {line_len - n} should be 2 '
+ f'to 4, {entry_len} given ({entry})',
+ _PasswordFileWarning)
continue
code, family, username, password = (
- self.site.code, self.site.family.name)[:4 - len(entry)] + entry
+ self.site.code, self.site.family.name)[:4 - entry_len] + entry
+
if (normalize_username(username) == self.username
and family == self.site.family.name
and code == self.site.code):
diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py
index 6eb0fe8..6e9bc35 100644
--- a/pywikibot/tools/__init__.py
+++ b/pywikibot/tools/__init__.py
@@ -659,7 +659,7 @@
def file_mode_checker(
- filename: str,
+ filename: str | bytes | os.PathLike,
mode: int = 0o600,
quiet: bool = False,
create: bool = False
diff --git a/tests/login_tests.py b/tests/login_tests.py
index 0e95d18..48d7fd1 100755
--- a/tests/login_tests.py
+++ b/tests/login_tests.py
@@ -4,7 +4,7 @@
e.g. used to test password-file based login.
"""
#
-# (C) Pywikibot team, 2012-2022
+# (C) Pywikibot team, 2012-2025
#
# Distributed under the terms of the MIT license.
#
@@ -12,10 +12,12 @@
from collections import defaultdict
from io import StringIO
+from pathlib import Path
from unittest import mock
from pywikibot.exceptions import NoUsernameError
from pywikibot.login import LoginManager
+from pywikibot.tools import PYTHON_VERSION
from tests.aspects import DefaultDrySiteTestCase, unittest
@@ -105,26 +107,29 @@
self.stat = self.patch('os.stat')
self.stat.return_value.st_mode = 0o100600
-
self.chmod = self.patch('os.chmod')
- self.open = self.patch('codecs.open')
+ if PYTHON_VERSION[:2] == (3, 10):
+ self.open = self.patch('pathlib.Path._accessor.open')
+ else:
+ self.open = self.patch('io.open')
+
self.open.return_value = StringIO()
def test_auto_chmod_OK(self):
"""Do not chmod files that have mode private_files_permission."""
self.stat.return_value.st_mode = 0o100600
LoginManager()
- self.stat.assert_called_with(self.config.password_file)
+ self.stat.assert_called_with(Path(self.config.password_file))
self.assertFalse(self.chmod.called)
def test_auto_chmod_not_OK(self):
"""Chmod files that do not have mode private_files_permission."""
self.stat.return_value.st_mode = 0o100644
LoginManager()
- self.stat.assert_called_with(self.config.password_file)
+ self.stat.assert_called_with(Path(self.config.password_file))
self.chmod.assert_called_once_with(
- self.config.password_file,
+ Path(self.config.password_file),
0o600
)
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1149831?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: I25af80fd37b55e84f33fc78ad86ceacfacd6476b
Gerrit-Change-Number: 1149831
Gerrit-PatchSet: 13
Gerrit-Owner: Xqt <[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]