Skip to content

Commit e21aa69

Browse files
Merge branch 'master' into master
2 parents d5cefb8 + dfd2f28 commit e21aa69

File tree

10 files changed

+250
-46
lines changed

10 files changed

+250
-46
lines changed

docs/source/command_line.rst

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,58 @@ of the above sections.
593593
This flag causes mypy to suppress errors caused by not being able to fully
594594
infer the types of global and class variables.
595595

596-
.. option:: --allow-redefinition
596+
.. option:: --allow-redefinition-new
597597

598598
By default, mypy won't allow a variable to be redefined with an
599-
unrelated type. This flag enables redefinition of a variable with an
599+
unrelated type. This *experimental* flag enables the redefinition of
600+
unannotated variables with an arbitrary type. You will also need to enable
601+
:option:`--local-partial-types <mypy --local-partial-types>`.
602+
Example:
603+
604+
.. code-block:: python
605+
606+
def maybe_convert(n: int, b: bool) -> int | str:
607+
if b:
608+
x = str(n) # Assign "str"
609+
else:
610+
x = n # Assign "int"
611+
# Type of "x" is "int | str" here.
612+
return x
613+
614+
Without the new flag, mypy only supports inferring optional types
615+
(``X | None``) from multiple assignments. With this option enabled,
616+
mypy can infer arbitrary union types.
617+
618+
This also enables an unannotated variable to have different types in different
619+
code locations:
620+
621+
.. code-block:: python
622+
623+
if check():
624+
for x in range(n):
625+
# Type of "x" is "int" here.
626+
...
627+
else:
628+
for x in ['a', 'b']:
629+
# Type of "x" is "str" here.
630+
...
631+
632+
Note: We are planning to turn this flag on by default in a future mypy
633+
release, along with :option:`--local-partial-types <mypy --local-partial-types>`.
634+
The feature is still experimental, and the semantics may still change.
635+
636+
.. option:: --allow-redefinition
637+
638+
This is an older variant of
639+
:option:`--allow-redefinition-new <mypy --allow-redefinition-new>`.
640+
This flag enables redefinition of a variable with an
600641
arbitrary type *in some contexts*: only redefinitions within the
601642
same block and nesting depth as the original definition are allowed.
643+
644+
We have no plans to remove this flag, but we expect that
645+
:option:`--allow-redefinition-new <mypy --allow-redefinition-new>`
646+
will replace this flag for new use cases eventually.
647+
602648
Example where this can be useful:
603649

604650
.. code-block:: python

docs/source/config_file.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,44 @@ section of the command line docs.
713713
Causes mypy to suppress errors caused by not being able to fully
714714
infer the types of global and class variables.
715715

716+
.. confval:: allow_redefinition_new
717+
718+
:type: boolean
719+
:default: False
720+
721+
By default, mypy won't allow a variable to be redefined with an
722+
unrelated type. This *experimental* flag enables the redefinition of
723+
unannotated variables with an arbitrary type. You will also need to enable
724+
:confval:`local_partial_types`.
725+
Example:
726+
727+
.. code-block:: python
728+
729+
def maybe_convert(n: int, b: bool) -> int | str:
730+
if b:
731+
x = str(n) # Assign "str"
732+
else:
733+
x = n # Assign "int"
734+
# Type of "x" is "int | str" here.
735+
return x
736+
737+
This also enables an unannotated variable to have different types in different
738+
code locations:
739+
740+
.. code-block:: python
741+
742+
if check():
743+
for x in range(n):
744+
# Type of "x" is "int" here.
745+
...
746+
else:
747+
for x in ['a', 'b']:
748+
# Type of "x" is "str" here.
749+
...
750+
751+
Note: We are planning to turn this flag on by default in a future mypy
752+
release, along with :confval:`local_partial_types`.
753+
716754
.. confval:: allow_redefinition
717755

718756
:type: boolean
@@ -746,6 +784,7 @@ section of the command line docs.
746784

747785
Disallows inferring variable type for ``None`` from two assignments in different scopes.
748786
This is always implicitly enabled when using the :ref:`mypy daemon <mypy_daemon>`.
787+
This will be enabled by default in a future mypy release.
749788

750789
.. confval:: disable_error_code
751790

mypy/join.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@ def default(self, typ: Type) -> ProperType:
635635
typ = get_proper_type(typ)
636636
if isinstance(typ, Instance):
637637
return object_from_instance(typ)
638+
elif isinstance(typ, TypeType):
639+
return self.default(typ.item)
638640
elif isinstance(typ, UnboundType):
639641
return AnyType(TypeOfAny.special_form)
640642
elif isinstance(typ, TupleType):

mypy/plugins/default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type:
316316

317317
value_types = []
318318
for key in keys:
319-
if key in ctx.type.required_keys:
319+
if key in ctx.type.required_keys or key in ctx.type.readonly_keys:
320320
ctx.api.msg.typeddict_key_cannot_be_deleted(ctx.type, key, key_expr)
321321

322322
value_type = ctx.type.items.get(key)

mypy/test/testtypes.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,10 @@ def test_variadic_tuple_joins(self) -> None:
10641064
self.tuple(UnpackType(Instance(self.fx.std_tuplei, [self.fx.a])), self.fx.a),
10651065
)
10661066

1067+
def test_join_type_type_type_var(self) -> None:
1068+
self.assert_join(self.fx.type_a, self.fx.t, self.fx.o)
1069+
self.assert_join(self.fx.t, self.fx.type_a, self.fx.o)
1070+
10671071
# There are additional test cases in check-inference.test.
10681072

10691073
# TODO: Function types + varargs and default args.

mypy/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# - Release versions have the form "1.2.3".
99
# - Dev versions have the form "1.2.3+dev" (PLUS sign to conform to PEP 440).
1010
# - Before 1.0 we had the form "0.NNN".
11-
__version__ = "1.16.0+dev"
11+
__version__ = "1.17.0+dev"
1212
base_version = __version__
1313

1414
mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

mypyc/codegen/emitmodule.py

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -867,8 +867,16 @@ def generate_globals_init(self, emitter: Emitter) -> None:
867867

868868
def generate_module_def(self, emitter: Emitter, module_name: str, module: ModuleIR) -> None:
869869
"""Emit the PyModuleDef struct for a module and the module init function."""
870-
# Emit module methods
871870
module_prefix = emitter.names.private_name(module_name)
871+
self.emit_module_exec_func(emitter, module_name, module_prefix, module)
872+
self.emit_module_methods(emitter, module_name, module_prefix, module)
873+
self.emit_module_def_struct(emitter, module_name, module_prefix)
874+
self.emit_module_init_func(emitter, module_name, module_prefix)
875+
876+
def emit_module_methods(
877+
self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
878+
) -> None:
879+
"""Emit module methods (the static PyMethodDef table)."""
872880
emitter.emit_line(f"static PyMethodDef {module_prefix}module_methods[] = {{")
873881
for fn in module.functions:
874882
if fn.class_name is not None or fn.name == TOP_LEVEL_NAME:
@@ -888,7 +896,10 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
888896
emitter.emit_line("};")
889897
emitter.emit_line()
890898

891-
# Emit module definition struct
899+
def emit_module_def_struct(
900+
self, emitter: Emitter, module_name: str, module_prefix: str
901+
) -> None:
902+
"""Emit the static module definition struct (PyModuleDef)."""
892903
emitter.emit_lines(
893904
f"static struct PyModuleDef {module_prefix}module = {{",
894905
"PyModuleDef_HEAD_INIT,",
@@ -900,36 +911,22 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
900911
"};",
901912
)
902913
emitter.emit_line()
903-
# Emit module init function. If we are compiling just one module, this
904-
# will be the C API init function. If we are compiling 2+ modules, we
905-
# generate a shared library for the modules and shims that call into
906-
# the shared library, and in this case we use an internal module
907-
# initialized function that will be called by the shim.
908-
if not self.use_shared_lib:
909-
declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)"
910-
else:
911-
declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)"
912-
emitter.emit_lines(declaration, "{")
913-
emitter.emit_line("PyObject* modname = NULL;")
914-
# Store the module reference in a static and return it when necessary.
915-
# This is separate from the *global* reference to the module that will
916-
# be populated when it is imported by a compiled module. We want that
917-
# reference to only be populated when the module has been successfully
918-
# imported, whereas this we want to have to stop a circular import.
919-
module_static = self.module_internal_static_name(module_name, emitter)
920914

