Skip to main content

Split top-level Python functions into dedicated modules and rewire imports automatically.

Project description

image

ManaSplice

ManaSplice is a small CLI for pulling top-level Python functions into their own files without leaving the original module broken.

It creates a modules/ package next to the source file, moves the function there, and rewrites the source module to import it back in.

What it does

  • Splits one top-level function with splitfunc.
  • Splits every top-level function in a file, or every top-level function in each Python file in a directory, with splitall.
  • Restructures modules toward a simple OOP shape with paradigm OOP.
  • Checks whether a split is safe, and shows the planned changes without writing files, with check.
  • Emits JSON plans with --json for automation.
  • Copies the imports and top-level definitions the extracted function needs.
  • Maintains modules/__init__.py so rewritten files can use a single merged import line.

Quick example

Starting point:

import math


def area(radius: float) -> float:
    return math.pi * radius * radius


def greet(name: str) -> str:
    return f"Hello, {name}!"

Run:

uv run manasplice splitfunc main.area

Result:

import math
from modules import area


def greet(name: str) -> str:
    return f"Hello, {name}!"
"""Auto-generated by ManaSplice from main.py."""

import math


def area(radius: float) -> float:
    return math.pi * radius * radius

Installation

For using in a project:

uv add manasplice

or

pip install manasplice

For local development in this repo:

uv sync

To run the CLI without installing it globally:

uv run manasplice --help

Commands

manasplice splitfunc <module>.<function>
manasplice splitall <path/to/file.py>
manasplice splitall --dir <directory>
manasplice splitall --dir <directory> --recursive
manasplice check <module>.<function>
manasplice check <module>.<function> --json
manasplice check <module>.<function> --project-check
manasplice check <path/to/file.py>
manasplice check --dir <directory>
manasplice undo [count]
manasplice config init
manasplice config show
manasplice splitfunc <module>.<function> --preview
manasplice splitfunc <module>.<function> --preview --json
manasplice splitall <path/to/file.py> --preview
manasplice splitall <path/to/file.py> --preview --json
manasplice splitfunc <module>.<function> --validate
manasplice splitall <path/to/file.py> --public-only --exclude main,_*
manasplice splitall <path/to/file.py> --auto-group
manasplice splitall <path/to/file.py> --group area,diameter,circumference --module geometry
manasplice splitfunc <module>.<function> --output-package generated
manasplice splitfunc <module>.<function> --output modules/geometry.py
manasplice splitfunc <module>.<function> --name circle_area
manasplice splitfunc <module>.<function> --into modules/geometry.py
manasplice splitfunc <module>.<function> --format
manasplice splitfunc <module>.<function> --require-clean-git
manasplice splitfunc <module>.<function> --git-commit
manasplice splitfunc <module>.<function> --keep-decorators
manasplice splitfunc <module>.<function> --strip-decorators
manasplice splitmethod <module>.<Class>.<method>
manasplice splitfunc <module>.<function> --force
manasplit paradigm OOP
manasplit paradigm functional <path/to/file.py>
manasplit paradigm event-driven <path/to/file.py>
manasplit paradigm procedural <path/to/file.py>
manasplit paradigm OOP --dir <directory> --recursive
manasplit paradigm OOP <path/to/file.py> --preview
manasplit paradigm OOP --dir <directory> --recursive --audit --json
manasplit paradigm OOP <path/to/file.py> --verify-command "python main.py"

Examples:

uv run manasplice splitfunc main.area
uv run manasplice splitfunc package.utils.normalize_name
uv run manasplice splitall main.py
uv run manasplice splitall --dir app
uv run manasplice splitall --dir app --recursive
uv run manasplice check main.area
uv run manasplice check main.area --project-check
uv run manasplice check main.py --public-only --exclude main,_*
uv run manasplice splitall main.py --preview
uv run manasplice splitall main.py --preview --json
uv run manasplice splitall main.py --public-only --exclude main,_*
uv run manasplice splitall main.py --auto-group
uv run manasplice splitall main.py --group area,diameter,circumference --module geometry
uv run manasplice splitfunc main.area --validate
uv run manasplice splitfunc main.area --output-package generated
uv run manasplice splitfunc main.area --output modules/geometry.py
uv run manasplice splitfunc main.area --into modules/geometry.py
uv run manasplice splitfunc main.area --format
uv run manasplice splitmethod services.UserService.normalize_name
uv run manasplit paradigm OOP
uv run manasplit paradigm functional main.py
uv run manasplit paradigm event-driven main.py
uv run manasplit paradigm procedural main.py
uv run manasplit paradigm OOP main.py --preview
uv run manasplit paradigm OOP --dir src --recursive --audit
uv run manasplit paradigm OOP main.py --verify-command "python main.py"
uv run manasplice undo

