"""This module contains backports to support older Python versions."""
#
# (C) Pywikibot team, 2014-2022
#
# Distributed under the terms of the MIT license.
#
import sys
from typing import Any
PYTHON_VERSION = sys.version_info[:3]
SPHINX_RUNNING = 'sphinx' in sys.modules
# functools.cache
if PYTHON_VERSION >= (3, 9):
from functools import cache # type: ignore[attr-defined]
else:
from functools import lru_cache as _lru_cache
cache = _lru_cache(None)
# context
if PYTHON_VERSION < (3, 7) or SPHINX_RUNNING:
[docs] class nullcontext: # noqa: N801
"""Context manager that does no additional processing.
.. seealso:: :python:`contextlib.nullcontext
<library/contextlib.html#contextlib.nullcontext>`,
backported from Python 3.7.
"""
def __init__(self, enter_result: Any = None) -> None: # noqa: D107
self.enter_result = enter_result
def __enter__(self) -> Any:
return self.enter_result
def __exit__(self, *excinfo: Any) -> None:
pass
else:
from contextlib import nullcontext # type: ignore[misc]
# queue
if PYTHON_VERSION < (3, 7):
from queue import Queue as SimpleQueue
else:
from queue import SimpleQueue # type: ignore[misc]
# typing
if PYTHON_VERSION < (3, 9):
from typing import DefaultDict # type: ignore[misc]
else:
from collections import ( # type: ignore[misc] # noqa: N812
defaultdict as DefaultDict,
)
if PYTHON_VERSION < (3, 7, 2):
from typing import Dict as OrderedDict
elif PYTHON_VERSION < (3, 9):
from typing import OrderedDict
else:
from collections import OrderedDict
if PYTHON_VERSION < (3, 9):
from typing import (
Container,
Dict,
FrozenSet,
Generator,
Iterable,
Iterator,
List,
Mapping,
Match,
Pattern,
Sequence,
Set,
Tuple,
Type,
)
else:
from collections.abc import (
Container,
Generator,
Iterable,
Iterator,
Mapping,
Sequence,
)
from re import Match, Pattern
Dict = dict # type: ignore[misc]
FrozenSet = frozenset # type: ignore[misc]
List = list # type: ignore[misc]
Set = set # type: ignore[misc]
Tuple = tuple # type: ignore[assignment]
Type = type
if PYTHON_VERSION < (3, 9, 2):
from typing import Callable
else:
from collections.abc import Callable
# PEP 616 string methods
if PYTHON_VERSION < (3, 9) or SPHINX_RUNNING:
[docs] def removeprefix(string: str, prefix: str) -> str: # skipcq: TYP-053
"""Remove prefix from a string or return a copy otherwise.
>>> removeprefix('TestHook', 'Test')
'Hook'
>>> removeprefix('BaseTestCase', 'Test')
'BaseTestCase'
.. seealso:: :python:`str.removeprefix
<library/stdtypes.html#str.removeprefix>`,
backported from Python 3.9.
.. versionadded:: 5.4
"""
if string.startswith(prefix):
return string[len(prefix):]
return string
[docs] def removesuffix(string: str, suffix: str) -> str: # skipcq: TYP-053
"""Remove suffix from a string or return a copy otherwise.
>>> removesuffix('MiscTests', 'Tests')
'Misc'
>>> removesuffix('TmpDirMixin', 'Tests')
'TmpDirMixin'
.. seealso:: :python:`str.removesuffix
<library/stdtypes.html#str.removesuffix>`,
backported from Python 3.9.
.. versionadded:: 5.4
"""
if string.endswith(suffix):
return string[:-len(suffix)]
return string
else:
removeprefix = str.removeprefix # type: ignore[attr-defined]
removesuffix = str.removesuffix # type: ignore[attr-defined]
# bpo-38200
if PYTHON_VERSION < (3, 10) or SPHINX_RUNNING:
from itertools import tee
[docs] def pairwise(iterable):
"""Return successive overlapping pairs taken from the input iterable.
.. seealso:: :python:`itertools.pairwise
<library/itertools.html#itertools.pairwise>`,
backported from Python 3.10.
.. versionadded:: 7.6
"""
a, b = tee(iterable)
next(b, None)
return zip(a, b)
else:
from itertools import pairwise