Skip to content

Commit

Permalink
Update DeprecatedABCImport Rule (#474)
Browse files Browse the repository at this point in the history
* Updated rule to catch case where import is wrapped in try block

* Satisfied typechecker

* Added comments

* Updated to use ParentNodeProvider

* Updated to walk up the node instead of checking a deterministic level

* Fixed typo
  • Loading branch information
surge119 authored Jul 29, 2024
1 parent 2795e01 commit 48491b4
Showing 1 changed file with 61 additions and 1 deletion.
62 changes: 61 additions & 1 deletion src/fixit/rules/deprecated_abc_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from typing import List, Optional, Union

import libcst as cst

import libcst.matchers as m

from libcst.metadata import ParentNodeProvider

from fixit import Invalid, LintRule, Valid


Expand Down Expand Up @@ -54,6 +55,7 @@ class DeprecatedABCImport(LintRule):

MESSAGE = "ABCs must be imported from collections.abc"
PYTHON_VERSION = ">= 3.3"
METADATA_DEPENDENCIES = (ParentNodeProvider,)

VALID = [
Valid("from collections.abc import Container"),
Expand All @@ -71,6 +73,47 @@ def test(self):
pass
"""
),
Valid(
"""
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping
"""
),
Valid(
"""
try:
from collections.abc import Mapping, Container
except ImportError:
from collections import Mapping, Container
"""
),
Valid(
"""
try:
from collections.abc import Mapping, Container
except ImportError:
def fallback_import():
from collections import Mapping, Container
"""
),
Valid(
"""
try:
from collections.abc import Mapping, Container
except Exception:
exit()
"""
),
Valid(
"""
try:
from collections import defaultdict
except Exception:
exit()
"""
),
]
INVALID = [
Invalid(
Expand Down Expand Up @@ -122,10 +165,27 @@ def __init__(self) -> None:
# The original imports
self.imports_names: List[str] = []

def is_except_block(self, node: cst.CSTNode) -> bool:
"""
Check if the node is in an except block - if it is, we know to ignore it, as it
may be a fallback import
"""
parent = self.get_metadata(ParentNodeProvider, node)
while not isinstance(parent, cst.Module):
if isinstance(parent, cst.ExceptHandler):
return True

parent = self.get_metadata(ParentNodeProvider, parent)

return False

def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
"""
This catches the `from collections import <ABC>` cases
"""
if self.is_except_block(node):
return

# Get imports in this statement
import_names = (
[name.name.value for name in node.names]
Expand Down

0 comments on commit 48491b4

Please sign in to comment.