Source code for codegen.naming
import re
from typing import TYPE_CHECKING, Literal
from codegen.context import current_context
_BOUNDARY = re.compile(r"(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z0-9])(?=[A-Z])")
_SEPARATORS = re.compile(r"[./]")
if TYPE_CHECKING:
from codegen.imports import ImportCollector
type _LocationType = Literal["generated", "external"]
[docs]
class Name:
original: str
raw_parts: list[str]
module_parts: list[str]
location: _LocationType
def __init__(
self,
raw: str | None,
location: _LocationType = "generated",
) -> None:
raw = raw or ""
*module_parts, name = _SEPARATORS.split(raw)
self.original = name
words = [w for w in _BOUNDARY.sub("_", name).split("_") if w]
self.module_parts = module_parts
self.raw_parts = words
self.location = location
@staticmethod
def from_module_name(module: str | None, name: str) -> Name:
if module:
return Name(f"{module}.{name}")
return Name(name)
@property
def external(self) -> Name:
self.location = "external"
return self
def __repr__(self) -> str:
return f"Name({self.module_path}: '{self.snake}')"
def __eq__(self, other: object) -> bool:
if not isinstance(other, Name):
return NotImplemented
assert isinstance(other, Name)
return (
self.raw_parts == other.raw_parts
and self.module_parts == other.module_parts
)
def __hash__(self) -> int:
return hash("".join(self.raw_parts) + "".join(self.module_parts))
@property
def prefixed_module_parts(self) -> list[str]:
if self.location == "external":
return self.module_parts
ctx = current_context()
if ctx:
return [ctx.config.package_prefix, *self.module_parts]
return self.module_parts
@property
def raw(self) -> str:
return self.snake
@property
def pascal(self) -> str:
return "".join(part.capitalize() for part in self.raw_parts)
@property
def lower(self) -> str:
return "".join(part.lower() for part in self.raw_parts)
@property
def snake(self) -> str:
return "_".join(part.lower() for part in self.raw_parts)
@property
def slug(self) -> str:
return "-".join(part.lower() for part in self.raw_parts)
@property
def module_path(self) -> str:
# Exclude the prefix, that's automatically included by
# `codegen` when rendering
return f"{'/'.join(self.module_parts)}.py"
@property
def init_path(self) -> str:
# Exclude the prefix, that's automatically included by
# `codegen` when rendering
parts = [*self.module_parts[:-1], "__init__"]
return f"{'/'.join(parts)}.py"
@property
def module_import(self) -> str:
return ".".join(self.prefixed_module_parts)
@property
def complete_path(self) -> str:
return ".".join([*self.prefixed_module_parts, self.original])
def suffixed(self, suffix: str) -> str:
# Deprecated
return f"{self.pascal}{suffix}"
def add_import(self, imports: ImportCollector) -> None:
imports.add_from(self.module_import, self.original)
@staticmethod
def from_dotted(dotted_path: str) -> tuple[str, Name]:
# deprecated
name = Name(dotted_path)
return name.module_import, name
def __bool__(self) -> bool:
return bool(self.raw_parts)
def split_dotted_class(dotted_path: str) -> tuple[str, str]:
# Deprecated
name = Name(dotted_path)
return name.module_import, name.original
[docs]
def prefix_import(prefix: str, *parts: str) -> str:
if prefix:
return ".".join([prefix, *parts])
return ".".join(parts)