1Additional features 2------------------- 3 4This section discusses various features that did not fit in naturally in one 5of the previous sections. 6 7.. _dataclasses_support: 8 9Dataclasses 10*********** 11 12In Python 3.7, a new :py:mod:`dataclasses` module has been added to the standard library. 13This module allows defining and customizing simple boilerplate-free classes. 14They can be defined using the :py:func:`@dataclasses.dataclass 15<python:dataclasses.dataclass>` decorator: 16 17.. code-block:: python 18 19 from dataclasses import dataclass, field 20 21 @dataclass 22 class Application: 23 name: str 24 plugins: List[str] = field(default_factory=list) 25 26 test = Application("Testing...") # OK 27 bad = Application("Testing...", "with plugin") # Error: List[str] expected 28 29Mypy will detect special methods (such as :py:meth:`__lt__ <object.__lt__>`) depending on the flags used to 30define dataclasses. For example: 31 32.. code-block:: python 33 34 from dataclasses import dataclass 35 36 @dataclass(order=True) 37 class OrderedPoint: 38 x: int 39 y: int 40 41 @dataclass(order=False) 42 class UnorderedPoint: 43 x: int 44 y: int 45 46 OrderedPoint(1, 2) < OrderedPoint(3, 4) # OK 47 UnorderedPoint(1, 2) < UnorderedPoint(3, 4) # Error: Unsupported operand types 48 49Dataclasses can be generic and can be used in any other way a normal 50class can be used: 51 52.. code-block:: python 53 54 from dataclasses import dataclass 55 from typing import Generic, TypeVar 56 57 T = TypeVar('T') 58 59 @dataclass 60 class BoxedData(Generic[T]): 61 data: T 62 label: str 63 64 def unbox(bd: BoxedData[T]) -> T: 65 ... 66 67 val = unbox(BoxedData(42, "<important>")) # OK, inferred type is int 68 69For more information see :doc:`official docs <python:library/dataclasses>` 70and :pep:`557`. 71 72Caveats/Known Issues 73==================== 74 75Some functions in the :py:mod:`dataclasses` module, such as :py:func:`~dataclasses.replace` and :py:func:`~dataclasses.asdict`, 76have imprecise (too permissive) types. This will be fixed in future releases. 77 78Mypy does not yet recognize aliases of :py:func:`dataclasses.dataclass <dataclasses.dataclass>`, and will 79probably never recognize dynamically computed decorators. The following examples 80do **not** work: 81 82.. code-block:: python 83 84 from dataclasses import dataclass 85 86 dataclass_alias = dataclass 87 def dataclass_wrapper(cls): 88 return dataclass(cls) 89 90 @dataclass_alias 91 class AliasDecorated: 92 """ 93 Mypy doesn't recognize this as a dataclass because it is decorated by an 94 alias of `dataclass` rather than by `dataclass` itself. 95 """ 96 attribute: int 97 98 @dataclass_wrapper 99 class DynamicallyDecorated: 100 """ 101 Mypy doesn't recognize this as a dataclass because it is decorated by a 102 function returning `dataclass` rather than by `dataclass` itself. 103 """ 104 attribute: int 105 106 AliasDecorated(attribute=1) # error: Unexpected keyword argument 107 DynamicallyDecorated(attribute=1) # error: Unexpected keyword argument 108 109.. _attrs_package: 110 111The attrs package 112***************** 113 114:doc:`attrs <attrs:index>` is a package that lets you define 115classes without writing boilerplate code. Mypy can detect uses of the 116package and will generate the necessary method definitions for decorated 117classes using the type annotations it finds. 118Type annotations can be added as follows: 119 120.. code-block:: python 121 122 import attr 123 124 @attr.s 125 class A: 126 one: int = attr.ib() # Variable annotation (Python 3.6+) 127 two = attr.ib() # type: int # Type comment 128 three = attr.ib(type=int) # type= argument 129 130If you're using ``auto_attribs=True`` you must use variable annotations. 131 132.. code-block:: python 133 134 import attr 135 136 @attr.s(auto_attribs=True) 137 class A: 138 one: int 139 two: int = 7 140 three: int = attr.ib(8) 141 142Typeshed has a couple of "white lie" annotations to make type checking 143easier. :py:func:`attr.ib` and :py:class:`attr.Factory` actually return objects, but the 144annotation says these return the types that they expect to be assigned to. 145That enables this to work: 146 147.. code-block:: python 148 149 import attr 150 from typing import Dict 151 152 @attr.s(auto_attribs=True) 153 class A: 154 one: int = attr.ib(8) 155 two: Dict[str, str] = attr.Factory(dict) 156 bad: str = attr.ib(16) # Error: can't assign int to str 157 158Caveats/Known Issues 159==================== 160 161* The detection of attr classes and attributes works by function name only. 162 This means that if you have your own helper functions that, for example, 163 ``return attr.ib()`` mypy will not see them. 164 165* All boolean arguments that mypy cares about must be literal ``True`` or ``False``. 166 e.g the following will not work: 167 168 .. code-block:: python 169 170 import attr 171 YES = True 172 @attr.s(init=YES) 173 class A: 174 ... 175 176* Currently, ``converter`` only supports named functions. If mypy finds something else it 177 will complain about not understanding the argument and the type annotation in 178 :py:meth:`__init__ <object.__init__>` will be replaced by ``Any``. 179 180* :ref:`Validator decorators <attrs:examples_validators>` 181 and `default decorators <http://www.attrs.org/en/stable/examples.html#defaults>`_ 182 are not type-checked against the attribute they are setting/validating. 183 184* Method definitions added by mypy currently overwrite any existing method 185 definitions. 186 187.. _remote-cache: 188 189Using a remote cache to speed up mypy runs 190****************************************** 191 192Mypy performs type checking *incrementally*, reusing results from 193previous runs to speed up successive runs. If you are type checking a 194large codebase, mypy can still be sometimes slower than desirable. For 195example, if you create a new branch based on a much more recent commit 196than the target of the previous mypy run, mypy may have to 197process almost every file, as a large fraction of source files may 198have changed. This can also happen after you've rebased a local 199branch. 200 201Mypy supports using a *remote cache* to improve performance in cases 202such as the above. In a large codebase, remote caching can sometimes 203speed up mypy runs by a factor of 10, or more. 204 205Mypy doesn't include all components needed to set 206this up -- generally you will have to perform some simple integration 207with your Continuous Integration (CI) or build system to configure 208mypy to use a remote cache. This discussion assumes you have a CI 209system set up for the mypy build you want to speed up, and that you 210are using a central git repository. Generalizing to different 211environments should not be difficult. 212 213Here are the main components needed: 214 215* A shared repository for storing mypy cache files for all landed commits. 216 217* CI build that uploads mypy incremental cache files to the shared repository for 218 each commit for which the CI build runs. 219 220* A wrapper script around mypy that developers use to run mypy with remote 221 caching enabled. 222 223Below we discuss each of these components in some detail. 224 225Shared repository for cache files 226================================= 227 228You need a repository that allows you to upload mypy cache files from 229your CI build and make the cache files available for download based on 230a commit id. A simple approach would be to produce an archive of the 231``.mypy_cache`` directory (which contains the mypy cache data) as a 232downloadable *build artifact* from your CI build (depending on the 233capabilities of your CI system). Alternatively, you could upload the 234data to a web server or to S3, for example. 235 236Continuous Integration build 237============================ 238 239The CI build would run a regular mypy build and create an archive containing 240the ``.mypy_cache`` directory produced by the build. Finally, it will produce 241the cache as a build artifact or upload it to a repository where it is 242accessible by the mypy wrapper script. 243 244Your CI script might work like this: 245 246* Run mypy normally. This will generate cache data under the 247 ``.mypy_cache`` directory. 248 249* Create a tarball from the ``.mypy_cache`` directory. 250 251* Determine the current git master branch commit id (say, using 252 ``git rev-parse HEAD``). 253 254* Upload the tarball to the shared repository with a name derived from the 255 commit id. 256 257Mypy wrapper script 258=================== 259 260The wrapper script is used by developers to run mypy locally during 261development instead of invoking mypy directly. The wrapper first 262populates the local ``.mypy_cache`` directory from the shared 263repository and then runs a normal incremental build. 264 265The wrapper script needs some logic to determine the most recent 266central repository commit (by convention, the ``origin/master`` branch 267for git) the local development branch is based on. In a typical git 268setup you can do it like this: 269 270.. code:: 271 272 git merge-base HEAD origin/master 273 274The next step is to download the cache data (contents of the 275``.mypy_cache`` directory) from the shared repository based on the 276commit id of the merge base produced by the git command above. The 277script will decompress the data so that mypy will start with a fresh 278``.mypy_cache``. Finally, the script runs mypy normally. And that's all! 279 280Caching with mypy daemon 281======================== 282 283You can also use remote caching with the :ref:`mypy daemon <mypy_daemon>`. 284The remote cache will significantly speed up the first ``dmypy check`` 285run after starting or restarting the daemon. 286 287The mypy daemon requires extra fine-grained dependency data in 288the cache files which aren't included by default. To use caching with 289the mypy daemon, use the :option:`--cache-fine-grained <mypy --cache-fine-grained>` option in your CI 290build:: 291 292 $ mypy --cache-fine-grained <args...> 293 294This flag adds extra information for the daemon to the cache. In 295order to use this extra information, you will also need to use the 296``--use-fine-grained-cache`` option with ``dmypy start`` or 297``dmypy restart``. Example:: 298 299 $ dmypy start -- --use-fine-grained-cache <options...> 300 301Now your first ``dmypy check`` run should be much faster, as it can use 302cache information to avoid processing the whole program. 303 304Refinements 305=========== 306 307There are several optional refinements that may improve things further, 308at least if your codebase is hundreds of thousands of lines or more: 309 310* If the wrapper script determines that the merge base hasn't changed 311 from a previous run, there's no need to download the cache data and 312 it's better to instead reuse the existing local cache data. 313 314* If you use the mypy daemon, you may want to restart the daemon each time 315 after the merge base or local branch has changed to avoid processing a 316 potentially large number of changes in an incremental build, as this can 317 be much slower than downloading cache data and restarting the daemon. 318 319* If the current local branch is based on a very recent master commit, 320 the remote cache data may not yet be available for that commit, as 321 there will necessarily be some latency to build the cache files. It 322 may be a good idea to look for cache data for, say, the 5 latest 323 master commits and use the most recent data that is available. 324 325* If the remote cache is not accessible for some reason (say, from a public 326 network), the script can still fall back to a normal incremental build. 327 328* You can have multiple local cache directories for different local branches 329 using the :option:`--cache-dir <mypy --cache-dir>` option. If the user switches to an existing 330 branch where downloaded cache data is already available, you can continue 331 to use the existing cache data instead of redownloading the data. 332 333* You can set up your CI build to use a remote cache to speed up the 334 CI build. This would be particularly useful if each CI build starts 335 from a fresh state without access to cache files from previous 336 builds. It's still recommended to run a full, non-incremental 337 mypy build to create the cache data, as repeatedly updating cache 338 data incrementally could result in drift over a long time period (due 339 to a mypy caching issue, perhaps). 340 341.. _extended_callable: 342 343Extended Callable types 344*********************** 345 346.. note:: 347 348 This feature is deprecated. You can use 349 :ref:`callback protocols <callback_protocols>` as a replacement. 350 351As an experimental mypy extension, you can specify :py:data:`~typing.Callable` types 352that support keyword arguments, optional arguments, and more. When 353you specify the arguments of a :py:data:`~typing.Callable`, you can choose to supply just 354the type of a nameless positional argument, or an "argument specifier" 355representing a more complicated form of argument. This allows one to 356more closely emulate the full range of possibilities given by the 357``def`` statement in Python. 358 359As an example, here's a complicated function definition and the 360corresponding :py:data:`~typing.Callable`: 361 362.. code-block:: python 363 364 from typing import Callable 365 from mypy_extensions import (Arg, DefaultArg, NamedArg, 366 DefaultNamedArg, VarArg, KwArg) 367 368 def func(__a: int, # This convention is for nameless arguments 369 b: int, 370 c: int = 0, 371 *args: int, 372 d: int, 373 e: int = 0, 374 **kwargs: int) -> int: 375 ... 376 377 F = Callable[[int, # Or Arg(int) 378 Arg(int, 'b'), 379 DefaultArg(int, 'c'), 380 VarArg(int), 381 NamedArg(int, 'd'), 382 DefaultNamedArg(int, 'e'), 383 KwArg(int)], 384 int] 385 386 f: F = func 387 388Argument specifiers are special function calls that can specify the 389following aspects of an argument: 390 391- its type (the only thing that the basic format supports) 392 393- its name (if it has one) 394 395- whether it may be omitted 396 397- whether it may or must be passed using a keyword 398 399- whether it is a ``*args`` argument (representing the remaining 400 positional arguments) 401 402- whether it is a ``**kwargs`` argument (representing the remaining 403 keyword arguments) 404 405The following functions are available in ``mypy_extensions`` for this 406purpose: 407 408.. code-block:: python 409 410 def Arg(type=Any, name=None): 411 # A normal, mandatory, positional argument. 412 # If the name is specified it may be passed as a keyword. 413 414 def DefaultArg(type=Any, name=None): 415 # An optional positional argument (i.e. with a default value). 416 # If the name is specified it may be passed as a keyword. 417 418 def NamedArg(type=Any, name=None): 419 # A mandatory keyword-only argument. 420 421 def DefaultNamedArg(type=Any, name=None): 422 # An optional keyword-only argument (i.e. with a default value). 423 424 def VarArg(type=Any): 425 # A *args-style variadic positional argument. 426 # A single VarArg() specifier represents all remaining 427 # positional arguments. 428 429 def KwArg(type=Any): 430 # A **kwargs-style variadic keyword argument. 431 # A single KwArg() specifier represents all remaining 432 # keyword arguments. 433 434In all cases, the ``type`` argument defaults to ``Any``, and if the 435``name`` argument is omitted the argument has no name (the name is 436required for ``NamedArg`` and ``DefaultNamedArg``). A basic 437:py:data:`~typing.Callable` such as 438 439.. code-block:: python 440 441 MyFunc = Callable[[int, str, int], float] 442 443is equivalent to the following: 444 445.. code-block:: python 446 447 MyFunc = Callable[[Arg(int), Arg(str), Arg(int)], float] 448 449A :py:data:`~typing.Callable` with unspecified argument types, such as 450 451.. code-block:: python 452 453 MyOtherFunc = Callable[..., int] 454 455is (roughly) equivalent to 456 457.. code-block:: python 458 459 MyOtherFunc = Callable[[VarArg(), KwArg()], int] 460 461.. note:: 462 463 Each of the functions above currently just returns its ``type`` 464 argument at runtime, so the information contained in the argument 465 specifiers is not available at runtime. This limitation is 466 necessary for backwards compatibility with the existing 467 ``typing.py`` module as present in the Python 3.5+ standard library 468 and distributed via PyPI. 469