Skip to content

Commit 4fff3d2

Browse files
committed
get write_pyi to support lowercase types with pipes
The specific form of `tuple[] | None` produces a `types.UnionType` in some way that seems to not be what it has ever been previously (an explicit Union will give you `<class 'typing._UnionGenericAlias'>`, apparently). so repr() this specific case so we can move to newer typing formats. As a test, this moves the type of partial_reordering to the newer format. Also, write output file using shutil.move from tempfile, so that crashes of write_pyi dont corrupt the file. Add version checks for black, python version bump minimum python version to 3.12 as 3.11 seems to have problems we dont need to fix Change-Id: I91914c1e1b979ad84ca8b82d362ed94312645994
1 parent feada28 commit 4fff3d2

File tree

5 files changed

+42
-14
lines changed

5 files changed

+42
-14
lines changed

alembic/op.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def batch_alter_table(
256256
table_name: str,
257257
schema: Optional[str] = None,
258258
recreate: Literal["auto", "always", "never"] = "auto",
259-
partial_reordering: Optional[list[tuple[str, ...]]] = None,
259+
partial_reordering: list[tuple[str, ...]] | None = None,
260260
copy_from: Optional[Table] = None,
261261
table_args: Tuple[Any, ...] = (),
262262
table_kwargs: Mapping[str, Any] = immutabledict({}),

alembic/operations/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def batch_alter_table(
248248
table_name: str,
249249
schema: Optional[str] = None,
250250
recreate: Literal["auto", "always", "never"] = "auto",
251-
partial_reordering: Optional[list[tuple[str, ...]]] = None,
251+
partial_reordering: list[tuple[str, ...]] | None = None,
252252
copy_from: Optional[Table] = None,
253253
table_args: Tuple[Any, ...] = (),
254254
table_kwargs: Mapping[str, Any] = util.immutabledict(),

alembic/script/write_hooks.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ def _run_hook(
135135

136136
@register("console_scripts")
137137
def console_scripts(
138-
path: str, options: dict, ignore_output: bool = False
138+
path: str,
139+
options: dict,
140+
ignore_output: bool = False,
141+
verify_version: tuple[int, ...] | None = None,
139142
) -> None:
140143
entrypoint_name = _get_required_option(options, "entrypoint")
141144
for entry in compat.importlib_metadata_get("console_scripts"):
@@ -147,11 +150,17 @@ def console_scripts(
147150
f"Could not find entrypoint console_scripts.{entrypoint_name}"
148151
)
149152

150-
command = [
151-
sys.executable,
152-
"-c",
153-
f"import {impl.module}; {impl.module}.{impl.attr}()",
154-
]
153+
if verify_version:
154+
pyscript = (
155+
f"import {impl.module}; "
156+
f"assert tuple(int(x) for x in {impl.module}.__version__.split('.')) >= {verify_version}, " # noqa: E501
157+
f"'need exactly version {verify_version} of {impl.name}'; "
158+
f"{impl.module}.{impl.attr}()"
159+
)
160+
else:
161+
pyscript = f"import {impl.module}; {impl.module}.{impl.attr}()"
162+
163+
command = [sys.executable, "-c", pyscript]
155164
_run_hook(path, options, ignore_output, command)
156165

157166

tests/requirements.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ def requirements():
376376
requirements, "black and zimports are required for this test"
377377
)
378378
version_low = exclusions.only_if(
379-
lambda _: compat.py311, "python 3.11 is required"
379+
lambda _: compat.py312, "python 3.12 is required"
380380
)
381381

382382
version_high = exclusions.only_if(

tools/write_pyi.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from dataclasses import field
77
from pathlib import Path
88
import re
9+
import shutil
910
import sys
1011
from tempfile import NamedTemporaryFile
1112
import textwrap
@@ -28,6 +29,10 @@
2829
from alembic.operations import ops
2930
import sqlalchemy as sa
3031

32+
BLACK_VERSION = (25, 9, 0)
33+
PYTHON_VERSIONS = (3, 12), (3, 14)
34+
35+
3136
TRIM_MODULE = [
3237
"alembic.autogenerate.api.",
3338
"alembic.operations.base.",
@@ -55,9 +60,13 @@
5560
def generate_pyi_for_proxy(
5661
file_info: FileInfo, destination_path: Path, ignore_output: bool
5762
):
58-
if sys.version_info < (3, 11):
63+
lower_python, upper_python = PYTHON_VERSIONS
64+
if sys.version_info < lower_python or sys.version_info >= upper_python:
5965
raise RuntimeError(
60-
"This script must be run with Python 3.11 or higher"
66+
f"Script supports at least python "
67+
f"{".".join(str(x) for x in lower_python)} "
68+
f"but less than {".".join(str(x) for x in upper_python)} "
69+
"right now."
6170
)
6271

6372
progname = Path(sys.argv[0]).as_posix()
@@ -132,6 +141,7 @@ def generate_pyi_for_proxy(
132141
str(destination_path),
133142
{"entrypoint": "black", "options": "-l79 --target-version py39"},
134143
ignore_output=ignore_output,
144+
verify_version=BLACK_VERSION,
135145
)
136146

137147

@@ -176,6 +186,8 @@ def _generate_stub_for_meth(
176186
def _formatannotation(annotation, base_module=None):
177187
if getattr(annotation, "__module__", None) == "typing":
178188
retval = repr(annotation).replace("typing.", "")
189+
elif getattr(annotation, "__module__", None) == "types":
190+
retval = repr(annotation).replace("types.", "")
179191
elif isinstance(annotation, type):
180192
retval = annotation.__qualname__
181193
elif isinstance(annotation, typing.TypeVar):
@@ -188,6 +200,8 @@ def _formatannotation(annotation, base_module=None):
188200
else:
189201
retval = annotation
190202

203+
assert isinstance(retval, str)
204+
191205
retval = re.sub(r"TypeEngine\b", "TypeEngine[Any]", retval)
192206

193207
retval = retval.replace("~", "") # typevar repr as "~T"
@@ -249,9 +263,14 @@ def {name}{argspec}: {"..." if not docs else ""}
249263

250264
def run_file(finfo: FileInfo, stdout: bool):
251265
if not stdout:
252-
generate_pyi_for_proxy(
253-
finfo, destination_path=finfo.path, ignore_output=False
254-
)
266+
with NamedTemporaryFile(delete=False, suffix=finfo.path.suffix) as f:
267+
f.close()
268+
f_path = Path(f.name)
269+
generate_pyi_for_proxy(
270+
finfo, destination_path=f_path, ignore_output=False
271+
)
272+
shutil.move(f_path, finfo.path)
273+
255274
else:
256275
with NamedTemporaryFile(delete=False, suffix=finfo.path.suffix) as f:
257276
f.close()

0 commit comments

Comments
 (0)