Skip to content

Commit 49ddf8c

Browse files
committed
Merge branch 'master' into config-extend
2 parents 91c5f74 + 50734e9 commit 49ddf8c

File tree

8 files changed

+174
-13
lines changed

8 files changed

+174
-13
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/doc/native_classes.rst

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ can be assigned to (similar to using ``__slots__``)::
4848
Inheritance
4949
-----------
5050

51-
Only single inheritance is supported (except for :ref:`traits
52-
<trait-types>`). Most non-native classes can't be used as base
53-
classes.
51+
Only single inheritance is supported from native classes (except for
52+
:ref:`traits <trait-types>`). Most non-native extension classes can't
53+
be used as base classes, but regular Python classes can be used as
54+
base classes unless they use unsupported metaclasses (see below for
55+
more about this).
5456

55-
These non-native classes can be used as base classes of native
57+
These non-native extension classes can be used as base classes of native
5658
classes:
5759

5860
* ``object``
@@ -63,8 +65,6 @@ classes:
6365
* ``IndexError``
6466
* ``LookupError``
6567
* ``UserWarning``
66-
* ``typing.NamedTuple``
67-
* ``enum.Enum``
6868

6969
By default, a non-native class can't inherit a native class, and you
7070
can't inherit from a native class outside the compilation unit that
@@ -89,6 +89,15 @@ You need to install ``mypy-extensions`` to use ``@mypyc_attr``:
8989
9090
pip install --upgrade mypy-extensions
9191
92+
Additionally, mypyc recognizes these base classes as special, and
93+
understands how they alter the behavior of classes (including native
94+
classes) that subclass them:
95+
96+
* ``typing.NamedTuple``
97+
* ``typing.Generic``
98+
* ``typing.Protocol``
99+
* ``enum.Enum``
100+
92101
Class variables
93102
---------------
94103

@@ -145,7 +154,8 @@ behavior is too dynamic. You can use these metaclasses, however:
145154
.. note::
146155

147156
If a class definition uses an unsupported metaclass, *mypyc
148-
compiles the class into a regular Python class*.
157+
compiles the class into a regular Python class* (non-native
158+
class).
149159

150160
Class decorators
151161
----------------
@@ -165,7 +175,63 @@ efficient as pure native classes.
165175
.. note::
166176

167177
If a class definition uses an unsupported class decorator, *mypyc
168-
compiles the class into a regular Python class*.
178+
compiles the class into a regular Python class* (non-native class).
179+
180+
Defining non-native classes
181+
---------------------------
182+
183+
You can use the ``@mypy_extensions.mypyc_attr(...)`` class decorator
184+
with an argument ``native_class=False`` to explicitly define normal
185+
Python classes (non-native classes)::
186+
187+
from mypy_extensions import mypyc_attr
188+
189+
@mypyc_attr(native_class=False)
190+
class NonNative:
191+
def __init__(self) -> None:
192+
self.attr = 1
193+
194+
setattr(NonNative, "extra", 1) # Ok
195+
196+
This only has an effect in classes compiled using mypyc. Non-native
197+
classes are significantly less efficient than native classes, but they
198+
are sometimes necessary to work around the limitations of native classes.
199+
200+
Non-native classes can use arbitrary metaclasses and class decorators,
201+
and they support flexible multiple inheritance. Mypyc will still
202+
generate a compile-time error if you try to assign to a method, or an
203+
attribute that is not defined in a class body, since these are static
204+
type errors detected by mypy::
205+
206+
o = NonNative()
207+
o.extra = "x" # Static type error: "extra" not defined
208+
209+
However, these operations still work at runtime, including in modules
210+
that are not compiled using mypyc. You can also use ``setattr`` and
211+
``getattr`` for dynamic access of arbitrary attributes. Expressions
212+
with an ``Any`` type are also not type checked statically, allowing
213+
access to arbitrary attributes::
214+
215+
a: Any = o
216+
a.extra = "x" # Ok
217+
218+
setattr(o, "extra", "y") # Also ok
219+
220+
Implicit non-native classes
221+
---------------------------
222+
223+
If a compiled class uses an unsupported metaclass or an unsupported
224+
class decorator, it will implicitly be a non-native class, as
225+
discussed above. You can still use ``@mypyc_attr(native_class=False)``
226+
to explicitly mark it as a non-native class.
227+
228+
Explicit native classes
229+
-----------------------
230+
231+
You can use ``@mypyc_attr(native_class=True)`` to explicitly declare a
232+
class as a native class. It will be a compile-time error if mypyc
233+
can't compile the class as a native class. You can use this to avoid
234+
accidentally defining implicit non-native classes.
169235

170236
Deleting attributes
171237
-------------------

test-data/unit/check-typeddict.test

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3760,20 +3760,24 @@ del x["optional_key"] # E: Key "optional_key" of TypedDict "TP" cannot be delet
37603760
[typing fixtures/typing-typeddict.pyi]
37613761

37623762
[case testTypedDictReadOnlyMutateMethods]
3763-
from typing import ReadOnly, TypedDict
3763+
from typing import ReadOnly, NotRequired, TypedDict
37643764

37653765
class TP(TypedDict):
37663766
key: ReadOnly[str]
3767+
optional_key: ReadOnly[NotRequired[str]]
37673768
other: ReadOnly[int]
37683769
mutable: bool
37693770

37703771
x: TP
37713772
reveal_type(x.pop("key")) # N: Revealed type is "builtins.str" \
37723773
# E: Key "key" of TypedDict "TP" cannot be deleted
3774+
reveal_type(x.pop("optional_key")) # N: Revealed type is "builtins.str" \
3775+
# E: Key "optional_key" of TypedDict "TP" cannot be deleted
37733776

37743777

37753778
x.update({"key": "abc", "other": 1, "mutable": True}) # E: ReadOnly TypedDict keys ("key", "other") TypedDict are mutated
37763779
x.setdefault("key", "abc") # E: ReadOnly TypedDict key "key" TypedDict is mutated
3780+
x.setdefault("optional_key", "foo") # E: ReadOnly TypedDict key "optional_key" TypedDict is mutated
37773781
x.setdefault("other", 1) # E: ReadOnly TypedDict key "other" TypedDict is mutated
37783782
x.setdefault("mutable", False) # ok
37793783
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)