simvx.editor.project_classes

Discovery + cache for user-defined Node subclasses in the active project.

Walks the project src/ directory (configurable via simvx.toml [editor].class_files_dir) and parses each .py file with

func:

simvx.core.scene_io.parse_source to find top-level class X(...) definitions whose base list references a known Node subclass name.

The result feeds the Add Node dialog’s class picker so users can instantiate their own classes alongside engine built-ins. We deliberately do not import the project files – the dialog only needs the class name, module path, and file location for display + lazy resolution at instantiation time.

Cache invalidation is mtime-based: each call to :meth:ProjectClassIndex.refresh re-stats every .py file under the configured root and re-parses any whose mtime changed since the last scan. Files with syntax errors are skipped silently (the parser still returns a tree thanks to error recovery, but we treat any reported error as “skip and try again next refresh”).

Module Contents

Classes

ProjectClass

A user-defined Node subclass discovered by walking project source files.

ProjectClassIndex

Mtime-cached index of user-defined Node subclasses under a project root.

RenameResult

Outcome of a project-wide :func:rename_class operation.

Functions

rename_class

Rename a class everywhere it appears in the project.

Data

log

API

simvx.editor.project_classes.log

‘getLogger(…)’

class simvx.editor.project_classes.ProjectClass[source]

A user-defined Node subclass discovered by walking project source files.

module_path is the dotted module name relative to the configured root (e.g. player.attack). file_path is the absolute filesystem path, kept so the editor can open the source on demand.

name: str

None

module_path: str

None

file_path: pathlib.Path

None

bases: tuple[str, ...]

None

property display_subtitle: str[source]

Secondary label shown under the class name in the picker.

class simvx.editor.project_classes.ProjectClassIndex(project_path: pathlib.Path | None = None, *, src_subdir: str = 'src')[source]

Mtime-cached index of user-defined Node subclasses under a project root.

Construct once per project session; call :meth:refresh whenever the picker is about to be shown. refresh is cheap when nothing changed (one stat per .py file plus a dict lookup); a full re-scan only happens for files whose mtime advanced.

Initialization

property project_path: pathlib.Path | None[source]
set_project_path(project_path: pathlib.Path | None) None[source]

Switch the index to a new project; clears the cache.

set_src_subdir(subdir: str) None[source]

Configure the scanned subdirectory (default src).

property root: pathlib.Path | None[source]

Absolute path of the directory walked for .py files.

refresh() list[simvx.editor.project_classes.ProjectClass][source]

Rescan the project root, re-parsing only files with changed mtime.

Returns the full list of discovered classes (sorted by name).

all() list[simvx.editor.project_classes.ProjectClass][source]

Return the cached list without rescanning.

iter_use_sites(class_name: str) collections.abc.Iterator[tuple[pathlib.Path, simvx.core.scene_io.UseSiteRef]][source]

Yield (file_path, use_site) pairs for every reference to class_name.

Walks every project file (re-scanning the cache first) and runs

Func:

simvx.core.scene_io.find_class_uses against each parsed source. Files with parse errors are skipped silently — the next

Meth:

refresh will re-attempt them.

resolve(project_class: simvx.editor.project_classes.ProjectClass) type[simvx.core.Node] | None[source]

Import the project module and return the class object, or None.

The picker stores ProjectClass records (name + module path) so the editor can display the list without executing user code. When the user actually picks an entry, the editor calls this to import the module and resolve the live class. Failures are logged and return None – callers should surface a user-visible error in that case.

class simvx.editor.project_classes.RenameResult[source]

Outcome of a project-wide :func:rename_class operation.

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

None

file_renamed: tuple[pathlib.Path, pathlib.Path] | None

None

simvx.editor.project_classes.rename_class(project_index: simvx.editor.project_classes.ProjectClassIndex, old_name: str, new_name: str, *, rename_file: bool = False) simvx.editor.project_classes.RenameResult[source]

Rename a class everywhere it appears in the project.

Updates the class definition + every importer + every instantiation site by parsing each affected file, running

Func:

simvx.core.scene_io.rename_class_in_source against it, and writing the result back. Two-phase: collect every new-source mapping in memory first; only write to disk after every file successfully rewrites. If a write fails partway, restore each already-written file from its in-memory snapshot.

When rename_file=True and the definition lives in a file whose stem matches the old class’s snake_case form (or in a folder whose name matches), also rename the file/folder to match the new class. Importers in other files are updated by virtue of the same cross-file rewrite.

Returns the set of files modified and (optionally) the file rename. Raises :class:ValueError if old_name has no definition in the project, or if new_name already names another project class.