Last active
September 2, 2023 11:37
-
-
Save vzakharov/72ef9255c8cd455767f896c6719f36e5 to your computer and use it in GitHub Desktop.
Python Relative Import Converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Relative Import Converter | |
# | |
# This Python script is used to convert absolute imports to relative imports in a Python project. | |
# It recursively traverses through all the Python files in the specified directory and its subdirectories. | |
# For each Python file, it identifies all the import statements and modifies them from absolute to relative. | |
# The script only converts imports from the current directory or its descendants, it does not handle | |
# imports from parent directories or unrelated directories (i.e., it doesn't do the `from ....../something` conversions). | |
# This is particularly useful when you want to make your Python modules/packages portable or when you are | |
# restructuring your project and need to update the import paths. | |
# | |
# Usage: | |
# Run the script from the command line with the directory path as argument. | |
# Example: python relative_import_converter.py <directory_path> | |
# | |
# Note: This script uses the `ast` module to parse the Python source code and the `re` module for string manipulation. | |
import os | |
import re | |
import ast | |
import argparse | |
import ast | |
def get_imports(source): | |
""" | |
Given a source code string, returns a generator that yields the names of all imported modules. | |
Args: | |
source (str): The source code string to parse. | |
Yields: | |
str: The name of an imported module. | |
""" | |
tree = ast.parse(source) | |
for node in tree.body: | |
if isinstance(node, ast.Import): | |
for alias in node.names: | |
yield alias.name | |
elif isinstance(node, ast.ImportFrom): | |
yield node.module | |
import re | |
def replace_imports(file_path, relative_path): | |
""" | |
Replaces imports in a Python file with relative imports. | |
Args: | |
file_path (str): The path to the Python file to modify. | |
relative_path (str): The relative path to the package containing the imports to replace. | |
Returns: | |
None | |
""" | |
with open(file_path, 'r') as file: | |
source = file.read() | |
imports = list(get_imports(source)) | |
for import_name in imports: | |
if import_name.startswith(relative_path): | |
source = re.sub(r'from ' + import_name + ' import', 'from .' + import_name[len(relative_path)+1:] + ' import', source) | |
with open(file_path, 'w') as file: | |
file.write(source) | |
import os | |
def process_directory(path): | |
""" | |
Recursively walks through a directory and its subdirectories, finds all Python files, and replaces their imports with relative imports. | |
Args: | |
path (str): The path to the directory to process. | |
Returns: | |
None | |
""" | |
for root, dirs, files in os.walk(path): | |
for file in files: | |
if file.endswith('.py'): | |
relative_path = os.path.relpath(root, path).replace(os.sep, '.') | |
replace_imports(os.path.join(root, file), relative_path) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Convert absolute imports to relative in a Python project.') | |
parser.add_argument('path', help='The directory path to process') | |
args = parser.parse_args() | |
process_directory(args.path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment