Skip to content

Commit

Permalink
Merge pull request #67 from jorenham/fix/58/backport-indirect-imports
Browse files Browse the repository at this point in the history
renaming of backported import references
  • Loading branch information
jorenham authored Sep 29, 2024
2 parents 91b53ca + d890b1c commit 92b643f
Show file tree
Hide file tree
Showing 9 changed files with 455 additions and 144 deletions.
25 changes: 9 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,25 +320,18 @@ potential goals of `unpy`:
- [x] De-duplicate extracted typevar-likes with same name if equivalent
- [ ] Rename extracted typevar-likes with same name if not equivalent
- [ ] Infer variance of `typing_extensions.TypeVar(..., infer_variance=True)` whose
name does not end with `_contra` (`contravariant=True`) or `_co`
(`covariant=True`)
- [x] Convert `default=Any` to `default={bound}` or `default=object`
- [x] Remove `bound=Any` and `bound=object`
- Imports
- [x] Reuse existing `from typing[_extensions] import {name}` imports instead of
adding new ones
- [x] Reuse `from {module} import {name} as {alias}` import aliases if present, e.g.
`from typing import TypeVar as TypeParam`
- [x] Reuse `import {module} as {alias}` if present, e.g. `import typing as tp`
- [x] Support for `from typing[_extensions] import *` (not recommended)
- [ ] Support for custom `typing` modules (like `[tool.ruff.lint.typing-modules]`)
name does not end with `{}_contra` (contravariant) or `{}_co` (covariant)

### Simplification and refactoring

- [ ] Transform `self` parameters to be positional-only
- [ ] Use `None` as the default return type
- [ ] De-duplicate and flatten unions and literals
- [ ] `type[S] | type[T]` => `type[S | T]`
- Type parameters
- [x] Convert `default=Any` with `bound=T` to `default=T`
- [x] Remove `bound=Any` and `bound=object`
- Annotations
- [ ] Transform `self` parameters to be positional-only
- [ ] Use `None` as the default return type
- [ ] De-duplicate and flatten unions and literals
- [ ] `type[S] | type[T]` => `type[S | T]`

### Beyond Python

Expand Down
5 changes: 5 additions & 0 deletions examples/.ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extend = "../pyproject.toml"
target-version = "py313"

[lint]
extend-ignore = ["E251", "PYI042", "N816"]
2 changes: 0 additions & 2 deletions examples/imports.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# generated

from types import CapsuleType
from typing import override
from warnings import deprecated
Expand Down
16 changes: 9 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ reportUnusedCallResult = false

executionEnvironments = [
{root = "examples", pythonVersion = "3.13"},
{root = "tests", reportPrivateUsage = false},
{root = "tests", pythonVersion = "3.12", reportPrivateUsage = false},
{root = "unpy", pythonVersion = "3.12"}
]

[tool.pytest.ini_options]
testpaths = ["unpy", "tests"]
addopts = ["-rav", "--strict-config", "--strict-markers", "--doctest-modules"]
addopts = ["-ra", "--strict-config", "--strict-markers", "--doctest-modules"]
doctest_optionflags = ["NORMALIZE_WHITESPACE", "ELLIPSIS"]
filterwarnings = ["error"]
log_cli_level = "INFO"
Expand Down Expand Up @@ -151,8 +151,7 @@ ignore = [
]

[tool.ruff.lint.per-file-ignores]
"examples/*" = ["E251", "PYI042", "N816"]
"tests/*" = ["ANN201", "PLC2701"]
"tests/*" = ["ANN201", "PLC2701", "SLF001"]

[tool.ruff.lint.mccabe]
max-complexity = 16
Expand All @@ -168,12 +167,15 @@ ctypes = "ct"
datetime = "dt"
libcst = "cst"

[tool.ruff.lint.pydocstyle]
ignore-decorators = ["typing.overload", "typing.override"]

[tool.ruff.lint.pylint]
allow-magic-value-types = ["str", "int"]
max-args = 10
max-bool-expr = 20
max-args = 8
max-bool-expr = 16
max-branches = 24
max-locals = 30
max-locals = 32
max-statements = 100

[tool.tox]
Expand Down
73 changes: 43 additions & 30 deletions tests/test_transformers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import textwrap

import pytest
from unpy._types import PythonVersion
from unpy.transformers import transform_source


