ml_switcheroo.core.scanners =========================== .. py:module:: ml_switcheroo.core.scanners .. autoapi-nested-parse:: AST Scanners for Symbol Usage Detection. This module provides LibCST visitors that analyze code to determine if specific names, frameworks, or aliases are actively referenced in the source body. These scanners are critical for the ``ImportFixer`` logic: 1. If a framework alias (e.g., ``jnp``) is injected, ``SimpleNameScanner`` verifies it is actually used before committing the import. 2. If a source import (e.g., ``import torch``) is slated for removal, ``UsageScanner`` checks if it persists in the code (e.g., inside an Escape Hatch) to prevent breaking valid code. Classes ------- .. autoapisummary:: ml_switcheroo.core.scanners.SimpleNameScanner ml_switcheroo.core.scanners.UsageScanner Functions --------- .. autoapisummary:: ml_switcheroo.core.scanners.get_full_name Module Contents --------------- .. py:function:: get_full_name(node: Union[libcst.Name, libcst.Attribute]) -> str Recursively resolves a CST Name or Attribute chain to a dot-separated string. This helper flattens the AST representation of dotted names into strings comparable with import definitions. :param node: The CST node representing the identifier. Typically a ``cst.Name`` (e.g., ``x``) or ``cst.Attribute`` (e.g., ``x.y``). :returns: The fully qualified string representation (e.g., "torch.nn.functional"). Returns an empty string if the node structure is not a supported Name/Attribute chain. :rtype: str .. py:class:: SimpleNameScanner(target_name: str) Bases: :py:obj:`libcst.CSTVisitor` Scans for the usage of a specific identifier in the code body. This visitor is designed to check for the presence of variables or aliases (like ``jnp``, ``tf``, ``mx``) *outside* of import statements. It is used to determine if a speculative import injection is actually required. .. py:attribute:: target_name .. py:attribute:: found :value: False .. py:method:: visit_Import(node: libcst.Import) -> None Flags entry into an ``import ...`` statement. Names appearing here are definitions, not usages. .. py:method:: leave_Import(node: libcst.Import) -> None Flags exit from an ``import ...`` statement. .. py:method:: visit_ImportFrom(node: libcst.ImportFrom) -> None Flags entry into a ``from ... import ...`` statement. .. py:method:: leave_ImportFrom(node: libcst.ImportFrom) -> None Flags exit from a ``from ... import ...`` statement. .. py:method:: visit_Name(node: libcst.Name) -> None Checks if the visited name matches the target. If the name matches ``target_name`` and we are NOT currently inside an import definition, we mark ``found = True``. :param node: The name node being visited. .. py:method:: should_traverse(_node: libcst.CSTNode) -> bool Optimization hook to stop traversal once found. :returns: False if the target has already been found, effectively short-circuiting the rest of the AST traversal. :rtype: bool .. py:class:: UsageScanner(source_fw: str) Bases: :py:obj:`libcst.CSTVisitor` Scans the AST for usages of a specific framework root or its local aliases. This class implements a multi-pass logic during a single traversal: 1. **Cataloging**: It identifies all aliases bound to the ``source_fw`` (e.g., ``import torch as t`` -> ``t`` is an alias). 2. **Detection**: It checks if any of these cataloged aliases are used in the code body (e.g., ``t.abs(x)``). This is primarily used by the ``ImportFixer`` to decide whether to prune the original import statement. If usages persist (e.g., via the Escape Hatch), the import MUST be preserved to keep the code valid. .. py:attribute:: source_fw .. py:attribute:: found_usages :type: Set[str] .. py:method:: get_result() -> bool Returns the scan result. :returns: True if any tracked alias was found used in the body. :rtype: bool .. py:method:: visit_Import(node: libcst.Import) -> None Catalogs names bound by ``import ...``. Logic: - ``import torch`` -> tracks 'torch'. - ``import torch as t`` -> tracks 't'. - ``import torch.nn as nn`` -> tracks 'nn' (because it stems from torch). :param node: The import node. .. py:method:: leave_Import(node: libcst.Import) -> None Exit import scope. .. py:method:: visit_ImportFrom(node: libcst.ImportFrom) -> None Catalogs names bound by ``from ... import ...``. Logic: - ``from torch import nn`` -> tracks 'nn'. - ``from torch.nn import Linear`` -> tracks 'Linear'. :param node: The import-from node. .. py:method:: leave_ImportFrom(node: libcst.ImportFrom) -> None Exit import-from scope. .. py:method:: visit_Name(node: libcst.Name) -> None Checks if a name in the body matches one of our tracked aliases. If found, it is recorded in ``found_usages``. :param node: The name node.