Skip to content

Commit

Permalink
Merge branch 'master' into support-python-3.10
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierre-Sassoulas committed May 24, 2021
2 parents eabc5c8 + e9ec079 commit 511b0a3
Show file tree
Hide file tree
Showing 31 changed files with 660 additions and 103 deletions.
1 change: 1 addition & 0 deletions .github/SECURITY.md
@@ -0,0 +1 @@
Coordinated Disclosure Plan: https://tidelift.com/security
3 changes: 1 addition & 2 deletions .github/workflows/ci.yaml
Expand Up @@ -333,8 +333,7 @@ jobs:
id: generate-python-key
run: >-
echo "::set-output name=key::venv-${{ env.CACHE_VERSION }}-${{
hashFiles('setup.cfg', 'requirements_test_min.txt',
'requirements_test_brain.txt', 'requirements_test_pre_commit.txt') }}"
hashFiles('setup.cfg', 'requirements_test_min.txt') }}"
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.4
Expand Down
14 changes: 7 additions & 7 deletions .pre-commit-config.yaml
Expand Up @@ -22,31 +22,31 @@ repos:
hooks:
- id: black-disable-checker
- repo: https://github.com/asottile/pyupgrade
rev: v2.13.0
rev: v2.18.2
hooks:
- id: pyupgrade
exclude: tests/testdata
args: [--py36-plus]
- repo: https://github.com/ambv/black
rev: 20.8b1
- repo: https://github.com/psf/black
rev: 21.5b1
hooks:
- id: black
args: [--safe, --quiet]
exclude: tests/testdata|doc/
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v4.0.1
hooks:
- id: trailing-whitespace
exclude: .github/|tests/testdata
- id: end-of-file-fixer
exclude: tests/testdata
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
rev: v2.3.0
hooks:
- id: prettier
args: [--prose-wrap=always, --print-width=88]
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.0
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear]
Expand Down
48 changes: 48 additions & 0 deletions ChangeLog
Expand Up @@ -8,6 +8,54 @@ Release Date: TBA

* Astroid now support python 3.10.

* Fix detection of relative imports.
Closes #930
Closes PyCQA/pylint#4186

* Fix inference of instance attributes defined in base classes

Closes #932

* Do not set instance attributes on builtin object()

Closes #945
Closes PyCQA/pylint#4232
Closes PyCQA/pylint#4221
Closes PyCQA/pylint#3970
Closes PyCQA/pylint#3595

* Fix some spurious cycles detected in ``context.path`` leading to more cases
that can now be inferred

Closes #926

* Add ``kind`` field to ``Const`` nodes, matching the structure of the built-in ast Const.
The kind field is "u" if the literal is a u-prefixed string, and ``None`` otherwise.

Closes #898

* Fix property inference in class contexts for properties defined on the metaclass

Closes #940

* Update enum brain to fix definition of __members__ for subclass-defined Enums

Closes PyCQA/pylint#3535
Closes PyCQA/pylint#4358

* Update random brain to fix a crash with inference of some sequence elements

Closes #922

* Fix inference of attributes defined in a base class that is an inner class

Closes #904

* Allow inferring a return value of None for non-abstract empty functions and
functions with no return statements (implicitly returning None)

Closes #485

What's New in astroid 2.5.6?
============================
Release Date: 2021-04-25
Expand Down
8 changes: 3 additions & 5 deletions astroid/as_string.py
Expand Up @@ -184,10 +184,8 @@ def visit_classdef(self, node):
def visit_compare(self, node):
"""return an astroid.Compare node as string"""
rhs_str = " ".join(
[
f"{op} {self._precedence_parens(node, expr, is_left=False)}"
for op, expr in node.ops
]
f"{op} {self._precedence_parens(node, expr, is_left=False)}"
for op, expr in node.ops
)
return f"{self._precedence_parens(node, node.left)} {rhs_str}"

Expand Down Expand Up @@ -580,7 +578,7 @@ def visit_yield(self, node):
return f"({expr})"

