jenkins-bot submitted this change.

View Change

Approvals: Mpaa: Looks good to me, but someone else must approve Xqt: Looks good to me, approved jenkins-bot: Verified
[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(-)

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):

To view, visit change 640103. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Iec722ce846a0d43d8d939c081ec36fa4e5619c81
Gerrit-Change-Number: 640103
Gerrit-PatchSet: 4
Gerrit-Owner: Xqt <info@gno.de>
Gerrit-Reviewer: D3r1ck01 <xsavitar.wiki@aol.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki@gmail.com>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged