Skip to content

Commit

Permalink
Use --no-implicit-optional by default (#13401)
Browse files Browse the repository at this point in the history
Fixes #9091

This brings us in line with the PEP 484 updates in 2018
  • Loading branch information
hauntsaninja authored Sep 27, 2022
1 parent ab3b98f commit 027c58a
Show file tree
Hide file tree
Showing 16 changed files with 44 additions and 44 deletions.
20 changes: 7 additions & 13 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -390,29 +390,23 @@ None and Optional handling
The following flags adjust how mypy handles values of type ``None``.
For more details, see :ref:`no_strict_optional`.

.. _no-implicit-optional:
.. _implicit-optional:

.. option:: --no-implicit-optional
.. option:: --implicit-optional

This flag causes mypy to stop treating arguments with a ``None``
This flag causes mypy to treat arguments with a ``None``
default value as having an implicit :py:data:`~typing.Optional` type.

For example, by default mypy will assume that the ``x`` parameter
is of type ``Optional[int]`` in the code snippet below since
the default parameter is ``None``:
For example, if this flag is set, mypy would assume that the ``x``
parameter is actually of type ``Optional[int]`` in the code snippet below
since the default parameter is ``None``:

.. code-block:: python
def foo(x: int = None) -> None:
print(x)
If this flag is set, the above snippet will no longer type check:
we must now explicitly indicate that the type is ``Optional[int]``:

.. code-block:: python
def foo(x: Optional[int] = None) -> None:
print(x)
**Note:** This was disabled by default starting in mypy 0.980.

.. option:: --no-strict-optional

Expand Down
8 changes: 5 additions & 3 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,15 @@ None and Optional handling
For more information, see the :ref:`None and Optional handling <none-and-optional-handling>`
section of the command line docs.

.. confval:: no_implicit_optional
.. confval:: implicit_optional

:type: boolean
:default: False

Changes the treatment of arguments with a default value of ``None`` by not implicitly
making their type :py:data:`~typing.Optional`.
Causes mypy to treat arguments with a ``None``
default value as having an implicit :py:data:`~typing.Optional` type.

**Note:** This was True by default in mypy versions 0.980 and earlier.

.. confval:: strict_optional

Expand Down
8 changes: 2 additions & 6 deletions docs/source/kinds_of_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,8 @@ case you should add an explicit ``Optional[...]`` annotation (or type comment).
.. note::

``Optional[...]`` *does not* mean a function argument with a default value.
However, if the default value of an argument is ``None``, you can use
an optional type for the argument, but it's not enforced by default.
You can use the :option:`--no-implicit-optional <mypy --no-implicit-optional>` command-line option to stop
treating arguments with a ``None`` default value as having an implicit
``Optional[...]`` type. It's possible that this will become the default
behavior in the future.
It simply means that ``None`` is a valid value for the argument. This is
a common confusion because ``None`` is a common default value for arguments.

.. _alternative_union_syntax:

Expand Down
2 changes: 1 addition & 1 deletion mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,7 @@ def do_func_def(
return retval

def set_type_optional(self, type: Type | None, initializer: Expression | None) -> None:
if self.options.no_implicit_optional:
if not self.options.implicit_optional:
return
# Indicate that type should be wrapped in an Optional if arg is initialized to None.
optional = isinstance(initializer, NameExpr) and initializer.name == "None"
Expand Down
5 changes: 2 additions & 3 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,10 +720,9 @@ def add_invertible_flag(
"https://mypy.readthedocs.io/en/stable/kinds_of_types.html#no-strict-optional",
)
add_invertible_flag(
"--no-implicit-optional",
"--implicit-optional",
default=False,
strict_flag=True,
help="Don't assume arguments with default values of None are Optional",
help="Assume arguments with default values of None are Optional",
group=none_group,
)
none_group.add_argument("--strict-optional", action="store_true", help=argparse.SUPPRESS)
Expand Down
8 changes: 4 additions & 4 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ class BuildType:
"disallow_untyped_defs",
"enable_error_code",
"enabled_error_codes",
"follow_imports",
"follow_imports_for_stubs",
"follow_imports",
"ignore_errors",
"ignore_missing_imports",
"implicit_optional",
"implicit_reexport",
"local_partial_types",
"mypyc",
"no_implicit_optional",
"strict_concatenate",
"strict_equality",
"strict_optional",
Expand Down Expand Up @@ -162,8 +162,8 @@ def __init__(self) -> None:
self.color_output = True
self.error_summary = True

# Don't assume arguments with default values of None are Optional
self.no_implicit_optional = False
# Assume arguments with default values of None are Optional
self.implicit_optional = False

# Don't re-export names unless they are imported with `from ... as ...`
self.implicit_reexport = True
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -2979,8 +2979,9 @@ class B: pass


[case testConstructInstanceWith__new__]
from typing import Optional
class C:
def __new__(cls, foo: int = None) -> 'C':
def __new__(cls, foo: Optional[int] = None) -> 'C':
obj = object.__new__(cls)
return obj

Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-fastparse.test
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class C:
[builtins fixtures/property.pyi]

[case testFastParsePerArgumentAnnotations]
# flags: --implicit-optional

class A: pass
class B: pass
Expand All @@ -130,6 +131,7 @@ def f(a, # type: A
[out]

[case testFastParsePerArgumentAnnotationsWithReturn]
# flags: --implicit-optional

class A: pass
class B: pass
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-flags.test
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@ standard.f(None)
[file mypy.ini]
\[mypy]
strict_optional = False
implicit_optional = true
\[mypy-optional]
strict_optional = True

Expand All @@ -862,6 +863,7 @@ standard.f(None)
[file pyproject.toml]
\[tool.mypy]
strict_optional = false
implicit_optional = true
\[[tool.mypy.overrides]]
module = 'optional'
strict_optional = true
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ if int():


[case testCallingFunctionsWithDefaultArgumentValues]
# flags: --implicit-optional --no-strict-optional

a, b = None, None # type: (A, B)
if int():
Expand Down
3 changes: 3 additions & 0 deletions test-data/unit/check-kwargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class A: pass
class B: pass

[case testOneOfSeveralOptionalKeywordArguments]
# flags: --implicit-optional
import typing
def f(a: 'A' = None, b: 'B' = None, c: 'C' = None) -> None: pass
f(a=A())
Expand Down Expand Up @@ -219,6 +220,7 @@ f(a, **b)
[builtins fixtures/dict.pyi]

[case testKeywordArgAfterVarArgs]
# flags: --implicit-optional
import typing
def f(*a: 'A', b: 'B' = None) -> None: pass
f()
Expand All @@ -235,6 +237,7 @@ class B: pass
[builtins fixtures/list.pyi]

[case testKeywordArgAfterVarArgsWithBothCallerAndCalleeVarArgs]
# flags: --implicit-optional --no-strict-optional
from typing import List
def f(*a: 'A', b: 'B' = None) -> None: pass
a = None # type: List[A]
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/check-optional.test
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def f(x: None) -> None: pass
f(None)

[case testInferOptionalFromDefaultNone]
# flags: --implicit-optional
def f(x: int = None) -> None:
x + 1 # E: Unsupported left operand type for + ("None") \
# N: Left operand is of type "Optional[int]"
Expand All @@ -140,6 +141,7 @@ def f(x: int = None) -> None: # E: Incompatible default for argument "x" (defau
[out]

[case testInferOptionalFromDefaultNoneComment]
# flags: --implicit-optional
def f(x=None):
# type: (int) -> None
x + 1 # E: Unsupported left operand type for + ("None") \
Expand Down
17 changes: 7 additions & 10 deletions test-data/unit/check-varargs.test
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class C: pass
[builtins fixtures/list.pyi]

[case testCallingVarArgsFunctionWithDefaultArgs]
# flags: --implicit-optional --no-strict-optional

a = None # type: A
b = None # type: B
Expand Down Expand Up @@ -388,12 +389,14 @@ class B(A): pass
[builtins fixtures/list.pyi]

[case testCallerVarArgsAndDefaultArgs]
# flags: --implicit-optional --no-strict-optional

a, b = None, None # type: (A, B)
f(*()) # Fail
f(a, *[a]) # Fail
f(a, b, *[a]) # Fail
f(*(a, a, b)) # Fail
f(*()) # E: Too few arguments for "f"
f(a, *[a]) # E: Argument 2 to "f" has incompatible type "*List[A]"; expected "Optional[B]" \
# E: Argument 2 to "f" has incompatible type "*List[A]"; expected "B"
f(a, b, *[a]) # E: Argument 3 to "f" has incompatible type "*List[A]"; expected "B"
f(*(a, a, b)) # E: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "Optional[B]"
f(*(a,))
f(*(a, b))
f(*(a, b, b, b))
Expand All @@ -407,12 +410,6 @@ def f(a: 'A', b: 'B' = None, *c: 'B') -> None:
class A: pass
class B: pass
[builtins fixtures/list.pyi]
[out]
main:3: error: Too few arguments for "f"
main:4: error: Argument 2 to "f" has incompatible type "*List[A]"; expected "Optional[B]"
main:4: error: Argument 2 to "f" has incompatible type "*List[A]"; expected "B"
main:5: error: Argument 3 to "f" has incompatible type "*List[A]"; expected "B"
main:6: error: Argument 1 to "f" has incompatible type "*Tuple[A, A, B]"; expected "Optional[B]"

[case testVarArgsAfterKeywordArgInCall1]
# see: mypy issue #2729
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/fine-grained.test
Original file line number Diff line number Diff line change
Expand Up @@ -7942,7 +7942,7 @@ class Foo(a.I):
==

[case testImplicitOptionalRefresh1]
# flags: --strict-optional
# flags: --strict-optional --implicit-optional
from x import f
def foo(x: int = None) -> None:
f()
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/fixtures/tuple.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Builtins stub used in tuple-related test cases.

from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Any, overload, Tuple, Type
from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Optional, overload, Tuple, Type

T = TypeVar("T")
Tco = TypeVar('Tco', covariant=True)
Expand Down Expand Up @@ -47,6 +47,6 @@ class list(Sequence[T], Generic[T]):

def isinstance(x: object, t: type) -> bool: pass

def sum(iterable: Iterable[T], start: T = None) -> T: pass
def sum(iterable: Iterable[T], start: Optional[T] = None) -> T: pass

class BaseException: pass
1 change: 1 addition & 0 deletions test-data/unit/fixtures/typing-namedtuple.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Any = 0
overload = 0
Type = 0
Literal = 0
Optional = 0

T_co = TypeVar('T_co', covariant=True)
KT = TypeVar('KT')
Expand Down

0 comments on commit 027c58a

Please sign in to comment.