{"action":"opened","issue":{"url":"https://api.github.com/repos/python/mypy/issues/5687","repository_url":"https://api.github.com/repos/python/mypy","labels_url":"https://api.github.com/repos/python/mypy/issues/5687/labels{/name}","comments_url":"https://api.github.com/repos/python/mypy/issues/5687/comments","events_url":"https://api.github.com/repos/python/mypy/issues/5687/events","html_url":"https://github.com/python/mypy/issues/5687","id":364644688,"node_id":"MDU6SXNzdWUzNjQ2NDQ2ODg=","number":5687,"title":"Add static_assert to verify type constraints in Mypy","user":{"login":"TV4Fun","id":4702728,"node_id":"MDQ6VXNlcjQ3MDI3Mjg=","avatar_url":"https://avatars1.githubusercontent.com/u/4702728?v=4","gravatar_id":"","url":"https://api.github.com/users/TV4Fun","html_url":"https://github.com/TV4Fun","followers_url":"https://api.github.com/users/TV4Fun/followers","following_url":"https://api.github.com/users/TV4Fun/following{/other_user}","gists_url":"https://api.github.com/users/TV4Fun/gists{/gist_id}","starred_url":"https://api.github.com/users/TV4Fun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/TV4Fun/subscriptions","organizations_url":"https://api.github.com/users/TV4Fun/orgs","repos_url":"https://api.github.com/users/TV4Fun/repos","events_url":"https://api.github.com/users/TV4Fun/events{/privacy}","received_events_url":"https://api.github.com/users/TV4Fun/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-09-27T20:40:04Z","updated_at":"2018-09-27T20:40:04Z","closed_at":null,"author_association":"NONE","body":"Mypy's type checking works well in many cases, but there are some complex cases that are difficult to code for, and would be impossible or costly to check at runtime. I give an example of a case where this would be useful in #5666. While I give a slightly cumbersome possible solution there, another possibility is to use explicit casting:\r\n```python\r\nfrom abc import ABC, abstractmethod\r\nfrom typing import Generic, TypeVar, cast\r\n\r\n_InputType = TypeVar('_InputType', contravariant=True)\r\n_IntermediateType = TypeVar('_IntermediateType')\r\n_OutputType = TypeVar('_OutputType', covariant=True)\r\n\r\n\r\nclass GenericBase(Generic[_InputType, _IntermediateType, _OutputType], ABC):\r\n    @abstractmethod\r\n    def first_step(self, pipeline_input: _InputType) -> _IntermediateType: ...\r\n\r\n    def second_step(self, state: _IntermediateType) -> _OutputType:\r\n        # By default, pass through state unmodified\r\n        return cast(_OutputType, state)\r\n\r\n    def execute(self, pipeline_input: _InputType) -> _OutputType:\r\n        state = self.first_step(pipeline_input)\r\n        return self.second_step(state)\r\n```\r\nThis works, but it eliminates type safety, as I have to trust that the implementing class chooses `_IntermediateType` and `_OutputType` in a way that that cast makes sense. If these were not `TypeVar`s, I could do a runtime check like `assert issubclass(_IntermediateType, _OutputType)` to verify that this cast is safe, but obviously Python does not have enough information at runtime to do an `issubclass` check with `TypeVar`s or parameterized `Generics`.\r\n\r\nI propose adding a `static_assert` method, which would be ignored at runtime, but would be capable of evaluating statements about `Type`s in static analysis. So for example, in the code above, I could add:\r\n```python\r\n    def second_step(self, state: _IntermediateType) -> _OutputType:\r\n        # By default, pass through state unmodified\r\n        static_assert issubclass(_IntermateType, _OutputType)\r\n        return cast(_OutputType, state)\r\n```\r\nIn the definition of the base class, this would be assumed to be true if there were any possible unification of `_IntermediateType` and `_OutputType` that would allow it to be true (defaulting to true or producing a warning if this is non-trivial to evaluate), but this `static_assert` would be re-evaluated, whenever this class was subclassed/instantiated or this method was called, and if it could ever be determined to be false, would cause Mypy to raise an error.\r\n\r\nAny thoughts on this?"}}
{"actor":{"display_login":"TV4Fun"}}