Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SimpleCookie generic type

SimpleCookie is apparently a generic type and thus the following code (test.py) gives an error when checked with mypy:

from http.cookies import SimpleCookie

cookie = SimpleCookie()

test.py:3: error: Need type annotation for 'cookie'

Now if I change test.py line 3 to:

cookie: SimpleCookie = SimpleCookie()

I get the following error:

test.py:3: error: Missing type parameters for generic type "SimpleCookie"

SimpleCookie inherits from dict, has str keys and Morsel values, so I'd assume that the correct generic type annotation is something like this:

from http.cookies import Morsel, SimpleCookie

cookie: SimpleCookie[str, Morsel] = SimpleCookie()

But now the error is:

test.py:3: error: "SimpleCookie" expects 1 type argument, but 2 given

Changing line 3 to

cookie: SimpleCookie[str] = SimpleCookie()

suddenly makes mypy happy, but leaves me very confused why this is the correct solution, so I have two questions:

  1. Why is SimpleCookie a generic type with one argument?
  2. What's the best way to handle this in my code? Should I annotate SimpleCookie variables with SimpleCookie[str] (which seems like a lie to me) or should I just annotate them with Any and hope this will be cleaned up in future Python versions?

mypy version 0.750 and Python 3.8.0

like image 576
Agost Biro Avatar asked Dec 16 '19 12:12

Agost Biro


1 Answers

Explanation

str in SimpleCookie[str] actually refers to the type _T of coded_value in Morsel.

mypy uses https://github.com/python/typeshed/blob/master/stdlib/3/http/cookies.pyi:

class Morsel(Dict[str, Any], Generic[_T]):
    value: str
    coded_value: _T
    key: str
    def set(self, key: str, val: str, coded_val: _T) -> None: ...
    # ...

class BaseCookie(Dict[str, Morsel[_T]], Generic[_T]):
    # ...
    def value_decode(self, val: str) -> _T: ...
    def value_encode(self, val: _T) -> str: ...
    # ...
    def __setitem__(self, key: str, value: Union[str, Morsel[_T]]) -> None: ...

class SimpleCookie(BaseCookie[_T], Generic[_T]): ...

Correct typing

_T should be Any, i.e. SimpleCookie[Any], as explained in python/typeshed#3060:

Morsel does cast any value to string ... max-age can take an integer (unix time) and http-only a boolean.

Actually, I could not reproduce the error you get with this:

from http.cookies import SimpleCookie

cookie: SimpleCookie = SimpleCookie()
like image 90
aaron Avatar answered Sep 30 '22 01:09

aaron