Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect reportUndefinedVariable for builtin __qualname__ #1376

Closed
RMBeristain opened this issue Jun 1, 2021 · 7 comments
Closed

Incorrect reportUndefinedVariable for builtin __qualname__ #1376

RMBeristain opened this issue Jun 1, 2021 · 7 comments
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version

Comments

@RMBeristain
Copy link

RMBeristain commented Jun 1, 2021

Pylance mistakenly raises a reportUndefinedVariable warning for dundermethod__qualname__

Environment data

  • Language Server version: v2021.5.4
  • OS and version:
  • Python version (& distribution if applicable, e.g. Anaconda): cPython 3.8.5

Expected behaviour

Pylance should recognise qualname as valid dundermethod in a class defintion and not raise any warning.

Example:

class Test:
    "Pylance bug"
    my_name = __qualname__

>>> t = Test()
>>> t.my_name
'Test'

Actual behaviour

Pylance raises reportUndefinedVariable for the above code.

"__qualname__" is not defined

Logs

{
	"resource": "/mnt/c/Users/virtualraider/Documents/Code/GitLab/deleteme.py",
	"owner": "_generated_diagnostic_collection_name_#1",
	"code": {
		"value": "reportUndefinedVariable",
		"target": {
			"$mid": 1,
			"external": "https://github.com/microsoft/pylance-release/blob/main/DIAGNOSTIC_SEVERITY_RULES.md#diagnostic-severity-rules",
			"path": "/microsoft/pylance-release/blob/main/DIAGNOSTIC_SEVERITY_RULES.md",
			"scheme": "https",
			"authority": "github.com",
			"fragment": "diagnostic-severity-rules"
		}
	},
	"severity": 4,
	"message": "\"__qualname__\" is not defined",
	"source": "Pylance",
	"startLineNumber": 6,
	"startColumn": 15,
	"endLineNumber": 6,
	"endColumn": 27
}
@github-actions github-actions bot added the triage label Jun 1, 2021
@erictraut
Copy link
Contributor

The documentation for __qualname__ within a class does not suggest that it is accessible from within the class body itself. See PEP 3155 for the introduction of this attribute, and note that none of its examples show it being used within the class body. That said, I've confirmed that the Python interpreter does allow this. I wonder whether this was intentional or an oversight. It appears to be somewhat inconsistent because some dundered attributes like __doc__ and __module__ are accessible within the class body but others like __dict__ and __mro__ are not. Perhaps that's because the latter are still being constructed at the time the class body is executed.

The same PEP introduced __qualname__ for functions, and it is not accessible from within a function body (it generates a NameError at runtime).

Mypy (another popular type checker) also reports the use of __qualname__ and __module__ as errors.

So I wonder whether this is intended behavior or just an implementation detail that you should not be relying on.

Do you happen to know of any official Python documentation that explains that the use of __qualname__ within a class body is allowed?

@RMBeristain
Copy link
Author

Hi @erictraut , thanks for replying so quick!

I do not have any official documentation other than that PEP, sorry.

I have seen it in the dataclass implementation, which is where I found out about it

https://github.com/python/cpython/blob/a6a20658814e8668966fc86de0e80a4772864781/Lib/dataclasses.py#L582

I wanted to use it to build a qualified name hierarchy for logging so instead of:

class MyClass:
  log = module_logger.getChild("MyClass")

I can do

class MyClass:
  log = module_logger.getChild(__qualname__)

and this future-proofs any code refactoring of the module name.

@erictraut
Copy link
Contributor

I've updated the logic in pyright/pylance to handle this case consistently with the Python interpreter. I also filed a bug report for mypy, since it contains the same problem. After some experimentation, I found that __module__ needs to be handled in the same manner as __qualname__.

This will be fixed in the next release of pylance. Thanks for reporting the problem.

@erictraut erictraut added bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version and removed triage labels Jun 1, 2021
@jakebailey
Copy link
Member

This issue has been fixed in version 2021.6.0, which we've just released. You can find the changelog here: https://github.com/microsoft/pylance-release/blob/main/CHANGELOG.md#202160-2-june-2021

@maresb
Copy link

maresb commented Dec 8, 2021

@erictraut, there is unfortunately a regression of this bug since v2021.8.1. Could we please reopen this issue?

@erictraut erictraut removed the fixed in next version (main) A fix has been implemented and will appear in an upcoming version label Dec 8, 2021
@erictraut erictraut reopened this Dec 8, 2021
@erictraut
Copy link
Contributor

__qualname__ is very strange. It doesn't show up in the __dict__ for a class. If you call dir() for the class, it isn't listed. When you call locals() within a class body, it is listed. So it appears to be a local variable injected into the scope at the time the class body is executed.

I'll note that mypy doesn't handle this correctly either. It's not surprising given that it appears to be a very strange special case.

I've added some logic that makes this work. This will be included in the next release.

@erictraut erictraut added the fixed in next version (main) A fix has been implemented and will appear in an upcoming version label Dec 9, 2021
@bschnurr
Copy link
Member

This issue has been fixed in version 2021.12.1, which we've just released. You can find the changelog here: CHANGELOG.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed in next version (main) A fix has been implemented and will appear in an upcoming version
Projects
None yet
Development

No branches or pull requests

5 participants