classification
Title: Literal[True] prints as Literal[1] in some cases
Type: behavior Stage: resolved
Components: ctypes Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: gvanrossum, kornicameister, levkivskyi
Priority: normal Keywords: patch

Created on 2020-01-11 23:35 by kornicameister, last changed 2020-01-17 16:22 by gvanrossum. This issue is now closed.

Files
File name Uploaded Description Edit
scratch_1.py kornicameister, 2020-01-11 23:35 snipped to match comment
Pull Requests
URL Status Linked Edit
PR 17974 closed kornicameister, 2020-01-12 21:42
Messages (5)
msg359822 - (view) Author: kornicameister (kornicameister) * Date: 2020-01-11 23:35
Consider code (in attachment) that is being run on Python 3.9.
An expected output of such code ought to be:

(typing_extensions.Literal[1], typing_extensions.Literal[0]) (typing_extensions.Literal[True], typing_extensions.Literal[False])

However that's not the case. An output of the code, given that A is declared first, will be:

(typing.Literal[1], typing.Literal[0]) (typing.Literal[1], typing.Literal[0])

and if B is declared first we receive:

(typing.Literal[True], typing.Literal[False]) (typing.Literal[True], typing.Literal[False])

I believe a reason for that is having `bool` as subclass of `int` and consecutively having `typing._tp_cache` function that declares untyped cache. Indeed changing `cached = functools.lru_cache()(func)` to `cached = functools.lru_cache(typed=True)(func)` makes the linked code immune to A and B deceleration order.
msg359823 - (view) Author: kornicameister (kornicameister) * Date: 2020-01-11 23:54
Also happens in `3.8`, so right when `Literal` got moved to core `Python`.
msg359831 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-01-12 05:10
No need to open the attachment -- a simpler repro is:

>>> from typing import Literal
>>> Literal[1]
typing.Literal[1]
>>> Literal[True]
typing.Literal[1]
>>> 

However, in a fresh session

>>> from typing import Literal
>>> Literal[True]
typing.Literal[True]
>>> 

This must be because there's a cache in typing.py that use == but doesn't check the type. We can demonstrate that this is the case using a similar equivalence between 2.0 and 2:

>>> Literal[2.0]
typing.Literal[2.0]
>>> Literal[2]
typing.Literal[2.0]
>>> 

It looks like the function _tp_cache() is the culprit -- it uses functools.lru_cache() which apparently uses simple equality.

def _tp_cache(func):
    """..."""
    cached = functools.lru_cache()(func)
    _cleanups.append(cached.cache_clear)

    @functools.wraps(func)
    def inner(*args, **kwds):
        try:
            return cached(*args, **kwds)
        except TypeError:
            pass  # All real errors (not unhashable args) are raised below.
        return func(*args, **kwds)
    return inner

Here's a confusing demo:

>> @functools.lru_cache()
... def f(a): return a
... 
>>> f(1)
1
>>> f(True)
True
>>> f(1)
1
>>> f(1.0)
True
>>> 

(Confusing because it distinguishes between 1 and True, unlike Literal, but it then maps 1.0 to True.)

However a possible fix might be to add typed=True to the functools.lru_cache() call in typing.py, which should have the desired effect (not tested).
msg359864 - (view) Author: kornicameister (kornicameister) * Date: 2020-01-12 18:38
I will play around and maybe submit PR later this evening.
msg360151 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2020-01-16 23:06
In the PR (https://github.com/python/cpython/pull/17974) we found that there's no great solution for this. This issue will probably linger for a while, but maybe we should just set an example and close it, since we're unlikely to take any action?
History
Date User Action Args
2020-01-17 16:22:53gvanrossumsetstatus: open -> closed
resolution: wont fix
stage: patch review -> resolved
2020-01-16 23:06:29gvanrossumsetmessages: + msg360151
2020-01-12 21:42:05kornicameistersetkeywords: + patch
stage: patch review
pull_requests: + pull_request17381
2020-01-12 18:38:37kornicameistersetmessages: + msg359864
2020-01-12 05:10:39gvanrossumsetmessages: + msg359831
title: Literal declarations are not typed. Literal[True] can be Literal[1] if declared after latter. -> Literal[True] prints as Literal[1] in some cases
2020-01-11 23:54:26kornicameistersetmessages: + msg359823
versions: + Python 3.8
2020-01-11 23:52:39ned.deilysetnosy: + gvanrossum, levkivskyi
2020-01-11 23:36:57kornicameistersettitle: Literal[True] interpreted as Literal[1] -> Literal declarations are not typed. Literal[True] can be Literal[1] if declared after latter.
2020-01-11 23:35:15kornicameistercreate