jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/640103 )
Change subject: [IMPR] replace FrozenDict with frozenmap ......................................................................
[IMPR] replace FrozenDict with frozenmap
- Add a new frozenmap Mapping. See PEP 603 for naming. frozenmap does not support methods like pop(), popitem() or setdefault() like FrozenDict does which leads to changed content. - replace FrozenDict with frozenmap in family.py and archivebot.py - update family_tests.py - deprecate tools.FrozenDict
Change-Id: Iec722ce846a0d43d8d939c081ec36fa4e5619c81 --- M pywikibot/family.py M pywikibot/tools/__init__.py M scripts/archivebot.py M tests/family_tests.py 4 files changed, 58 insertions(+), 34 deletions(-)
Approvals: Mpaa: Looks good to me, but someone else must approve Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/family.py b/pywikibot/family.py index d509e77..c90d622 100644 --- a/pywikibot/family.py +++ b/pywikibot/family.py @@ -22,7 +22,7 @@ from pywikibot import config from pywikibot.exceptions import UnknownFamily, FamilyMaintenanceWarning from pywikibot.tools import ( - classproperty, deprecated, deprecated_args, FrozenDict, + classproperty, deprecated, deprecated_args, frozenmap, issue_deprecation_warning, ModuleDeprecationWrapper, PYTHON_VERSION, remove_last_args, ) @@ -1118,10 +1118,7 @@ """ data = {code: None for code in self.interwiki_removals} data.update(self.interwiki_replacements) - return FrozenDict(data, - 'Family.obsolete not updatable; ' - 'use Family.interwiki_removals ' - 'and Family.interwiki_replacements') + return frozenmap(data)
@obsolete.setter def obsolete(self, data): @@ -1357,8 +1354,7 @@ @classproperty def interwiki_replacements(cls): """Return an interwiki code replacement mapping.""" - rv = cls.code_aliases.copy() - return FrozenDict(rv) + return frozenmap(cls.code_aliases)
def shared_image_repository(self, code): """Return Wikimedia Commons as the shared image repository.""" diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py index 06514d7..49e4451 100644 --- a/pywikibot/tools/__init__.py +++ b/pywikibot/tools/__init__.py @@ -244,7 +244,7 @@ return repr(self.__dict__)
-class FrozenDict(dict): +class _FrozenDict(dict):
""" Frozen dict, preventing write after initialisation. @@ -274,6 +274,38 @@ __setitem__ = update
+class frozenmap(Mapping): # noqa: N801 + + """Frozen mapping, preventing write after initialisation.""" + + def __init__(self, data=(), **kwargs): + """Initialize data in same ways like a dict.""" + self.__data = {} + if isinstance(data, Mapping): + for key in data: + self.__data[key] = data[key] + elif hasattr(data, 'keys'): + for key in data.keys(): + self.__data[key] = data[key] + else: + for key, value in data: + self.__data[key] = value + for key, value in kwargs.items(): + self.__data[key] = value + + def __getitem__(self, key): + return self.__data[key] + + def __iter__(self): + return iter(self.__data) + + def __len__(self): + return len(self.__data) + + def __repr__(self): + return '{}({!r})'.format(self.__class__.__name__, self.__data) + + class LazyRegex:
""" @@ -1790,3 +1822,9 @@ option_msg += '\n' + ' ' * indent option_msg += option_line return '{} ({}):'.format(message, option_msg) + + +wrapper = ModuleDeprecationWrapper(__name__) +wrapper._add_deprecated_attr('FrozenDict', _FrozenDict, + replacement_name='tools.frozenmap', + since='20201109', future_warning=True) diff --git a/scripts/archivebot.py b/scripts/archivebot.py index 04cca3f..bb9783b 100755 --- a/scripts/archivebot.py +++ b/scripts/archivebot.py @@ -112,7 +112,7 @@ from pywikibot.textlib import (extract_sections, findmarker, TimeStripper, to_local_digits) from pywikibot.tools import ( - deprecated, FrozenDict, issue_deprecation_warning, PYTHON_VERSION, + deprecated, frozenmap, issue_deprecation_warning, PYTHON_VERSION, )
if PYTHON_VERSION >= (3, 9): @@ -127,14 +127,14 @@
ZERO = datetime.timedelta(0)
-MW_KEYS = FrozenDict({ +MW_KEYS = frozenmap({ 's': 'seconds', 'h': 'hours', 'd': 'days', 'w': 'weeks', 'y': 'years', # 'months' and 'minutes' were removed because confusion outweighs merit -}, 'MW_KEYS is a dict constant') +})
class ArchiveBotSiteConfigError(pywikibot.Error): diff --git a/tests/family_tests.py b/tests/family_tests.py index 8655a4f..626f9e2 100644 --- a/tests/family_tests.py +++ b/tests/family_tests.py @@ -5,6 +5,7 @@ # # Distributed under the terms of the MIT license. # +from collections.abc import Mapping from contextlib import suppress
import pywikibot.site @@ -22,11 +23,8 @@ """Test cases for Family methods."""
UNKNOWNFAMILY_RE = 'Family unknown does not exist' - FAMILY_TYPEERROR_RE = ( - 'Family.obsolete not updatable; ' - 'use Family.interwiki_removals and Family.interwiki_replacements') - FROZENSET_TYPEERROR_RE = ("'frozenset' object does not support item " - 'assignment') + FROZEN_TYPEERROR_RE = r"'frozen\w+' object does not support item " \ + 'assignment' net = False
def test_family_load_valid(self): @@ -110,7 +108,7 @@ def test_get_obsolete_wp(self): """Test three types of obsolete codes.""" family = Family.load('wikipedia') - self.assertIsInstance(family.obsolete, dict) + self.assertIsInstance(family.obsolete, Mapping) # redirected code (see site tests test_alias_code_site) self.assertEqual(family.obsolete['dk'], 'da') # closed/locked site (see site tests test_locked_site) @@ -136,27 +134,19 @@ def test_obsolete_readonly(self): """Test obsolete result not updatable.""" family = Family.load('wikipedia') - self.assertRaisesRegex( - TypeError, - self.FAMILY_TYPEERROR_RE, - family.obsolete.update, - {}) - self.assertRaisesRegex( - TypeError, - self.FAMILY_TYPEERROR_RE, - family.obsolete.__setitem__, - 'a', - 'b') + with self.assertRaisesRegex( + AttributeError, + "'frozenmap' object has no attribute 'update'"): + family.obsolete.update({}) + + with self.assertRaisesRegex(TypeError, self.FROZEN_TYPEERROR_RE): + family.obsolete['a'] = 'b'
def test_WikimediaFamily_obsolete_readonly(self): """Test WikimediaFamily obsolete is readonly.""" family = Family.load('wikipedia') - self.assertRaisesRegex( - TypeError, - self.FROZENSET_TYPEERROR_RE, - family.__setattr__, - 'obsolete', - {'a': 'b', 'c': None}) + with self.assertRaisesRegex(TypeError, self.FROZEN_TYPEERROR_RE): + family.obsolete = {'a': 'b', 'c': None}
class TestFamilyUrlRegex(PatchingTestCase):
pywikibot-commits@lists.wikimedia.org