Configuration

ManaSplice reads [tool.manasplice] from the nearest pyproject.toml above --cwd.

Create a starter config:

manasplice config init

Show the active config:

manasplice config show
manasplice config show --json

Example:

[tool.manasplice]
output_package = "modules"
validate = true
public_only = true
exclude = ["main", "_*"]
recursive = true
format = "ruff"

Notes

  • ManaSplice primarily moves top-level functions. splitmethod supports conservative class-method extraction by generating a forwarding wrapper and refusing methods without explicit self/cls.
  • paradigm OOP converts safe top-level functions into @staticmethods on a generated module class and leaves compatibility wrappers in place. With no path, it scans the project recursively while skipping virtualenvs, caches, build output, and __init__.py.
  • paradigm functional adds a generated functional facade with FUNCTIONAL_API, pipe, and compose_functions without changing existing function definitions.
  • paradigm event-driven adds EVENT_HANDLERS and dispatch_event so functions can be invoked through event names.
  • paradigm procedural flattens ManaSplice-generated OOP staticmethod classes back into top-level functions while preserving a compatibility class.
  • Use paradigm --audit for a no-write transformability report with skip reasons and JSON support.
  • Broad functional and event-driven facade generation across multiple files is refused by default. Audit first, narrow the scope, or pass --allow-broad-facade when the generated facade APIs are intentional.
  • Use paradigm --verify-command "..." to run project-specific smoke checks after a successful write.
  • splitall is literal: it will split every top-level function it finds, including helper functions and main() if present.
  • Use check for a no-write safety report without diffs.
  • Use --json with check or preview commands to get a machine-readable plan instead of human text and colored diffs.
  • Use --preview to inspect planned edits with a safety report and unified diffs before ManaSplice writes anything.
  • Use --validate to make ManaSplice parse the generated Python source before it writes changes.
  • Use --include, --exclude, --public-only, and --recursive to make splitall selective instead of splitting every top-level function.
  • Use --auto-group to keep top-level functions that reference each other in the same generated module.
  • Use --group ... --module ... when architectural grouping should override call-graph grouping.
  • Use --output-package if you want generated modules somewhere other than modules/.
  • Use --output, --name, and --into to control the exact destination module, extracted function name, or append to an existing module.
  • Use --keep-decorators or --strip-decorators to control decorator handling for frameworks with registration side effects.
  • Use --format to run ruff format and ruff check --fix on changed Python files after writing.
  • Use --require-clean-git to refuse dirty working trees, or --git-commit to create a commit after a successful write.
  • ManaSplice refuses to overwrite an existing generated module unless you pass --force.
  • ManaSplice refuses splits that depend on mutable top-level globals, because copying those globals into a new module changes runtime state.
  • ManaSplice now refuses to split functions that participate in a local top-level dependency cycle, such as simple mutual recursion.
  • check reports async and generator functions explicitly.
  • Every non-preview split records rollback history in .manasplice_history.json, and undo replays the last recorded operation.
  • The generated modules/ package is part of the rewritten code, not just scratch output.

Development

Run tests with:

uv run python -m pytest tests --basetemp .pytest_tmp
uv run ruff check src tests
uv run mypy

CONTRIBUTION

Feel free to give feedback and/or suggest changes. This is just meant to be a helpful tool for larger projects made for fun.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

manasplice-0.1.2.tar.gz (130.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

manasplice-0.1.2-py3-none-any.whl (39.8 kB view details)

Uploaded Python 3

File details

Details for the file manasplice-0.1.2.tar.gz.

File metadata

  • Download URL: manasplice-0.1.2.tar.gz
  • Upload date:
  • Size: 130.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for manasplice-0.1.2.tar.gz
Algorithm Hash digest
SHA256 fe15156af6699357e104d266e62dc988c014eb1bc7cec217ace4d03b028183d4
MD5 b7411d418692ac0899995726990f2bed
BLAKE2b-256 8a1c433bf86b826916f5de5bcdd4e34d01e214b5a3b190cda20f2240011b69b5

See more details on using hashes here.

File details

Details for the file manasplice-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: manasplice-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 39.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for manasplice-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cb923359481b2fde7753e49a2e9637cc1d27fc945bc76b02ba8f6e6bdfeb2161
MD5 f60e30fe5a026b56b92e990d3c780eb6
BLAKE2b-256 ed86d6d1758959d1065d4f981f77e4dc7a091e9d6f95885df23e7cd355bc511b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page