Skip to content

simondoesstuff/snake_shift

Repository files navigation

Snake Shift

Tests Python License

A powerful Python refactoring tool that converts camelCase codebases to pythonic naming conventions while intelligently preserving external library calls.

Features

  • Smart Environment Detection - Automatically distinguishes between internal and external modules
  • Aggressive Refactoring - Converts entire codebases while preserving external library APIs
  • File & Directory Renaming - Renames files and directories to match pythonic conventions
    • Ignores non-code directories.
  • Gitignore Integration - Respects .gitignore patterns and includes sensible defaults
  • PascalCase Preservation - Keeps class names and type imports in PascalCase
  • LibCST-Powered - Uses concrete syntax trees for accurate code transformation
  • Dry Run Support - Preview changes before applying them

Quick Start

# Install the tool
pip install snake-shift

# Preview changes to a single file
snake-shift my_file.py --dry-run

# Refactor code and rename files in a directory
snake-shift src/ --rename-files

# Just refactor code without renaming files
snake-shift project/ --dry-run

Before & After

Before:

# myModule.py
import pandas as pd
from myPackage.dataUtils import processData

class myClass:
    def myMethod(self, inputData):
        df = pd.DataFrame(inputData)
        processedData = processData(df.dropna())
        return processedData

After:

# my_module.py
import pandas as pd
from my_package.data_utils import process_data

class MyClass:
    def my_method(self, input_data):
        df = pd.DataFrame(input_data)  # External library preserved!
        processed_data = process_data(df.dropna())
        return processed_data

How It Works

1. Environment-Based Module Detection

Unlike other tools that use hardcoded library lists, snake-shift intelligently detects external modules by:

  • Checking if modules are installed in your Python environment
  • Identifying standard library modules
  • Recognizing common external packages even when not installed
  • Treating unknown modules as internal (local code)

2. Pythonic Convention Application

  • Classes ? PascalCase (MyClass)
  • Functions & Variables ? snake_case (my_function, my_var)
  • PascalCase Imports ? Preserved (Dict, Path, MyClass)
  • External Library Calls ? Untouched (pd.DataFrame, np.zeros)

3. File System Organization

With --rename-files:

  • myModule.py $\to$ my_module.py
  • dataUtils/ $\to$ data_utils/

Installation

pip install snake-shift

Or install from source:

git clone https://github.com/simondoesstuff/snake_shift.git
pip install -e .

Usage

Command Line Interface

snake-shift [OPTIONS] PATH

Options:

  • --dry-run, -n - Show changes without writing to files
  • --rename-files, -r - Also rename files and directories
  • --stdout - Print refactored code to stdout (single files only)
  • --verbose, -v - Show detailed output during processing
  • --help - Show help message

Examples:

# Preview all changes to a project
snake-shift my_project/ --rename-files --dry-run

# Refactor a single file
snake-shift utils.py

# Refactor directory with file renaming
snake-shift src/ --rename-files --verbose

# Output refactored code to stdout
snake-shift my_script.py --stdout

Python API

from snake_shift import refactor_source, refactor_directory

# Refactor source code string
code = """
def myFunction(inputData):
    return inputData.lower()
"""
refactored = refactor_source(code)
print(refactored)
# Output: def my_function(input_data):\n    return input_data.lower()

# Refactor entire directory
from pathlib import Path
refactor_directory(
    Path("my_project/"),
    rename_files=True,
    dry_run=False
)

What Gets Refactored

Internal Code (Your Code)

  • Variable names: myVar $\to$ my_var
  • Function names: myFunction $\to$ my_function
  • Class names: myClass $\to$ MyClass
  • Module imports: from myPackage.myModule $\to$ from my_package.my_module
  • File names: myModule.py $\to$ my_module.py
  • Directory names: myPackage/ $\to$ my_package/

External Code (Preserved)

  • Library calls: pd.DataFrame() stays pd.DataFrame()
  • Standard library: os.path.join() stays os.path.join()
  • PascalCase imports: from typing import Dict stays Dict
  • External attributes: model.fit() stays model.fit()

Contributing

Contributions are welcome!

Building

# run tests with,
uv run pytest

# or just set up the environment,
uv sync

# or if you don't have UV,
pip install -r requirements.txt
pytest  # tests

License

MIT Licence

Acknowledgments

  • Built with LibCST for accurate Python code transformation
  • CLI powered by Typer
  • Inspired by the need for better Python refactoring tools that understand modern codebases

About

smart codebase refactor camelCase to pythonic_case

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages