Skip to content

Commit

Permalink
update all backported import refs, backport more stdlib names
Browse files Browse the repository at this point in the history
  • Loading branch information
jorenham committed Oct 1, 2024
1 parent 082f9ad commit 72f1869
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 140 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,10 @@ potential goals of `unpy`:
- [x] [PEP 696][PEP696]: Backport [PEP 695][PEP695] type signatures i.f.f. it
includes a type parameter with default
- [x] [PEP 696][PEP696]: `typing.NoDefault` => `typing_extensions.NoDefault`
- [ ] `asyncio.QueueShutDown` => `builtins.Exception`
- [ ] `pathlib.UnsupportedOperation` => `builtins.NotImplementedError`
- [ ] `queue.ShutDown` => `builtins.Exception`
- [ ] `re.PatternError` => `re.error`
- [x] `asyncio.QueueShutDown` => `builtins.Exception`
- [x] `pathlib.UnsupportedOperation` => `builtins.NotImplementedError`
- [x] `queue.ShutDown` => `builtins.Exception`
- [x] `re.PatternError` => `re.error`
- [x] `types.CapsuleType` => `typing_extensions.CapsuleType`
- [ ] `typing.{ClassVar,Final}` => `typing_extensions.{ClassVar,Final}` when nested
(python/cpython#89547)
Expand All @@ -295,7 +295,7 @@ potential goals of `unpy`:
- [x] [PEP 695][PEP695]: Backport generic classes and protocols
- [x] [PEP 695][PEP695]: `typing.TypeAliasType` => `typing_extensions.TypeAliasType`
- [x] [PEP 688][PEP688]: `collections.abc.Buffer` => `typing_extensions.Buffer`
- [ ] [PEP 688][PEP688]: `inspect.BufferFlags` => `int` (#57)
- [x] [PEP 688][PEP688]: `inspect.BufferFlags` => `int` (jorenham/unpy#57)
- Python 3.11 => 3.10
- [x] [PEP 681][PEP681]: `typing.dataclass_transform` =>
`typing_extensions.dataclass_transform`
Expand All @@ -306,7 +306,8 @@ potential goals of `unpy`:
- [x] [PEP 646][PEP646]: `*Ts` => `typing_extensions.Unpack[Ts]`
- [ ] Backport `typing.Any` base class (not recommended)
- [ ] Backport `asyncio.TaskGroup` base classes
- [ ] `enum.ReprEnum` => `enum.Enum` and `enum.StrEnum` => `str & enum.Enum`
- [x] `enum.ReprEnum` => `enum.Enum`
- [ ] `enum.StrEnum` => `str & enum.Enum`
- Generated `TypeVar`s
- [ ] Prefix extracted `TypeVar`s names with `_`
- [x] De-duplicate extracted typevar-likes with same name if equivalent
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ executionEnvironments = [

[tool.pytest.ini_options]
testpaths = ["unpy", "tests"]
addopts = ["-ra", "--strict-config", "--strict-markers", "--doctest-modules"]
addopts = ["-ra", "-vv", "--strict-config", "--strict-markers", "--doctest-modules"]
doctest_optionflags = ["NORMALIZE_WHITESPACE", "ELLIPSIS"]
filterwarnings = ["error"]
log_cli_level = "INFO"
Expand Down
69 changes: 62 additions & 7 deletions tests/test_transformers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: N802

import textwrap

import pytest
Expand Down Expand Up @@ -529,21 +531,74 @@ class HasArrayStruct(Protocol):
assert pyi_out == pyi_expect


def test_subclass_path():
def test_backport_exceptions():
pyi_in = _src("""
from pathlib import Path
import re
from asyncio import QueueShutDown
from pathlib import UnsupportedOperation
from queue import ShutDown
class MyPath(Path): ...
class AsyncShutdownError(QueueShutDown): ...
class ShutdownError(ShutDown): ...
class UnsupportedError(UnsupportedOperation): ...
class RegexError(re.PatternError): ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in)
pyi_expect = _src("""
import re
class AsyncShutdownError(Exception): ...
class ShutdownError(Exception): ...
class UnsupportedError(NotImplementedError): ...
class RegexError(re.error): ...
""")
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


def test_backport_enum_ReprEnum():
pyi_in = _src("""
from enum import ReprEnum
class StrEnum(str, ReprEnum): ...
""")
pyi_expect = _src("""
from enum import Enum
class StrEnum(str, Enum): ...
""")
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


def test_subclass_pathlib_path():
def test_backport_inspect_BufferFlags():
pyi_in = _src("""
from inspect import BufferFlags
from optype import CanBuffer
def buffer(obj: CanBuffer[BufferFlags], flags: BufferFlags, /) -> memoryview: ...
""")
pyi_expect = _src("""
from optype import CanBuffer
def buffer(obj: CanBuffer[int], flags: int, /) -> memoryview: ...
""")
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


def test_subclass_pathlib_Path():
pyi_import = _src("""
import pathlib
class MyPath(pathlib.Path): ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in)
transform_source(pyi_import)

pyi_import_from = _src("""
from pathlib import Path
class MyPath(Path): ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_import_from)
197 changes: 120 additions & 77 deletions unpy/_stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
from typing import Final

__all__ = (
"BASES_WITHOUT_BACKPORT",
"GLOBALS_DEFAULT",
"NAMES_BACKPORT_TPX",
"NAMES_DEPRECATED_ALIASES",
"NAMES_WITHOUT_BACKPORT",
"BACKPORTS",
"DEFAULT_GLOBALS",
"UNSUPPORTED_BASES",
"UNSUPPORTED_NAMES",
)


GLOBALS_DEFAULT: Final[dict[str, type | object]] = {
DEFAULT_GLOBALS: Final[dict[str, type | object]] = {
"__name__": str,
"__doc__": str | None,
"__package__": str | None,
Expand All @@ -24,65 +22,33 @@
"__cached__": str | None,
}

NAMES_DEPRECATED_ALIASES: Final = {
typing_module: {
# builtins
"Text": "builtins.str",
**{
alias: f"builtins.{alias.lower()}"
for alias in ["Dict", "List", "Set", "FrozenSet", "Tuple", "Type"]
},
# typing
"IntVar": f"{typing_module}.TypeVar",
"runtime": f"{typing_module}.runtime_checkable",
# collections
"DefaultDict": "collections.defaultdict",
"Deque": "collections.deque",
"ChainMap": "collections.ChainMap",
"Counter": "collections.Counter",
"OrderedDict": "collections.OrderedDict",
# collections.abc
"AbstractSet": "collections.abc.Set",
**{
name: f"collections.abc.{name}"
for name in [
"Collection",
"Container",
"ItemsView",
"KeysView",
"ValuesView",
"Mapping",
"MappingView",
"MutableMapping",
"MutableSequence",
"MutableSet",
"Sequence",
"Coroutine",
"AsyncGenerator",
"AsyncIterable",
"AsyncIterator",
"Awaitable",
"Iterable",
"Iterator",
"Callable",
"Generator",
"Hashable",
"Reversible",
"Sized",
]
},
# contextlib
"ContextManager": "contextlib.ContextManager",
"AsyncContextManager": "contextlib.AsyncContextManager",
# re
"Pattern": "re.Pattern",
"Match": "re.Match",
}
for typing_module in ["typing", "typing_extensions"]
UNSUPPORTED_NAMES: Final = {
"asyncio": {
"TaskGroup": (3, 11),
},
"builtins": {
"_IncompleteInputError": (3, 13),
"PythonFinalizationError": (3, 13),
"BaseExceptionGroup": (3, 11),
"ExceptionGroup": (3, 11),
"EncodingWarning": (3, 10),
},
"enum": {
# TODO(jorenham): Backport to `builtins.str & enum.Enum`
"StrEnum": (3, 11),
},
}
UNSUPPORTED_BASES: Final = {
"builtins": {"object": (4, 0)},
"inspect": {"BufferFlags", (3, 12)},
"pathlib": {"Path": (3, 12)},
"typing": {"Any": (3, 11)},
"typing_extensions": {"Any": (3, 11)},
}


# stdlib imports that have a backport in `typing_extensions`
NAMES_BACKPORT_TPX: Final = {
_BACKPORTS_TPX: Final = {
"collections.abc": {
"Buffer": (3, 12),
},
Expand Down Expand Up @@ -131,24 +97,101 @@
"deprecated": (3, 13),
},
}
_BACKPORTS_DEPRECATED: Final = {
typing_module: {
# builtins
"Text": ("builtins", "str"),
**{
alias: ("builtins", alias.lower())
for alias in ["Dict", "List", "Set", "FrozenSet", "Tuple", "Type"]
},
# typing
"IntVar": (typing_module, "TypeVar"),
"runtime": (typing_module, "runtime_checkable"),
# collections
"DefaultDict": ("collections", "defaultdict"),
"Deque": ("collections", "deque"),
"ChainMap": ("collections", "ChainMap"),
"Counter": ("collections", "Counter"),
"OrderedDict": ("collections", "OrderedDict"),
# collections.abc
"AbstractSet": ("collections.abc", "Set"),
**{
name: ("collections.abc", name)
for name in [
"Collection",
"Container",
"ItemsView",
"KeysView",
"ValuesView",
"Mapping",
"MappingView",
"MutableMapping",
"MutableSequence",
"MutableSet",
"Sequence",
"Coroutine",
"AsyncGenerator",
"AsyncIterable",
"AsyncIterator",
"Awaitable",
"Iterable",
"Iterator",
"Callable",
"Generator",
"Hashable",
"Reversible",
"Sized",
]
},
# contextlib
"ContextManager": ("contextlib", "ContextManager"),
"AsyncContextManager": ("contextlib", "AsyncContextManager"),
# re
"Pattern": ("re", "Pattern"),
"Match": ("re", "Match"),
}
for typing_module in ["typing", "typing_extensions"]
}

NAMES_WITHOUT_BACKPORT: Final = {
"builtins": {
"_IncompleteInputError": (3, 13),
"PythonFinalizationError": (3, 13),
"BaseExceptionGroup": (3, 11),
"ExceptionGroup": (3, 11),
"EncodingWarning": (3, 10),
BACKPORTS: Final = {
"asyncio": {
"QueueShutDown": ("builtins", "Exception", (3, 13)),
},
"collections.abc": {},
"enum": {
"ReprEnum": ("enum", "Enum", (3, 11)),
},
"inspect": {
"BufferFlags": ("builtins", "int", (3, 12)),
},
}
BASES_WITHOUT_BACKPORT: Final = {
"pathlib": {
"Path": (3, 12),
"UnsupportedOperation": ("builtins", "NotImplementedError", (3, 13)),
},
"typing": {
"Any": (3, 11),
"queue": {
"ShutDown": ("builtins", "Exception", (3, 13)),
},
"typing_extensions": {
"Any": (3, 11),
"re": {
"PatternError": ("re", "error", (3, 13)),
},
}


def __collect_backports() -> None:
for module, reqs in _BACKPORTS_TPX.items():
if module not in BACKPORTS:
BACKPORTS[module] = {}
BACKPORTS[module] |= {
name: ("typing_extensions", name, req) for name, req in reqs.items()
}

for module, aliases in _BACKPORTS_DEPRECATED.items():
if module not in BACKPORTS:
BACKPORTS[module] = {}
BACKPORTS[module] |= {
name: (module_new, name_new, (4, 0))
for name, (module_new, name_new) in aliases.items()
}


__collect_backports()
Loading

0 comments on commit 72f1869

Please sign in to comment.