This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: Literal[True] prints as Literal[1] in some cases
Type: behavior Stage: resolved
Components: ctypes Versions: Python 3.9, Python 3.8
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 2022-04-11 14:59 by admin. This issue is now closed.

File name Uploaded Description Edit 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]
>>> Literal[True]

However, in a fresh session

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

This must be because there's a cache in 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]
>>> Literal[2]

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)

    def inner(*args, **kwds):
            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)
>>> f(True)
>>> f(1)
>>> f(1.0)

(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, 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 ( 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?
Date User Action Args
2022-04-11 14:59:25adminsetgithub: 83489
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