921-
emitter.emit_lines(
922-
f"if ({module_static}) {{",
923-
f"Py_INCREF({module_static});",
924-
f"return {module_static};",
925-
"}",
926-
)
915+
def emit_module_exec_func(
916+
self, emitter: Emitter, module_name: str, module_prefix: str, module: ModuleIR
917+
) -> None:
918+
"""Emit the module init function.
927919
928-
emitter.emit_lines(
929-
f"{module_static} = PyModule_Create(&{module_prefix}module);",
930-
f"if (unlikely({module_static} == NULL))",
931-
" goto fail;",
932-
)
920+
If we are compiling just one module, this will be the C API init
921+
function. If we are compiling 2+ modules, we generate a shared
922+
library for the modules and shims that call into the shared
923+
library, and in this case we use an internal module initialized
924+
function that will be called by the shim.
925+
"""
926+
declaration = f"static int {module_prefix}_exec(PyObject *module)"
927+
module_static = self.module_internal_static_name(module_name, emitter)
928+
emitter.emit_lines(declaration, "{")
929+
emitter.emit_line("PyObject* modname = NULL;")
933930
emitter.emit_line(
934931
f'modname = PyObject_GetAttrString((PyObject *){module_static}, "__name__");'
935932
)
@@ -959,8 +956,9 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
959956

960957
emitter.emit_lines("Py_DECREF(modname);")
961958

962-
emitter.emit_line(f"return {module_static};")
963-
emitter.emit_lines("fail:", f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
959+
emitter.emit_line("return 0;")
960+
emitter.emit_lines("fail:")
961+
emitter.emit_lines(f"Py_CLEAR({module_static});", "Py_CLEAR(modname);")
964962
for name, typ in module.final_names:
965963
static_name = emitter.static_name(name, module_name)
966964
emitter.emit_dec_ref(static_name, typ, is_xdec=True)
@@ -970,9 +968,44 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module
970968
# so we have to decref them
971969
for t in type_structs:
972970
emitter.emit_line(f"Py_CLEAR({t});")
973-
emitter.emit_line("return NULL;")
971+
emitter.emit_line("return -1;")
974972
emitter.emit_line("}")
975973

974+
def emit_module_init_func(
975+
self, emitter: Emitter, module_name: str, module_prefix: str
976+
) -> None:
977+
if not self.use_shared_lib:
978+
declaration = f"PyMODINIT_FUNC PyInit_{module_name}(void)"
979+
else:
980+
declaration = f"PyObject *CPyInit_{exported_name(module_name)}(void)"
981+
emitter.emit_lines(declaration, "{")
982+
983+
exec_func = f"{module_prefix}_exec"
984+
985+
# Store the module reference in a static and return it when necessary.
986+
# This is separate from the *global* reference to the module that will
987+
# be populated when it is imported by a compiled module. We want that
988+
# reference to only be populated when the module has been successfully
989+
# imported, whereas this we want to have to stop a circular import.
990+
module_static = self.module_internal_static_name(module_name, emitter)
991+
992+
emitter.emit_lines(
993+
f"if ({module_static}) {{",
994+
f"Py_INCREF({module_static});",
995+
f"return {module_static};",
996+
"}",
997+
)
998+
999+
emitter.emit_lines(
1000+
f"{module_static} = PyModule_Create(&{module_prefix}module);",
1001+
f"if (unlikely({module_static} == NULL))",
1002+
" goto fail;",
1003+
)
1004+
emitter.emit_lines(f"if ({exec_func}({module_static}) != 0)", " goto fail;")
1005+
emitter.emit_line(f"return {module_static};")
1006+
emitter.emit_lines("fail:", "return NULL;")
1007+
emitter.emit_lines("}")
1008+
9761009
def generate_top_level_call(self, module: ModuleIR, emitter: Emitter) -> None:
9771010
"""Generate call to function representing module top level."""
9781011
# Optimization: we tend to put the top level last, so reverse iterate

0 commit comments

Comments
 (0)