simvx.core.scene_io.symbols

Where a class is defined, where it is used, and how to rename it.

This module is the foundation for cross-file class rename. It is stateless and does no I/O: every function takes the source text of one module and either answers a question about it or returns the rewritten text. Nothing here holds a parse tree, so a caller can hand it text straight off disk and compare what it gets back byte for byte.

Discovery is a walk of the standard library’s own parse tree, so the constructs it understands are the ones the running Python understands. Rewriting never regenerates source: the spans of the identifiers that move are spliced and every other byte of the file is the author’s, which is what makes an idempotent rename byte-identical rather than merely equivalent.

Use-site classification is purely syntactic, with no name resolution: an aliased import does not propagate to its alias, and a plain rebinding of the name in a block suppresses the uses below it in that block.

Module Contents

Classes

ClassDefRef

A top-level class definition discovered in a module’s source.

UseSiteRef

A site where a class name is referenced.

Functions

find_class_definitions

Top-level class definitions of source, in source order.

find_class_uses

Every site in source referencing class_name, classified by kind.

rename_class_in_source

Rename a class throughout source, returning the rewritten text.

rename_module_in_imports

Rewrite from <old_module> import … lines to name new_module instead.

Data

API

simvx.core.scene_io.symbols.__all__

[‘ClassDefRef’, ‘UseSiteRef’, ‘find_class_definitions’, ‘find_class_uses’, ‘rename_class_in_source’,…

simvx.core.scene_io.symbols.UseKind

None

class simvx.core.scene_io.symbols.ClassDefRef[source]

A top-level class definition discovered in a module’s source.

A caller moving the class to another file wants :attr:leading_trivia and

Attr:

text together: the comment an author wrote above a class describes that class and has to travel with it, and it is held separately only so that a caller listing classes is not handed text that is not the class.

name: str

None

bases: tuple[str, ...]

None

line: int

None

text: str

None

leading_trivia: str = <Multiline-String>
class simvx.core.scene_io.symbols.UseSiteRef[source]

A site where a class name is referenced.

Attr:

line and :attr:column locate the identifier itself, in the editor’s convention: the line is 1-indexed and the column counts characters from 0. For an aliased import (from x import Foo as Bar) they locate the source name Foo rather than the alias.

kind: simvx.core.scene_io.symbols.UseKind

None

line: int

None

column: int

None

import_module: str | None

None

import_alias: str | None

None

simvx.core.scene_io.symbols.find_class_definitions(source: str) → list[simvx.core.scene_io.symbols.ClassDefRef][source]

Top-level class definitions of source, in source order.

Classes nested inside functions (or other classes) are not returned: they are not project-rename candidates. Raises :class:SceneSyntaxError when source does not parse.

simvx.core.scene_io.symbols.find_class_uses(source: str, class_name: str) → list[simvx.core.scene_io.symbols.UseSiteRef][source]

Every site in source referencing class_name, classified by kind.

Ordered by position. Scope-aware: a plain <class_name> = … rebinding shadows the uses written below it in the same block, and a function or class body starts again unshadowed because a name bound outside it is a different binding from one bound inside. Conservative everywhere else: a site whose meaning depends on what a name resolves to at runtime is included.

Raises :class:SceneSyntaxError when source does not parse.

simvx.core.scene_io.symbols.rename_class_in_source(source: str, old_name: str, new_name: str) → str[source]

Rename a class throughout source, returning the rewritten text.

Rewrites the name of every top-level class <old_name> definition and every site :func:find_class_uses reports, except that an aliased import keeps its alias: from x import Old as P becomes from x import New as P and the P uses below it are left alone, because they were never references to this class.

Only whole identifiers move. A class name written inside a string or a comment is text rather than a reference and is left as the author wrote it. Idempotent: renaming a class to the name it already has returns source unchanged, byte for byte.

simvx.core.scene_io.symbols.rename_module_in_imports(source: str, old_module: str, new_module: str) → str[source]

Rewrite from <old_module> import … lines to name new_module instead.

Used when the user renames a class’s defining file (or folder) and every importer’s module path has to follow. old_module and new_module are dotted names, spelled as they would be in an absolute import ("src.player", "src.hero").

Matches both:

  • Exact matches: from src.player import X when old_module is "src.player".

  • Trailing-segment matches: from .player import X and from foo.bar.player import X when the import’s last component is the last component of old_module. This is what makes a relative import follow (.player shares its leaf with src.player although the prefix differs), and anchoring on the trailing segment is what keeps an unrelated from os.path import X out of it.

Idempotent, and returns source unchanged when nothing matches. A plain import x binds the module itself rather than a name from it, and rewriting it would need the uses of that binding to follow; it is left alone here and would warrant a primitive of its own.