simvx.editor.refactor_extract

Extract classes from a single .py file into a sibling folder/package.

The “Extract classes to folder” refactoring takes a multi-class scene file like src/things.py (containing classes Foo, Bar) and splits it into:

  • src/things/__init__.py: re-exports each extracted class

  • src/things/foo.py: class Foo and its required imports

  • src/things/bar.py: class Bar and its required imports

Importers like from src.things import Foo keep working transparently through Python’s package resolution (the new __init__.py provides the names).

Refusal cases (raise :class:ExtractRefused):

  • The file has fewer than two top-level classes: extraction is a no-op.

  • The file has top-level statements that are neither classes nor imports (free functions, module-level state, etc.), these have nowhere clean to land. The user must inline or relocate them before extraction.

  • A folder of the target name already exists.

A module docstring is not one of those statements, though it is an expression statement and was refused as one until 2026-09-07. See

func:

is_module_docstring.

Module Contents

Classes

ExtractResult

Outcome of :func:extract_to_folder.

Functions

extract_to_folder

Extract every top-level class from file_path into a sibling folder.

class_block

One class as a standalone block: the comments written above it, then its source.

is_module_docstring

Is stmt the file’s module docstring?

block_keyword

The keyword a block statement opens with, its decorators skipped.

Data

log

API

simvx.editor.refactor_extract.log

‘getLogger(…)’

exception simvx.editor.refactor_extract.ExtractRefused[source]

Bases: ValueError

Raised when a file cannot be cleanly extracted to a folder.

Initialization

Initialize self. See help(type(self)) for accurate signature.

class __cause__
class __context__
__delattr__()
__dir__()
__eq__()
__format__()
__ge__()
__getattribute__()
__getstate__()
__gt__()
__hash__()
__le__()
__lt__()
__ne__()
__new__()
__reduce__()
__reduce_ex__()
__repr__()
__setattr__()
__setstate__()
__sizeof__()
__str__()
__subclasshook__()
class __suppress_context__
class __traceback__
add_note()
class args
with_traceback()
class simvx.editor.refactor_extract.ExtractResult[source]

Outcome of :func:extract_to_folder.

folder: pathlib.Path

None

created_files: tuple[pathlib.Path, ...]

None

original_path: pathlib.Path

None

simvx.editor.refactor_extract.extract_to_folder(file_path: pathlib.Path, project_index: simvx.editor.project_classes.ProjectClassIndex | None = None) → simvx.editor.refactor_extract.ExtractResult[source]

Extract every top-level class from file_path into a sibling folder.

The folder is created at file_path.parent / file_path.stem. The original file_path is deleted on success.

Each extracted class file inherits the original file’s import block verbatim (lint may flag unused imports per file, the user can prune those manually). Inter-class dependencies (one extracted class subclasses another) are wired via a relative import inside the new folder.

project_index is refreshed at the end so downstream callers see the new layout. May be None for one-shot scripted use.

Raises :class:ExtractRefused on failure with a message pointing at the offending construct.

simvx.editor.refactor_extract.class_block(cls: simvx.core.scene_io.ClassDefRef) → str[source]

One class as a standalone block: the comments written above it, then its source.

A comment above a class describes that class, so it moves with it into whatever file the class lands in; the blank lines that spaced the class out from what preceded it do not, because what precedes it is not coming along.

simvx.editor.refactor_extract.is_module_docstring(stmt: simvx.core.scene_io.source.Statement, index: int) → bool[source]

Is stmt the file’s module docstring?

Python’s own rule, so that these refactorings agree with the language: the FIRST statement of the module, and a bare string literal. A string literal anywhere else is a no-op expression rather than a docstring, and stays subject to the ordinary side-effect refusal.

This exists because both refactorings classify statements by what they ARE, which is the right question for a free function or module-level state and the wrong one here: a docstring is an expression statement, so it tripped the same branch as do_something() and made extract_to_folder refuse nearly every hand-written file. The engine’s own emitted scenes carry no module docstring, which is why the test suite never reached it.

simvx.editor.refactor_extract.block_keyword(stmt: simvx.core.scene_io.source.Statement) → str[source]

The keyword a block statement opens with, its decorators skipped.

class, def, async def, if, for, with, try, while, match: what the statement is, at the coarseness

Class:

~simvx.core.scene_io.source.StatementKind deliberately does not draw, since a block that holds a class and one that runs at import time are the same kind of statement and not the same thing to a refactoring.