Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ of the above sections.
This flag causes mypy to suppress errors caused by not being able to fully
infer the types of global and class variables.

.. option:: --allow-redefinition-new
.. option:: --allow-redefinition

By default, mypy won't allow a variable to be redefined with an
unrelated type. This flag enables the redefinition of *unannotated*
Expand All @@ -613,7 +613,7 @@ of the above sections.
# Type of "x" is "int | str" here.
return x
Without the new flag, mypy only supports inferring optional types
Without this flag, mypy only supports inferring optional types
(``X | None``) from multiple assignments. With this option enabled,
mypy can infer arbitrary union types.

Expand Down Expand Up @@ -646,24 +646,21 @@ of the above sections.
Note: We are planning to turn this flag on by default in a future mypy
release.

.. option:: --allow-redefinition
.. option:: --allow-redefinition-new

This is an alias to :option:`--allow-redefinition-old <mypy --allow-redefinition-old>`.
In mypy v2.0 this will point to
:option:`--allow-redefinition-new <mypy --allow-redefinition-new>`, and will
eventually became the default.
Deprecated alias for :option:`--allow-redefinition <mypy --allow-redefinition>`.

.. option:: --allow-redefinition-old

This is an older variant of
:option:`--allow-redefinition-new <mypy --allow-redefinition-new>`.
This is an older, more limited variant of
:option:`--allow-redefinition <mypy --allow-redefinition>`.
This flag enables redefinition of a variable with an
arbitrary type *in some contexts*: only redefinitions within the
same block and nesting depth as the original definition are allowed.

We have no plans to remove this flag, but we expect that
:option:`--allow-redefinition-new <mypy --allow-redefinition-new>`
will replace this flag for new use cases eventually.
We have no plans to remove this flag, but
:option:`--allow-redefinition <mypy --allow-redefinition>`
is recommended for new use cases.

Example where this can be useful:

Expand Down
17 changes: 8 additions & 9 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ section of the command line docs.
Causes mypy to suppress errors caused by not being able to fully
infer the types of global and class variables.

.. confval:: allow_redefinition_new
.. confval:: allow_redefinition

:type: boolean
:default: False
Expand Down Expand Up @@ -763,6 +763,13 @@ section of the command line docs.
Note: We are planning to turn this flag on by default in a future mypy
release.

.. confval:: allow_redefinition_new

:type: boolean
:default: False

Deprecated alias for :confval:`allow_redefinition`.

.. confval:: allow_redefinition_old

:type: boolean
Expand All @@ -789,14 +796,6 @@ section of the command line docs.
items = "100" # valid, items now has type str
items = int(items) # valid, items now has type int

.. confval:: allow_redefinition

:type: boolean
:default: False

An alias to :confval:`allow_redefinition_old`, in mypy v2.0 this will point to
:confval:`allow_redefinition_new`, and will eventually became the default.

.. confval:: local_partial_types

:type: boolean
Expand Down
6 changes: 3 additions & 3 deletions mypy/binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ def __init__(self, options: Options) -> None:

# If True, initial assignment to a simple variable (e.g. "x", but not "x.y")
# is added to the binder. This allows more precise narrowing and more
# flexible inference of variable types (--allow-redefinition-new).
self.bind_all = options.allow_redefinition_new
# flexible inference of variable types (--allow-redefinition).
self.bind_all = options.allow_redefinition

# This tracks any externally visible changes in binder to invalidate
# expression caches when needed.
Expand Down Expand Up @@ -339,7 +339,7 @@ def update_from_options(self, frames: list[Frame]) -> bool:
continue

