The logic is similar to that for instance attributes. Top-level class
attributes and methods defined in the class body get the precedence,
followed by class attributes defined with assignments in @classmethods
unless the latter would resolve to the same assignments as in
cls.attr = cls.attr + 1
finally, we scan through all other class methods resolving the name
to the first definition inside one of them.
So far, I intentionally didn't expose such attributes in findClassAttribute()
or getClassAttributes() because users of these methods assume that
this API considers only attributes defined immediately in the class body.
Adding extra definitions from class methods might break these usages.
I had to update the inspection about typing.Final, because it relied
on the fact that resolve() on assignment targets on class objects can
lead only to those top-level class attributes, where type hints are normally
located, but now it can lead to assignments to a qualified attribute inside
a containing class method.
GitOrigin-RevId: 0ca5bdaa4efca127ac187e822a49df6795e1028a
Namely, jump from an empty placeholder __init__.py under python_stubs to the
corresponding real package from a library. Climbing up the series of __init__.py's
is necessary to detect where the name is exported, but previously these parallel
hierarchies were not supported.
GitOrigin-RevId: 3e3d5b8175758f03d24454a29e3f5d7fa120423a
Now import elements from the same directory have priority in resolve
over other equally named elements in plain directories and other equally
named elements have priority over same directory modules in ordinary and
namespace packages.
In libraries, the same directory elements should not be resolved in import.
(cherry picked from commit 2b5103dae6ca3828eee7891be248acf99bc7eb56)
IJ-CR-3755
GitOrigin-RevId: 52161f5ba43415d3ceb2ce3dc2122832da2c6197
* Fix a dysfunctional assertion, comparing a wrong PSI file.
* Assert that the resolve result is a file or a directory. "os" from the standard
library actually resolves to a directory in Typeshed, so getContainingFile() call
on it led to an NPE.
* Remove __init__.py from the directory where the same directory import is
performed. It better reflects the future policy when imports from explicit
packages have priority of absolute resolve results, while imports from plain
directories have priority of same directory results (see PY-45114).
GitOrigin-RevId: 5262032a38e653ce9114402fbb710aedfd278773
The cache of resolved qualified names is not context aware, so it's not safe to
keep such results in it. The legacy same directory imports in Python 2 have
already been taken into account and excluded, but we forgot to do the same for
recently enabled same directory imports in Python 3 project sources.
GitOrigin-RevId: f8885543ffa34ea652368a4f190210439173bdfc
There are 3 types of directories: plain directories, ordinary packages (with
__init__.py) and PEP 420 namespace packages. There are 3 types of imports:
absolute (from a root), same directory (absolute import from the current
directory when it's not explicitly marked as a root) and relative imports
(imports that start with dot).
Absolute imports are correct in all kinds of directories.
Same directory imports are correct in Python 2 in all cases and in Python 3 if
we have the directory containing the script with this import in Python path at
runtime. Users of Python 3 often face the problem when they can run the script
from the console because the directory containing this script got into Python
path but still have red underline and an unresolved reference error in the same
directory import because PyCharm didn't know that this file will be used as a
program's entry point. Previously, the way to fix such a problem was marking it
as a source root. But this action was not so obvious, especially for newcomers.
With this feature, such imports resolve successfully and now it is not necessary
to mark directories as source roots.
Relative imports are correct only in Python 3 namespace or ordinary packages and
should not be used in plain directories. If we have a relative import in plain
directory we highlight it with a weak warning and suggest 2 ways of fixing that:
marking directory as a namespace package explicitly (with quick fix or with Mark
As | Namespace Package) or changing this import to the same directory import
with a quickfix or manually.
Explicitly marking namespace packages can later be used for automatically
running files from them and ordinary packages with "-m".
The new resolve policy and explicit namespace packages can be disabled with the
Registry flag "python.explicit.namespace.packages".
These changes also address PY-40396. Namely, now any directory with __init__.py
inside or explicitly marked as a namespace package has a package icon,
regardless of its name or parents.
GitOrigin-RevId: 310fa562eb60121243cb6d68386ffc3e45c73245
Using PyTypeParser for this purpose is, first, too heavyweight since it does a lot of
unnecessary work, and, second, introduces unexpected problems related to the fact
that it was originally intended to resolve types in docstrings and thus too permissive
and depends on surrounding context such as existing imports.
Import resolution now gradually resolve qualified names one step at a
time to exclude cases when parent and child modules are resolved from
different source roots.
PyCharm used to fail to statically evaluate __all__.append() calls in
typing.py and concluded that since __all__ was too dynamic to analyse
it was better to assume that __all__ isn't present.
It affected resolving to internal module attributes not mentioned in
__all__ and even led to false positives in unused locals when these
internal names were imported from another module right next to
star-import from the module with __all__.
Since we switched to multi-resolve for imported modules, we have to
modify the contract of QualifiedNameResolver so it doesn't return all
the results across all the paths, but rather stops on the first set
of results for a particular path in sys.path.
We used to resolve members __init__.py of imported packages using only
the __init__.py itself, i.e. without considering their submodules. Now
we do that and we also don't filter unimported submodules because we
cannot detect them reliably. They might be imported in other modules
via some import chain.