"""Read project metadata from pyproject.toml for packaging scripts.""" from __future__ import annotations import importlib import subprocess import sys from pathlib import Path def _load_toml(text: str) -> dict: if sys.version_info >= (3, 11): import tomllib return tomllib.loads(text) try: import tomli except ImportError: subprocess.check_call([sys.executable, "-m", "pip", "install", "tomli", "-q"]) import tomli return tomli.loads(text) def load_pyproject(repo_root: Path) -> dict: path = repo_root / "pyproject.toml" return _load_toml(path.read_text(encoding="utf-8")) def project_metadata(repo_root: Path) -> dict: return load_pyproject(repo_root)["project"] def install_requires(repo_root: Path) -> list[str]: return list(project_metadata(repo_root)["dependencies"]) def extras_require(repo_root: Path) -> dict[str, list[str]]: optional = project_metadata(repo_root).get("optional-dependencies", {}) return {name: list(requirements) for name, requirements in optional.items()} def _resolve_version_attr(repo_root: Path, attr: str) -> str: """Resolve a setuptools dynamic version attr (e.g. orisys.__version__).""" src_dir = str(repo_root / "src") if src_dir not in sys.path: sys.path.insert(0, src_dir) module_name, _, attr_name = attr.partition(".") if not attr_name: raise RuntimeError(f"Invalid dynamic version attr: {attr!r}") module = importlib.import_module(module_name) value = module for name in attr_name.split("."): value = getattr(value, name) return str(value) def get_version(repo_root: Path) -> str: """Return project version from pyproject.toml (static or dynamic).""" project = project_metadata(repo_root) if "version" in project: return str(project["version"]) dynamic = load_pyproject(repo_root).get("tool", {}).get("setuptools", {}).get("dynamic", {}) version_cfg = dynamic.get("version") if isinstance(version_cfg, dict) and "attr" in version_cfg: return _resolve_version_attr(repo_root, version_cfg["attr"]) raise RuntimeError("Project version is not defined in pyproject.toml") def bundle_items(repo_root: Path) -> list[dict]: """Return bundle rules from [[tool.orisys.bundle.items]] in pyproject.toml.""" items = load_pyproject(repo_root).get("tool", {}).get("orisys", {}).get("bundle", {}).get("items") if not items: raise RuntimeError("Missing [[tool.orisys.bundle.items]] in pyproject.toml") return list(items) def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]: """Map bundle items marked dev_install=true to setuptools data_files entries.""" from wheel_layout import resolve_bundle_source_files grouped: dict[str, list[str]] = {} for item in bundle_items(repo_root): if not item.get("dev_install", False): continue src_dir = repo_root / item["source"] base_dest = f"orisys/{item['target']}".replace("\\", "/") for src_file in resolve_bundle_source_files(repo_root, item): rel_src = src_file.relative_to(repo_root).as_posix() if item.get("preserve_tree", False): rel_parent = src_file.relative_to(src_dir).parent.as_posix() dest = base_dest if rel_parent == "." else f"{base_dest}/{rel_parent}" else: dest = base_dest grouped.setdefault(dest, []).append(rel_src) return [(dest, sorted(files)) for dest, files in sorted(grouped.items())]