diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index c0b96c5..45e10cd 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -94,7 +94,9 @@ jobs: python3 -m venv .venv source .venv/bin/activate pip install reclass-rs --find-links dist --force-reinstall - pip install pytest kapicorp-reclass + pip install pytest mypy kapicorp-reclass + stubtest reclass_rs + mypy --check-untyped-defs --ignore-missing-imports tests/ pytest - name: pytest if: ${{ !startsWith(matrix.platform.target, 'x86') && matrix.platform.target != 'ppc64' }} diff --git a/pyproject.toml b/pyproject.toml index ae5a199..f210a0c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,4 +14,5 @@ dynamic = ["version"] [tool.maturin] +python-source = "python" features = ["pyo3/extension-module"] diff --git a/python/reclass_rs/__init__.py b/python/reclass_rs/__init__.py new file mode 100644 index 0000000..7ea0d79 --- /dev/null +++ b/python/reclass_rs/__init__.py @@ -0,0 +1,5 @@ +from .reclass_rs import * + +__doc__ = reclass_rs.__doc__ +if hasattr(reclass_rs, "__all__"): + __all__ = reclass_rs.__all__ diff --git a/python/reclass_rs/__init__.pyi b/python/reclass_rs/__init__.pyi new file mode 120000 index 0000000..3d91bfd --- /dev/null +++ b/python/reclass_rs/__init__.pyi @@ -0,0 +1 @@ +reclass_rs.pyi \ No newline at end of file diff --git a/python/reclass_rs/py.typed b/python/reclass_rs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/python/reclass_rs/reclass_rs.pyi b/python/reclass_rs/reclass_rs.pyi new file mode 100644 index 0000000..dd0de04 --- /dev/null +++ b/python/reclass_rs/reclass_rs.pyi @@ -0,0 +1,116 @@ +from enum import Enum +from typing import Any, Optional, final +from datetime import datetime + +__all__: list[str] = [ + "CompatFlag", + "Config", + "Inventory", + "NodeInfo", + "NodeInfoMeta", + "Reclass", + "buildinfo", +] + +@final +class CompatFlag(Enum): + ComposeNodeNameLiteralDots = "ComposeNodeNameLiteralDots" + +@final +class Config: + @property + def class_mappings(self) -> list[str]: ... + @property + def class_mappings_match_path(self) -> bool: ... + @property + def classes_path(self) -> str: ... + @property + def compatflags(self) -> set[CompatFlag]: ... + @property + def compose_node_name(self) -> bool: ... + @classmethod + def from_dict( + cls, inventory_path: str, config: dict[str, Any], verbose: bool = False + ) -> Config: ... + @property + def ignore_class_notfound(self) -> bool: ... + @property + def ignore_class_notfound_regexp(self) -> list[str]: ... + @property + def ignore_overwritten_missing_references(self) -> bool: ... + @property + def inventory_path(self) -> str: ... + @property + def nodes_path(self) -> str: ... + @property + def verbose_warnings(self) -> bool: ... + +@final +class NodeInfoMeta: + @property + def environment(self) -> str: ... + @property + def name(self) -> str: ... + @property + def node(self) -> str: ... + @property + def render_time(self) -> datetime: ... + @property + def uri(self) -> str: ... + +@final +class NodeInfo: + @property + def __reclass__(self) -> NodeInfoMeta: ... + @property + def applications(self) -> list[str]: ... + def as_dict(self) -> dict[str, Any]: ... + @property + def classes(self) -> list[str]: ... + @property + def exports(self) -> dict[str, Any]: ... + @property + def parameters(self) -> dict[str, Any]: ... + def reclass_as_dict(self) -> dict[str, Any]: ... + +@final +class Inventory: + @property + def applications(self) -> dict[str, list[str]]: ... + def as_dict(self) -> dict[str, Any]: ... + @property + def classes(self) -> dict[str, list[str]]: ... + @property + def nodes(self) -> dict[str, NodeInfo]: ... + +@final +class Reclass: + @property + def config(self) -> Config: ... + def __new__( + cls, + inventory_path: Optional[str] = ".", + nodes_path: Optional[str] = None, + classes_path: Optional[str] = None, + ignore_class_notfound: Optional[bool] = None, + ): ... + @classmethod + def from_config_file( + cls, inventory_path: str, config_file: str, verbose: bool = False + ) -> Reclass: ... + @classmethod + def from_config(cls, config: Config) -> Reclass: ... + def nodeinfo(self, nodename: str) -> NodeInfo: ... + def inventory(self) -> Inventory: ... + @classmethod + def set_thread_count(cls, count: int): ... + def set_compat_flag(self, flag: CompatFlag): ... + def unset_compat_flag(self, flag: CompatFlag): ... + def clear_compat_flags(self): ... + @property + def nodes(self) -> dict[str, str]: ... + @property + def classes(self) -> dict[str, str]: ... + def set_ignore_class_notfound_regexp(self, patterns: list[str]): ... + +def buildinfo() -> dict[str, str]: ... diff --git a/src/lib.rs b/src/lib.rs index 40777f5..5275c21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -413,20 +413,18 @@ fn buildinfo() -> HashMap<&'static str, &'static str> { } #[pymodule] -fn reclass_rs(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { - // Register the top-level `Reclass` Python class which is used to configure the library - m.add_class::()?; - // Register the `Config` class and `CompatFlag` enum - m.add_class::()?; - m.add_class::()?; - // Register the NodeInfoMeta and NodeInfo classes - m.add_class::()?; - m.add_class::()?; - // Register the Inventory class - m.add_class::()?; - // Register the buildinfo method - m.add_function(wrap_pyfunction!(buildinfo, m)?)?; - Ok(()) +mod reclass_rs { + #[pymodule_export] + use super::Reclass; + #[pymodule_export] + use super::buildinfo; + + #[pymodule_export] + use crate::config::{CompatFlag, Config}; + #[pymodule_export] + use crate::inventory::Inventory; + #[pymodule_export] + use crate::node::{NodeInfo, NodeInfoMeta}; } #[cfg(test)]