Convert `import …` into `from … import …` in source code – Python

by
Alexei Petrov
mysql-connector-python

Quick Fix: Utilize AST to identify attribute usage and convert import to from module import names, ... by transforming the code’s AST and replacing Import and Attribute nodes.

The Problem:

Transform Python code to replace import time with from time import sleep if time.sleep() is called in the code.

The Solutions:

Solution 1: Parse code into Abstract Syntax Tree (AST) and transform AST nodes

To transform `import …` statements into `from … import …` statements, we can first parse the code into an Abstract Syntax Tree (AST), which is a tree-like representation of the program’s structure. We can then use a NodeTransformer to modify the AST to achieve the desired transformation.

Here’s a step-by-step breakdown of this solution:

  1. Parse code into AST: Use the `ast.parse()` function to convert the code string into an AST object. This AST object represents the structure of the code.
  2. Find attribute usage: Walk through the AST and collect information about which modules and attributes are being used. For example, if the code contains a statement `time.sleep(3)`, we record that `time` is being used and that the attribute `sleep` is being accessed.
  3. Create a transformation class: Define a subclass of `ast.NodeTransformer` that will transform the AST nodes to achieve the desired result. This class will contain methods for visiting different types of AST nodes and modifying them as needed.
  4. Transform AST using NodeTransformer: Instantiate the NodeTransformer subclass and use its `visit()` method to modify the AST. In the transformer class, you can visit the `Import` and `Attribute` nodes and apply the appropriate transformations.
  5. Generate transformed code: After transforming the AST, use the `ast.unparse()` function to convert the modified AST back into code string. This code string will have the desired `from … import …` statements instead of `import …` statements.

This solution provides a structured and systematic way to transform `import …` statements into `from … import …` statements by leveraging the power of AST manipulation. It can handle complex code structures and ensure that the transformed code retains the original functionality and semantics.

Solution 2: “Extract library usage and import accordingly”

To effectively transform the provided code, we take the following approach:

  1. Library Extraction:
    We begin by identifying all the libraries imported into the code. This is done by parsing import statements and extracting the library names. For instance, in the given code, we would identify that math, numpy, random, and time are imported.
  2. Library Usage Analysis:
    Next, we investigate how these libraries are being used in the code. We scan through the code, line by line, searching for instances where library methods or attributes are referenced. For each library, we collect information on how it’s being utilized.
  3. Library Import Generation:
    Based on the usage analysis, we construct the necessary from … import … statements. For each library, we include the methods or attributes that are actually being used. This ensures that only the necessary parts of the library are imported.
  4. Unused Import Handling:
    If there are any libraries imported but not used in the code, we group them separately and import them using the standard import statement. This ensures that the code maintains its original behavior.
  5. Code Transformation:
    Finally, we assemble the transformed code by combining the newly generated import statements with the modified code where library references have been replaced with their respective from … import … statements. The result is a codebase that employs from … import … statements wherever applicable, while still maintaining the original behavior.

This approach provides a comprehensive solution that accurately identifies and handles library usage, leading to effective code transformation with the desired from … import … statements while preserving the integrity and behavior of the original code.

Solution 3: Using Regular Expression and Function

The provided Python code defines a function called transform_code that takes a string containing Python code as input and modifies it in the following ways:

  1. Handling time.sleep():

    • It first checks if the string contains the expression time.sleep(. If found, it replaces the line import time with from time import sleep.
    • Additionally, it replaces all occurrences of time.sleep( with sleep(.
  2. Generalizing to Other Import Statements:

    • The code then replaces other import statements with from statements. It identifies the imported module name and the functions used from that module in the code.
    • It creates a string with from {module} import {function1}, {function2}, ... based on the identified module name and functions.

Here’s how the code works:

  • The transform_code function takes the input code a as an argument.

  • It first checks if the string time.sleep( exists in a using the if 'time.sleep(' in code: condition.

    • If it does, it replaces import time with from time import sleep using re.sub(r'import time\n', 'from time import sleep\n', code).
    • It also replaces time.sleep( with sleep( using re.sub(r'time\.sleep\(', 'sleep(', code).
  • To handle other import statements, the code uses a regular expression re.sub(r'import (\w+)', replace_imports, code) to identify import statements and replace them with from statements.

    • The replace_imports function is defined as a nested function within transform_code.
    • It takes a match object as input and returns a modified string.
    • It extracts the module name from the match object and identifies the functions used from that module in the code.
    • It creates and returns a string with from {module} import {function1}, {function2}, ....
    • If no functions are used from the imported module, it returns an empty string.
  • Finally, the modified code is returned by the transform_code function.

When you call transform_code(a) with the input code a, it modifies the code as follows:

  • It replaces import time with from time import sleep and time.sleep( with sleep(.
  • It replaces import math, numpy, random with from math import sin, from numpy import random, respectively.
  • It replaces from PIL import Image with from PIL import Image.

The transformed code is then printed, and you will see the modified code with from statements and the sleep() function call.

Solution 4: Parsing and Mapping

This solution takes a different approach by parsing the input code and mapping modules to their functions. It operates in the following steps:

  1. Parsing:

    • Splits the code into lines and processes each line.
    • Identifies lines that start with import or from.
    • Extracts imported modules and functions.
  2. Mapping:

    • Creates a dictionary (funcs) to map modules to their functions.
    • For each module, it stores a list of functions imported from that module.
  3. Rewriting the Code:

    • It iterates through the code, line by line.
    • For import lines, it constructs the new import statement based on the information in the funcs dictionary.
    • For lines with method calls, it replaces the module name with the corresponding import statement.
    • It retains other lines (e.g., function calls) as they are.
  4. Output:

    • The modified code is returned, with the import statements sorted alphabetically and the method calls using the appropriate imports.

This solution provides a more structured approach to the problem by keeping track of imported modules and functions. It ensures proper handling of both import and from statements while maintaining the original structure of the code. However, it relies on the find_items function to extract imported items, which could be improved for more robust parsing.

Q&A

Convert import time to from time import sleep

Replace import time with from time import sleep and replace time.sleep() with sleep().

Using regular expression to replace import with import from

Collect names from attribute accesses, use a NodeTransformer subclass to transform Import and Attribute nodes.

Using regular expression to find python modules and function called during runtime.

Using string manipulation and list comprehension to find modules and functions called during runtime.

Video Explanation:

The following video, titled "GEE Tutorial #15 - Converting Earth Engine JavaScripts to Python ...", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

This tutorial shows you how to convert Earth Engine JavaScripts to Python code directly within Jupyter notebook ... How to import the shapefile to ...