Skip to content

pylint crashed with a AstroidError #7821

Closed
@John2013

Description

@John2013

Bug description

When linting, pylint crashed with a AstroidError and with the following stacktrace:

import csv
import logging

from django.core.management import BaseCommand
from django.db import transaction
from pik.core.shortcuts import update_or_create_object

from building_parts.models import BuildingPart, BuildingPartType
from core.utils.common import slugify_word
from housing.models import Building
from utility_systems.models import (
    UtilitySystemType, UtilitySystemExtraAttr, Elevator)

LOGGER = logging.getLogger(__name__)


def _read_rows_from_file(file_name):
    with open(file_name, encoding='utf-8') as csv_file:
        reader = csv.reader(csv_file, delimiter=';')
        for row in reader:
            yield row


class Command(BaseCommand):
    help = 'Upload elevators data'

    LIVING_ENTRANCE_TYPE_UID = '6e1b0154-4ac4-4511-81f7-ae80bdacc64f'
    ELEVATOR_TYPE_UID = '0167fa4a-f51f-1e4a-cfd3-7d6f33726faa'
    ACTIVITY_CODE_COLUMN = 3
    BUILDING_PART_NAME_COLUMN = 2
    ELEVATOR_NAME_COLUMN = 7
    ERP_CODE_COLUMN = 6

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.living_entrance_type = (
            BuildingPartType.objects.get(uid=self.LIVING_ENTRANCE_TYPE_UID))
        self.elevator_type = UtilitySystemType.objects.get(
            uid=self.ELEVATOR_TYPE_UID)
        self.headers = ()
        self.header_to_slug = {}
        self.extra_rows_start = 8

    def _is_valid_slug(self, slug):
        return UtilitySystemExtraAttr.objects.filter(
            slug=slug, utility_system_type=self.elevator_type).exists()

    def _validate_slugs(self, headers):
        header_to_slug = {}
        for header in headers:
            slug = slugify_word(header)
            if self._is_valid_slug(slug):
                header_to_slug[header] = slug
        return header_to_slug

    def _get_extra_attrs(self, extra_columns):
        result = {}
        for index, column in enumerate(
                extra_columns, start=self.extra_rows_start):
            header = self.headers[index]
            if header in self.header_to_slug:
                result[self.header_to_slug[header]] = column

        return result

    def _parse_row(self, row):
        activity_code = row[self.ACTIVITY_CODE_COLUMN].strip()
        building_part_name = row[self.BUILDING_PART_NAME_COLUMN]

        if building_part_name:
            building_part_name = f'№ {building_part_name.zfill(2)}'
        else:
            building_part_name = None

        elevator_name = row[self.ELEVATOR_NAME_COLUMN]
        extra_attrs = self._get_extra_attrs(row[self.extra_rows_start:])
        return {
            'activity_code': activity_code,
            'erp_code': row[self.ERP_CODE_COLUMN],
            'building_part_name': building_part_name,
            'elevator_name': elevator_name,
            'extra_attrs': extra_attrs}

    def _update(self, data):
        building_part_name = data['building_part_name']
        building_id = Building.objects.values_list('id', flat=True).get(
            activity_code=data['activity_code'])

        if building_part_name:
            building_part, _ = BuildingPart.objects.get_or_create(
                building_part_type=self.living_entrance_type,
                building_id=building_id, name=data['building_part_name'])
        else:
            building_part = None

        kwargs = {
            'utility_system_type': self.elevator_type,
            'building_part': building_part, 'building_id': building_id,
            'name': data['elevator_name']}
        elevator, _, _ = update_or_create_object(
            Elevator, search_keys={'erp_code': data['erp_code']}, **kwargs)

        elevator.extra_attrs.update(data['extra_attrs'])
        elevator.save()

    def add_arguments(self, parser):
        parser.add_argument('file_name', type=str)

    def handle(self, *args, **options):
        rows = list(_read_rows_from_file(options['file_name']))
        total_rows = len(rows) - 1
        LOGGER.debug(f'{total_rows}')
        self.headers = rows[0]
        self.header_to_slug = self._validate_slugs(self.headers)
        with transaction.atomic():
            Elevator.objects.all().delete()
            for line, row in enumerate(rows[1:]):
                LOGGER.debug(f'Line: {line}')
                data = self._parse_row(row)
                try:
                    self._update(data)
                except Building.DoesNotExist:
                    LOGGER.warning(
                        f"Building {data['activity_code']} doesn't exists!")
                    continue
                except Building.MultipleObjectsReturned:
                    LOGGER.warning(
                        f"Multiple buildings for {data['activity_code']} "
                        "returned!")
                    continue
                except Exception as exc:  # noqa: pylint=broad-except
                    LOGGER.error(exc)

pylint crashed with a AstroidError and with the following stacktrace:

Traceback (most recent call last):
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/lint/pylinter.py", line 790, in _lint_file
    check_astroid_module(module)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/lint/pylinter.py", line 1060, in check_astroid_module
    retval = self._check_astroid_module(
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/lint/pylinter.py", line 1110, in _check_astroid_module
    walker.walk(node)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/utils/ast_walker.py", line 93, in walk
    self.walk(child)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/utils/ast_walker.py", line 93, in walk
    self.walk(child)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/utils/ast_walker.py", line 93, in walk
    self.walk(child)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/utils/ast_walker.py", line 90, in walk
    callback(astroid)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 682, in visit_for
    self._check_unnecessary_list_index_lookup(node)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2113, in _check_unnecessary_list_index_lookup
    has_start_arg, confidence = self._enumerate_with_start(node)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2238, in _enumerate_with_start
    start_val, confidence = self._get_start_value(keyword.value)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2255, in _get_start_value
    start_val = node.value
AttributeError: 'Attribute' object has no attribute 'value'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/lint/pylinter.py", line 755, in _lint_files
    self._lint_file(fileitem, module, check_astroid_module)
  File "/Users/timofeeves/projects/housing-service-backend/venv3/lib/python3.8/site-packages/pylint/lint/pylinter.py", line 792, in _lint_file
    raise astroid.AstroidError from e
astroid.exceptions.AstroidError

with pylint==2.15.5 its ok

Configuration

test-warnings: true
strictness: veryhigh

uses:
  - django
  - celery

pylint:
  options:
    django-settings-module: _project_.settings
  disable:
    - unused-argument
    - too-few-public-methods
    - too-many-arguments
    - too-many-ancestors
    - redefined-outer-name
    - relative-beyond-top-level
    - logging-fstring-interpolation
    - no-else-return
    - too-many-instance-attributes
    - not-an-iterable
    - too-many-lines
    - import-outside-toplevel
    - unnecessary-pass
    - invalid-str-returned
    - unnecessary-comprehension

Command used

prospector --profile-path . --profile .prospector.yaml foo.py

Pylint output

foo.py:1:1:
    L1:0 None: pylint - astroid-error
    foo.py: Fatal error while checking 'foo.py'. Please open an issue in our bug tracker so we address this. There is a pre-filled template that you can use in '/Users/timofeeves/Library/Caches/pylint/pylint-crash-2022-11-22-12-54-52.txt'.

Expected behavior

Pylint does not crash and shows its linting messages (if any) for foo.py

Pylint version

pylint 2.15.6
astroid 2.12.13
Python 3.8.10 (v3.8.10:3d8993a744, May  3 2021, 09:09:08) 
[Clang 12.0.5 (clang-1205.0.22.9)]

Metadata

Metadata

Assignees

Labels

Crash 💥A bug that makes pylint crashNeeds PRThis issue is accepted, sufficiently specified and now needs an implementation

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions