Search code examples
pythontypesmypyliterals

python types: Literal of logging level as type?


the following code:

import logging

print(type(1))
print(type(logging.WARNING))

prints:

<class 'int'>
<class 'int'>

yet, according to mypy, the first line of this code snippet is legal, but the second is not (Variable "logging.WARNING" is not valid as a type):

OneOrTwo = Literal[1,2]  # ok
WarningOrError = Literal[logging.WARNING, logging.ERROR]  # not ok

I do not understand why the definition of OneOrTwo is ok but WarningOrError is not.

I would like also know what could be done to use a legal equivalent of WarningOrError, i.e. something I could use like this:

def a(arg: WarningOrError)->None:
    pass

note: mypy redirects to https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases , but this did not clarify things for me.


Solution

  • Literal only accepts literal arguments. PEP586 has very rigid definitions of what constitutes a "literal" in this context. You can read about it here. The problem with your definition of WarningOrError is that the actual definition of logging.ERROR and WARNING makes them mutable (you can look at the source code of logging here), and hence illegal literals. Constant expressions like 1 or 2, on the other hand, can be known statically (they never change!) and therefore are acceptable literals.

    Among the accepted legal literals, Enum objects can help you achieve what you want:

    import logging                                                                                                                        
                                                                                                                                          
    from typing import Literal                                                                                                            
                                                                                                                                          
    from enum import Enum                                                                                                                 
                                                                                                                                          
    class Log(Enum):                                                                                                                      
        ERROR = logging.ERROR                                                                                                             
        WARNING = logging.WARNING                                                                                                         
                                                                                                                                          
    LogType = Literal[Log.ERROR, Log.WARNING]
    
    def a(arr: LogType):
        pass