def visit_yieldfrom(self, node):
""" Return an astroid.YieldFrom node as string. """
"""Return an astroid.YieldFrom node as string."""
yi_val = (" " + node.value.accept(self)) if node.value else ""
expr = "yield from" + yi_val
if node.parent.is_statement:
Expand Down
9 changes: 8 additions & 1 deletion astroid/bases.py
Expand Up @@ -207,8 +207,9 @@ def igetattr(self, name, context=None):
if not context:
context = contextmod.InferenceContext()
try:
context.lookupname = name
# avoid recursively inferring the same attr on the same class
if context.push((self._proxied, name)):
if context.push(self._proxied):
raise exceptions.InferenceError(
message="Cannot infer the same attribute again",
node=self,
Expand Down Expand Up @@ -328,6 +329,12 @@ def getitem(self, index, context=None):
raise exceptions.InferenceError(
"Could not find __getitem__ for {node!r}.", node=self, context=context
)
if len(method.args.arguments) != 2: # (self, index)
raise exceptions.AstroidTypeError(
"__getitem__ for {node!r} does not have correct signature",
node=self,
context=context,
)
return next(method.infer_call_result(self, new_context))


Expand Down
16 changes: 13 additions & 3 deletions astroid/brain/brain_namedtuple_enum.py
Expand Up @@ -85,7 +85,7 @@ def _extract_namedtuple_arg_or_keyword( # pylint: disable=inconsistent-return-s


def infer_func_form(node, base_type, context=None, enum=False):
"""Specific inference function for namedtuple or Python 3 enum. """
"""Specific inference function for namedtuple or Python 3 enum."""
# node is a Call node, class name as first argument and generated class
# attributes as second argument

Expand Down Expand Up @@ -248,7 +248,7 @@ def _get_renamed_namedtuple_attributes(field_names):


def infer_enum(node, context=None):
""" Specific inference function for enum Call node. """
"""Specific inference function for enum Call node."""
enum_meta = extract_node(
"""
class EnumMeta(object):
Expand Down Expand Up @@ -306,7 +306,7 @@ def __mul__(self, other):


def infer_enum_class(node):
""" Specific inference for enums. """
"""Specific inference for enums."""
for basename in node.basenames:
# TODO: doesn't handle subclasses yet. This implementation
# is a hack to support enums.
Expand All @@ -315,6 +315,7 @@ def infer_enum_class(node):
if node.root().name == "enum":
# Skip if the class is directly from enum module.
break
dunder_members = {}
for local, values in node.locals.items():
if any(not isinstance(value, nodes.AssignName) for value in values):
continue
Expand Down Expand Up @@ -372,7 +373,16 @@ def name(self):
for method in node.mymethods():
fake.locals[method.name] = [method]
new_targets.append(fake.instantiate_class())
dunder_members[local] = fake
node.locals[local] = new_targets
members = nodes.Dict(parent=node)
members.postinit(
[
(nodes.Const(k, parent=members), nodes.Name(v.name, parent=members))
for k, v in dunder_members.items()
]
)
node.locals["__members__"] = [members]
break
return node

Expand Down
2 changes: 2 additions & 0 deletions astroid/brain/brain_random.py
Expand Up @@ -9,6 +9,8 @@


def _clone_node_with_lineno(node, parent, lineno):
if isinstance(node, astroid.EvaluatedObject):
node = node.original
cls = node.__class__
other_fields = node._other_fields
_astroid_fields = node._astroid_fields
Expand Down
5 changes: 4 additions & 1 deletion astroid/brain/brain_type.py
Expand Up @@ -17,7 +17,7 @@
"""
import sys

from astroid import MANAGER, extract_node, inference_tip, nodes
from astroid import MANAGER, UseInferenceDefault, extract_node, inference_tip, nodes

PY39 = sys.version_info >= (3, 9)

Expand Down Expand Up @@ -47,6 +47,9 @@ def infer_type_sub(node, context=None):
:return: the inferred node
:rtype: nodes.NodeNG
"""
node_scope, _ = node.scope().lookup("type")
if node_scope.qname() != "builtins":
raise UseInferenceDefault()
class_src = """
class type:
def __class_getitem__(cls, key):
Expand Down
2 changes: 1 addition & 1 deletion astroid/builder.py
Expand Up @@ -67,7 +67,7 @@ def _can_assign_attr(node, attrname):
else:
if slots and attrname not in {slot.value for slot in slots}:
return False
return True
return node.qname() != "builtins.object"


class AstroidBuilder(raw_building.InspectBuilder):
Expand Down
2 changes: 1 addition & 1 deletion astroid/context.py
Expand Up @@ -102,7 +102,7 @@ def clone(self):
starts with the same context but diverge as each side is inferred
so the InferenceContext will need be cloned"""
# XXX copy lookupname/callcontext ?
clone = InferenceContext(self.path, inferred=self.inferred)
clone = InferenceContext(self.path.copy(), inferred=self.inferred.copy())
clone.callcontext = self.callcontext
clone.boundnode = self.boundnode
clone.extra_context = self.extra_context
Expand Down
3 changes: 2 additions & 1 deletion astroid/inference.py
Expand Up @@ -309,6 +309,7 @@ def infer_attribute(self, context=None):
elif not context:
context = contextmod.InferenceContext()

old_boundnode = context.boundnode
try:
context.boundnode = owner
yield from owner.igetattr(self.attrname, context)
Expand All @@ -319,7 +320,7 @@ def infer_attribute(self, context=None):
):
pass
finally:
context.boundnode = None
context.boundnode = old_boundnode
return dict(node=self, context=context)


Expand Down
8 changes: 3 additions & 5 deletions astroid/interpreter/_import/spec.py
Expand Up @@ -292,15 +292,13 @@ def _precache_zipimporters(path=None):
new_paths = _cached_set_diff(req_paths, cached_paths)
for entry_path in new_paths:
try:
pic[entry_path] = zipimport.zipimporter( # pylint: disable=no-member
entry_path
)
except zipimport.ZipImportError: # pylint: disable=no-member
pic[entry_path] = zipimport.zipimporter(entry_path)
except zipimport.ZipImportError:
continue
return {
key: value
for key, value in pic.items()
if isinstance(value, zipimport.zipimporter) # pylint: disable=no-member
if isinstance(value, zipimport.zipimporter)
}


Expand Down
6 changes: 2 additions & 4 deletions astroid/manager.py
Expand Up @@ -103,7 +103,7 @@ def ast_from_file(self, filepath, modname=None, fallback=True, source=False):
)

def ast_from_string(self, data, modname="", filepath=None):
""" Given some source code as a string, return its corresponding astroid object"""
"""Given some source code as a string, return its corresponding astroid object"""
# pylint: disable=import-outside-toplevel; circular import
from astroid.builder import AstroidBuilder

Expand Down Expand Up @@ -213,9 +213,7 @@ def zip_import_data(self, filepath):
except ValueError:
continue
try:
importer = zipimport.zipimporter( # pylint: disable=no-member
eggpath + ext
)
importer = zipimport.zipimporter(eggpath + ext)
zmodname = resource.replace(os.path.sep, ".")
if importer.is_package(resource):
zmodname = zmodname + ".__init__"
Expand Down
2 changes: 1 addition & 1 deletion astroid/mixins.py
Expand Up @@ -20,7 +20,7 @@


class BlockRangeMixIn:
"""override block range """
"""override block range"""

@decorators.cachedproperty
def blockstart_tolineno(self):
Expand Down
21 changes: 7 additions & 14 deletions astroid/modutils.py
Expand Up @@ -18,6 +18,7 @@
# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
# Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
# Copyright (c) 2021 Andreas Finkler <andi.finkler@gmail.com>

# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
# For details: https://github.com/PyCQA/astroid/blob/master/LICENSE
Expand All @@ -37,6 +38,8 @@
# We disable the import-error so pylint can work without distutils installed.
# pylint: disable=no-name-in-module,useless-suppression

import importlib
import importlib.machinery
import importlib.util
import itertools
import os
Expand Down Expand Up @@ -574,21 +577,11 @@ def is_relative(modname, from_file):
from_file = os.path.dirname(from_file)
if from_file in sys.path:
return False
name = os.path.basename(from_file)
file_path = os.path.dirname(from_file)
parent_spec = importlib.util.find_spec(name, from_file)
while parent_spec is None and len(file_path) > 0:
name = os.path.basename(file_path) + "." + name
file_path = os.path.dirname(file_path)
parent_spec = importlib.util.find_spec(name, from_file)

if parent_spec is None:
return False

submodule_spec = importlib.util.find_spec(
name + "." + modname.split(".")[0], parent_spec.submodule_search_locations
return bool(
importlib.machinery.PathFinder.find_spec(
modname.split(".", maxsplit=1)[0], [from_file]
)
)
return submodule_spec is not None


# internal only functions #####################################################
Expand Down
10 changes: 5 additions & 5 deletions astroid/node_classes.py
Expand Up @@ -2557,7 +2557,7 @@ class Const(mixins.NoChildrenMixin, NodeNG, bases.Instance):

_other_fields = ("value",)

def __init__(self, value, lineno=None, col_offset=None, parent=None):
def __init__(self, value, lineno=None, col_offset=None, parent=None, kind=None):
"""
:param value: The value that the constant represents.
:type value: object
Expand All @@ -2571,12 +2571,12 @@ def __init__(self, value, lineno=None, col_offset=None, parent=None):
:param parent: The parent node in the syntax tree.
:type parent: NodeNG or None
"""
self.value = value
"""The value that the constant represents.
:type: object
:param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.
:type kind: str or None
"""
self.value = value
self.kind = kind

super().__init__(lineno, col_offset, parent)

Expand Down

0 comments on commit 511b0a3

Please sign in to comment.