Skip to content

Commit

Permalink
Document how evil --no-strict-optional is (#16731)
Browse files Browse the repository at this point in the history
On multiple occasions, I've encountered folks using this, running into
issues and then being perplexed when they figure out what
`--no-strict-optional` actually does. Most recently in
#16718 (comment)

This is a non-standard, dangerous option that should not be used in
modern typed Python. It's been five and a half years since it was the
default behaviour in mypy, so we should deemphasise and warn about its
existence.
  • Loading branch information
hauntsaninja authored Jan 2, 2024
1 parent f9e8e0b commit a1b4e32
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 78 deletions.
16 changes: 9 additions & 7 deletions docs/source/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ None and Optional handling
**************************

The following flags adjust how mypy handles values of type ``None``.
For more details, see :ref:`no_strict_optional`.

.. _implicit-optional:

Expand All @@ -435,16 +434,19 @@ For more details, see :ref:`no_strict_optional`.
**Note:** This was disabled by default starting in mypy 0.980.

.. _no_strict_optional:

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

This flag disables strict checking of :py:data:`~typing.Optional`
This flag effectively disables checking of :py:data:`~typing.Optional`
types and ``None`` values. With this option, mypy doesn't
generally check the use of ``None`` values -- they are valid
everywhere. See :ref:`no_strict_optional` for more about this feature.
generally check the use of ``None`` values -- it is treated
as compatible with every type.

.. warning::

**Note:** Strict optional checking was enabled by default starting in
mypy 0.600, and in previous versions it had to be explicitly enabled
using ``--strict-optional`` (which is still accepted).
``--no-strict-optional`` is evil. Avoid using it and definitely do
not use it without understanding what it does.


.. _configuring-warnings:
Expand Down
2 changes: 1 addition & 1 deletion docs/source/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ and mypy doesn't complain**.
return None # No error!
You may have disabled strict optional checking (see
:ref:`no_strict_optional` for more).
:ref:`--no-strict-optional <no_strict_optional>` for more).

.. _silencing_checker:

Expand Down
9 changes: 7 additions & 2 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,15 @@ section of the command line docs.
:type: boolean
:default: True

Enables or disables strict Optional checks. If False, mypy treats ``None``
Effectively disables checking of :py:data:`~typing.Optional`
types and ``None`` values. With this option, mypy doesn't
generally check the use of ``None`` values -- it is treated
as compatible with every type.

**Note:** This was False by default in mypy versions earlier than 0.600.
.. warning::

``strict_optional = false`` is evil. Avoid using it and definitely do
not use it without understanding what it does.


Configuring warnings
Expand Down
68 changes: 0 additions & 68 deletions docs/source/kinds_of_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -429,74 +429,6 @@ the runtime with some limitations (see :ref:`runtime_troubles`).
t2: int | None # equivalent to Optional[int]
.. _no_strict_optional:

Disabling strict optional checking
**********************************

Mypy also has an option to treat ``None`` as a valid value for every
type (in case you know Java, it's useful to think of it as similar to
the Java ``null``). In this mode ``None`` is also valid for primitive
types such as ``int`` and ``float``, and :py:data:`~typing.Optional` types are
not required.

The mode is enabled through the :option:`--no-strict-optional <mypy --no-strict-optional>` command-line
option. In mypy versions before 0.600 this was the default mode. You
can enable this option explicitly for backward compatibility with
earlier mypy versions, in case you don't want to introduce optional
types to your codebase yet.

It will cause mypy to silently accept some buggy code, such as
this example -- it's not recommended if you can avoid it:

.. code-block:: python
def inc(x: int) -> int:
return x + 1
x = inc(None) # No error reported by mypy if strict optional mode disabled!
However, making code "optional clean" can take some work! You can also use
:ref:`the mypy configuration file <config-file>` to migrate your code
to strict optional checking one file at a time, since there exists
the per-module flag
:confval:`strict_optional` to control strict optional mode.

Often it's still useful to document whether a variable can be
``None``. For example, this function accepts a ``None`` argument,
but it's not obvious from its signature:

.. code-block:: python
def greeting(name: str) -> str:
if name:
return f'Hello, {name}'
else:
return 'Hello, stranger'
print(greeting('Python')) # Okay!
print(greeting(None)) # Also okay!
You can still use :py:data:`Optional[t] <typing.Optional>` to document that ``None`` is a
valid argument type, even if strict ``None`` checking is not
enabled:

.. code-block:: python
from typing import Optional
def greeting(name: Optional[str]) -> str:
if name:
return f'Hello, {name}'
else:
return 'Hello, stranger'
Mypy treats this as semantically equivalent to the previous example
if strict optional checking is disabled, since ``None`` is implicitly
valid for any type, but it's much more
useful for a programmer who is reading the code. This also makes
it easier to migrate to strict ``None`` checking in the future.

.. _type-aliases:

Type aliases
Expand Down

0 comments on commit a1b4e32

Please sign in to comment.