Expand Down Expand Up @@ -42,7 +41,7 @@ def test_type_alias_simple():
from typing import TypeAlias
AnyStr: TypeAlias = str | bytes
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -57,7 +56,7 @@ def test_type_alias_simple_typing_import():
AnyBool: TypeAlias = Literal[False, 0, True, 1]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -69,7 +68,7 @@ def test_type_alias_param():
T = TypeVar("T")
Pair: TypeAlias = tuple[T, T]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -81,7 +80,7 @@ def test_type_alias_param_bound():
N = TypeVar("N", bound=int)
Shape2D: TypeAlias = tuple[N, N]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -99,7 +98,7 @@ def test_type_alias_param_constraints():
PathLike: TypeAlias = S | os.PathLike[S]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -112,7 +111,7 @@ def test_type_alias_param_default():
T = TypeVar("T", default=object)
OneOrMany: TypeAlias = T | tuple[T, ...]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -126,7 +125,7 @@ def test_type_alias_params_order_mismatch():
T0 = TypeVar("T0")
RPair = TypeAliasType("RPair", tuple[T0, T1], type_params=(T1, T0))
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -142,7 +141,7 @@ def test_type_alias_dupe_same():
Solo: TypeAlias = tuple[T]
Pair: TypeAlias = tuple[T, T]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -152,7 +151,7 @@ def test_type_alias_dupe_clash():
type SoloName[T: str] = tuple[T]
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in, target=PythonVersion.PY311)
transform_source(pyi_in)


def test_generic_function():
Expand All @@ -163,7 +162,7 @@ def test_generic_function():
T = TypeVar("T")
def spam(x: T) -> T: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -175,7 +174,7 @@ def test_generic_function_bound():
Z = TypeVar("Z", bound=complex)
def f(z: Z) -> Z: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -187,7 +186,7 @@ def test_generic_function_constraints():
Z = TypeVar("Z", int, float, complex)
def f(z: Z) -> Z: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -199,7 +198,7 @@ def test_generic_function_default():
Z = TypeVar("Z", bound=complex, default=complex)
def f(z: Z = ...) -> Z: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -217,7 +216,7 @@ def f[Z: complex = Any](z: Z = ...) -> Z: ...
def f(z: Z = ...) -> Z: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -233,7 +232,7 @@ def g[T](y: T, /) -> T: ...
def f(x: T, /) -> T: ...
def g(y: T, /) -> T: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -243,7 +242,7 @@ def f[T](x: T, /) -> T: ...
def g[T: str](v: T, /) -> T: ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in, target=PythonVersion.PY311)
transform_source(pyi_in)


def test_generic_function_dupe_clash_type():
Expand All @@ -252,7 +251,7 @@ def f[T](x: T, /) -> T: ...
def g[*T](*xs: *T) -> T: ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in, target=PythonVersion.PY311)
transform_source(pyi_in)


def test_generic_class():
Expand All @@ -266,7 +265,7 @@ def test_generic_class():
T_co = TypeVar("T_co", covariant=True)
class C(Generic[T_contra, T, T_co]): ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -287,7 +286,7 @@ class C[T_contra, T, T_co](Protocol): ...
class C(Protocol[T_contra, T, T_co]): ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -313,7 +312,7 @@ class B(A, Protocol):
@override
def f(self, /) -> int: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -328,7 +327,7 @@ def test_import_type_alias_type():
Alias = TypeAliasType("Alias", object)
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -343,7 +342,21 @@ def f(x: Buffer, /) -> bytes: ...
def f(x: Buffer, /) -> bytes: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


def test_import_collection_abs_buffer():
pyi_in = _src("""
import collections.abc
def f(x: collections.abc.Buffer, /) -> collections.abc.Sequence[int]: ...
""")
pyi_expect = _src("""
import collections.abc
from typing_extensions import Buffer
def f(x: Buffer, /) -> collections.abc.Sequence[int]: ...
""")
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -361,7 +374,7 @@ def is_str(x: object, /) -> TypeIs[str]: ...
def is_str(x: object, /) -> TypeIs[str]: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -379,7 +392,7 @@ class BoringDict(TypedDict):
class BoringDict(TypedDict):
key: ReadOnly[object]
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -396,7 +409,7 @@ def dont_use_me() -> None: ...
@deprecated("RTFM")
def dont_use_me() -> None: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -411,7 +424,7 @@ def getname(obj: object, default: NoDefault = ..., /) -> str: ...
def getname(obj: object, default: NoDefault = ..., /) -> str: ...
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -430,7 +443,7 @@ class HasArrayStruct(Protocol):
class HasArrayStruct(Protocol):
__array_struct__: CapsuleType
""")
pyi_out = transform_source(pyi_in, target=PythonVersion.PY311)
pyi_out = transform_source(pyi_in)
assert pyi_out == pyi_expect


Expand All @@ -441,7 +454,7 @@ def test_subclass_path():
class MyPath(Path): ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in, target=PythonVersion.PY311)
transform_source(pyi_in)


def test_subclass_pathlib_path():
Expand All @@ -451,4 +464,4 @@ def test_subclass_pathlib_path():
class MyPath(pathlib.Path): ...
""")
with pytest.raises(NotImplementedError):
transform_source(pyi_in, target=PythonVersion.PY311)
transform_source(pyi_in)
Loading

0 comments on commit 92b643f

Please sign in to comment.