1

I am trying to understand the usage of _nameToLevel and _levelToName methods available in logging. I tried to read up using documentation (https://docs.python.org/3.8/library/logging.html) but was not really able to find any information with any good example.

Next attempt was to understand by going into logging.__init__.py

RITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

_levelToName = {
    CRITICAL: 'CRITICAL',
    ERROR: 'ERROR',
    WARNING: 'WARNING',
    INFO: 'INFO',
    DEBUG: 'DEBUG',
    NOTSET: 'NOTSET',
}
_nameToLevel = {
    'CRITICAL': CRITICAL,
    'FATAL': FATAL,
    'ERROR': ERROR,
    'WARN': WARNING,
    'WARNING': WARNING,
    'INFO': INFO,
    'DEBUG': DEBUG,
    'NOTSET': NOTSET,
}

Due to the underscore use the intention of these dictionaries to be private and use getLevelName to access.

But I see code bases using from logging import _levelToName, _nameToLevel. Trying this on Python REPL is fine.

Python 3.8.10 (default, May 19 2021, 11:01:55) 
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from logging import _nameToLevel, _levelToName
>>> 

But when you try to do this in editor (PyCharm) you ended up getting following even with PyCharm is accurately setup to point to the Conda environment

Cannot find reference '_levelToName' in '__init__.pyi'
Cannot find reference '_nameToLevel' in '__init__.pyi'

Questions:

  1. Why is PyCharm is complaining?
  2. A usecase / example of usage?
bad_coder
  • 11,289
  • 20
  • 44
  • 72
add-semi-colons
  • 18,094
  • 55
  • 145
  • 232

1 Answers1

2
  1. Why is PyCharm is complaining?

Looking closely at the error message

Cannot find reference '_levelToName' in '__init__.pyi'

Notice the difference in the extension, .pyi has an additional i it's not just .py. So the import error is in the stub file, namely this one from the typeshed __init__.pyi not the runtime __init__.py whose excerpt you included in the beginning of the question.

What is interesting is that the private attributes _levelToName and _nameToLevel are in fact in the Python 3.9 stub file

__init__.pyi lines 27-28

_levelToName: dict[int, str]
_nameToLevel: dict[str, int]

You are using Python 3.8, the Python source code has changed in the meanwhile to use Generic Alias Type. Below is the same Python 3.8 code, notice the use of the now deprecated typing.Dict

__init__.pyi lines 41-45

if sys.version_info >= (3,):
    _levelToName: Dict[int, str]
    _nameToLevel: Dict[str, int]
else:
    _levelNames: Dict[Union[int, str], Union[str, int]]  # Union[int:str, str:int]

In conclusion, this is a bug in the PyCharm Linter. The IDE underlines the import with a red scribble, but if you run/debug the code the import works fine at runtime. You can ignore the import error because it's just a Linter warning, but if you use the imported attributes later in the code the Linter will add additional scribbles. Here's a screenshot running Python 3.8.0 with PyCharm 2021.1.3 Pro.

enter image description here

I tried searching on the JetBrains bug tracker for PyCharm but apparently this specific issue hasn't been reported yet.

  1. A usecase / example of usage?

Well you are supposed to access _nameToLevel and _levelToName using functions like logging.getLevelName(level), etc...If you check the Python source code there's only 1 function to get and 1 function to set _levelToName. The concept behind this is using these 2 functions with the predefined Logging Levels specified in the documentation.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • 1
    Thanks, yes totally agree with your point on `getLevelName` method but so many code bases that are directly accessing this dictionary of levels example just to iterate over all and see if they exists. Very strange implementation. But your answer is great. thanks. – add-semi-colons Jul 12 '21 at 15:08