# Remove exact duplicates to save pointless work later, this is
# a micro-optimization for --allow-redefinition-new.
# a micro-optimization for --allow-redefinition.
seen_types = set()
resulting_types = []
for rv in resulting_values:
Expand Down
2 changes: 1 addition & 1 deletion mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3207,7 +3207,7 @@ def semantic_analysis_pass1(self) -> None:
# TODO: Do this while constructing the AST?
self.tree.names = SymbolTable()
if not self.tree.is_stub:
if not self.options.allow_redefinition_new:
if not self.options.allow_redefinition:
# Perform some low-key variable renaming when assignments can't
# widen inferred types
self.tree.accept(LimitedVariableRenameVisitor())
Expand Down
28 changes: 14 additions & 14 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1490,7 +1490,7 @@ def check_func_def(
new_frame.types[key] = narrowed_type
self.binder.declarations[key] = old_binder.declarations[key]

if self.options.allow_redefinition_new and not self.is_stub:
if self.options.allow_redefinition and not self.is_stub:
# Add formal argument types to the binder.
for arg in defn.arguments:
# TODO: Add these directly using a fast path (possibly "put")
Expand Down Expand Up @@ -3437,7 +3437,7 @@ def check_assignment(
# unpleasant, and a generalization of this would
# be an improvement!
if (
not self.options.allow_redefinition_new
not self.options.allow_redefinition
and is_literal_none(rvalue)
and isinstance(lvalue, NameExpr)
and lvalue.kind == LDEF
Expand Down Expand Up @@ -3497,7 +3497,7 @@ def check_assignment(
):
lvalue.node.type = remove_instance_last_known_values(lvalue_type)
elif (
self.options.allow_redefinition_new
self.options.allow_redefinition
and lvalue_type is not None
and not isinstance(lvalue_type, PartialType)
# Note that `inferred is not None` is not a reliable check here, because
Expand Down Expand Up @@ -4480,7 +4480,7 @@ def check_lvalue(
# When revisiting the initial assignment (for example in a loop),
# treat is as regular if redefinitions are allowed.
skip_definition = (
self.options.allow_redefinition_new
self.options.allow_redefinition
and isinstance(lvalue, NameExpr)
and isinstance(lvalue.node, Var)
and lvalue.node.is_inferred
Expand Down Expand Up @@ -4511,7 +4511,7 @@ def check_lvalue(
elif isinstance(lvalue, NameExpr):
lvalue_type = self.expr_checker.analyze_ref_expr(lvalue, lvalue=True)
if (
self.options.allow_redefinition_new
self.options.allow_redefinition
and isinstance(lvalue.node, Var)
# We allow redefinition for function arguments inside function body.
# Although we normally do this for variables without annotation, users
Expand Down Expand Up @@ -4599,15 +4599,15 @@ def infer_variable_type(
init_type = strip_type(init_type)

self.set_inferred_type(name, lvalue, init_type)
if self.options.allow_redefinition_new:
if self.options.allow_redefinition:
self.binder.assign_type(lvalue, init_type, init_type)

def infer_partial_type(self, name: Var, lvalue: Lvalue, init_type: Type) -> bool:
init_type = get_proper_type(init_type)
if isinstance(init_type, NoneType) and (
isinstance(lvalue, MemberExpr) or not self.options.allow_redefinition_new
isinstance(lvalue, MemberExpr) or not self.options.allow_redefinition
):
# When using --allow-redefinition-new, None types aren't special
# When using --allow-redefinition, None types aren't special
# when inferring simple variable types.
partial_type = PartialType(None, name)
elif isinstance(init_type, Instance):
Expand Down Expand Up @@ -4855,7 +4855,7 @@ def check_simple_assignment(
get_proper_type(lvalue_type), AnyType
)

# If redefinitions are allowed (i.e. we have --allow-redefinition-new
# If redefinitions are allowed (i.e. we have --allow-redefinition
# and a variable without annotation) or if a variable has union type we
# try inferring r.h.s. twice with a fallback type context. The only exception
# is TypedDicts, they are often useless without context.
Expand Down Expand Up @@ -5024,8 +5024,8 @@ def replace_partial_type(
# Updating a partial type should invalidate expression caches.
self.binder.version += 1
del partial_types[var]
if self.options.allow_redefinition_new:
# When using --allow-redefinition-new, binder tracks all types of
if self.options.allow_redefinition:
# When using --allow-redefinition, binder tracks all types of
# simple variables.
n = NameExpr(var.name)
n.node = var
Expand Down Expand Up @@ -5442,10 +5442,10 @@ def visit_try_without_finally(self, s: TryStmt, try_frame: bool) -> None:
if isinstance(var.node, Var):
new_type = DeletedType(source=source)
var.node.type = new_type
if self.options.allow_redefinition_new:
if self.options.allow_redefinition:
# TODO: Should we use put() here?
self.binder.assign_type(var, new_type, new_type)
if not self.options.allow_redefinition_new:
if not self.options.allow_redefinition:
self.binder.cleanse(var)
if s.else_body:
self.accept(s.else_body)
Expand Down Expand Up @@ -9164,7 +9164,7 @@ def is_valid_inferred_type(
# type could either be NoneType or an Optional type, depending on
# the context. This resolution happens in leave_partial_types when
# we pop a partial types scope.
return is_lvalue_final or (not is_lvalue_member and options.allow_redefinition_new)
return is_lvalue_final or (not is_lvalue_member and options.allow_redefinition)
elif isinstance(proper_type, UninhabitedType):
return False
return not typ.accept(InvalidInferredTypes())
Expand Down
6 changes: 3 additions & 3 deletions mypy/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,9 +507,9 @@ def parse_section(
# Here we use `key` for original config section key, and `options_key` for
# the corresponding Options attribute.
options_key = key
# Match aliasing for command line flag.
if key.endswith("allow_redefinition"):
options_key += "_old"
# Match aliasing for deprecated config option name.
if options_key == "allow_redefinition_new":
options_key = "allow_redefinition"
if key in config_types:
ct = config_types[key]
elif key in invalid_options:
Expand Down
14 changes: 7 additions & 7 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,16 @@ def main(
if options.cache_dir == os.devnull:
fail("error: cache must be enabled in parallel mode", stderr, options)

if options.allow_redefinition_new and not options.local_partial_types:
if options.allow_redefinition and not options.local_partial_types:
fail(
"error: --local-partial-types must be enabled if using --allow-redefinition-new",
"error: --local-partial-types must be enabled if using --allow-redefinition",
stderr,
options,
)

if options.allow_redefinition_new and options.allow_redefinition_old:
if options.allow_redefinition and options.allow_redefinition_old:
fail(
"--allow-redefinition-old and --allow-redefinition-new should not be used together",
"--allow-redefinition-old and --allow-redefinition should not be used together",
stderr,
options,
)
Expand Down Expand Up @@ -888,9 +888,8 @@ def add_invertible_flag(
"--allow-redefinition",
default=False,
strict_flag=False,
help="Alias to --allow-redefinition-old; will point to --allow-redefinition-new in v2.0",
help="Allow flexible variable redefinition with a new type",
group=strictness_group,
dest="allow_redefinition_old",
)

add_invertible_flag(
Expand All @@ -905,8 +904,9 @@ def add_invertible_flag(
"--allow-redefinition-new",
default=False,
strict_flag=False,
help="Allow more flexible variable redefinition semantics",
help="Deprecated alias for --allow-redefinition",
group=strictness_group,
dest="allow_redefinition",
)

add_invertible_flag(
Expand Down
2 changes: 1 addition & 1 deletion mypy/nativeparse.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# mypy: allow-redefinition-new, local-partial-types
# mypy: allow-redefinition, local-partial-types
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are actually both enabled in self-check, so this is a no-op.

"""Python parser that directly constructs a native AST (when compiled).

Use a Rust extension to generate a serialized AST, and deserialize the AST directly
Expand Down
4 changes: 2 additions & 2 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class BuildType:
PER_MODULE_OPTIONS: Final = {
# Please keep this list sorted
"allow_redefinition_old",
"allow_redefinition_new",
"allow_redefinition",
"allow_untyped_globals",
"always_false",
"always_true",
Expand Down Expand Up @@ -235,7 +235,7 @@ def __init__(self) -> None:

# Allow flexible variable redefinition with an arbitrary type, in different
# blocks and at different nesting levels
self.allow_redefinition_new = False
self.allow_redefinition = False

# Prohibit equality, identity, and container checks for non-overlapping types.
# This makes 1 == '1', 1 in ['1'], and 1 is '1' errors.
Expand Down
14 changes: 6 additions & 8 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,23 +706,21 @@ def refresh_partial(

def refresh_top_level(self, file_node: MypyFile) -> None:
"""Reanalyze a stale module top-level in fine-grained incremental mode."""
if self.options.allow_redefinition_new and not self.options.local_partial_types:
if self.options.allow_redefinition and not self.options.local_partial_types:
n = TempNode(AnyType(TypeOfAny.special_form))
n.line = 1
n.column = 0
n.end_line = 1
n.end_column = 0
self.fail("--local-partial-types must be enabled if using --allow-redefinition-new", n)
if self.options.allow_redefinition_new and self.options.allow_redefinition_old:
self.fail("--local-partial-types must be enabled if using --allow-redefinition", n)
if self.options.allow_redefinition and self.options.allow_redefinition_old:
n = TempNode(AnyType(TypeOfAny.special_form))
n.line = 1
n.column = 0
n.end_line = 1
n.end_column = 0
self.fail(
"--allow-redefinition-old and --allow-redefinition-new"
" should not be used together",
n,
"--allow-redefinition-old and --allow-redefinition should not be used together", n
)
self.recurse_into_functions = False
self.add_implicit_module_attrs(file_node)
Expand Down Expand Up @@ -4493,9 +4491,9 @@ def analyze_name_lvalue(
else:
lvalue.fullname = lvalue.name
if self.is_func_scope():
if unmangle(name) == "_" and not self.options.allow_redefinition_new:
if unmangle(name) == "_" and not self.options.allow_redefinition:
# Special case for assignment to local named '_': always infer 'Any'.
# This isn't needed with --allow-redefinition-new, since arbitrary
# This isn't needed with --allow-redefinition, since arbitrary
# types can be assigned to '_' anyway.
typ = AnyType(TypeOfAny.special_form)
self.store_declared_types(lvalue, typ)
Expand Down
6 changes: 3 additions & 3 deletions test-data/unit/check-final.test
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ class C:
-- Reassignments

[case testFinalReassignModuleVar]
# flags: --allow-redefinition
# flags: --allow-redefinition-old
from typing import Final

x: Final = 1
Expand All @@ -412,7 +412,7 @@ z: Final = 2 # E: Cannot redefine an existing name as final
z = 3 # E: Cannot assign to final name "z"

[case testFinalReassignModuleVar2]
# flags: --allow-redefinition
# flags: --allow-redefinition-old
from typing import Final

x: Final = 1
Expand All @@ -429,7 +429,7 @@ y
y: Final = 3 # E: Cannot redefine an existing name as final

[case testFinalReassignModuleVar3]
# flags: --disallow-redefinition
# flags: --disallow-redefinition-old
from typing import Final

x: Final = 1
Expand Down
Loading
Loading