Skip to content

Commit e92b3b8

Browse files
Merge pull request #98 from CDCgov/rr-68-type-hints
update to CatalogNamespace
2 parents 843696e + 6a85eb7 commit e92b3b8

15 files changed

Lines changed: 853 additions & 11 deletions

File tree

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
recursive-include cfa/dataops/reporting/reports
22
recursive-include cfa/dataops/etl/transform_templates
33
include cfa/dataops/config.ini
4+
include cfa/dataops/py.typed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ This project provides data tools and low friction access to versioned datasets w
3333
df = datacat.{{dataset}}.load.get_dataframe()
3434
df.glimpse()
3535
```
36+
6. Generate project-local type stubs for the installed catalog:
37+
```bash
38+
dataops_catalog_stubs
39+
```
40+
41+
If using uv, run:
42+
```bash
43+
uv run dataops_catalog_stubs
44+
```
45+
46+
This writes stubs under `typings/`, which Pyright/Pylance uses by default.
47+
For mypy, include that directory with `MYPYPATH=typings`.
3648

3749
Read the [Dataset User Guide](docs/data_user_guide.md) for more information about accessing datasets.
3850

cfa/dataops/catalog.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ def get_all_catalogs() -> list:
8484
report_namespaces = get_dataset_dot_path(all_reports_ns_map)
8585

8686

87+
class CatalogNamespace(SimpleNamespace):
88+
"""Runtime namespace wrapper for catalog access."""
89+
90+
8791
class DatasetEndpoint:
8892
"""The DatasetEndpoint class for including in the datacat namespace.
8993
This ends the namespace branching at a config file and creates all the
@@ -736,7 +740,7 @@ def save_dir_to_blob(
736740
)
737741

738742

739-
def dict_to_sn(d: Any, defaults: dict = None, ns: str = "") -> SimpleNamespace:
743+
def dict_to_sn(d: Any, defaults: dict | None = None, ns: str = "") -> CatalogNamespace:
740744
"""Simple recursive namespace construction
741745
742746
Args:
@@ -745,9 +749,9 @@ def dict_to_sn(d: Any, defaults: dict = None, ns: str = "") -> SimpleNamespace:
745749
ns (str, optional): the current namespace path. Defaults to ''.
746750
747751
Returns:
748-
SimpleNamespace: namespace representation
752+
CatalogNamespace: namespace representation
749753
"""
750-
x = SimpleNamespace()
754+
x = CatalogNamespace()
751755
ns_prefix = f"{ns}." if ns != "" else ""
752756
_ = [
753757
setattr(
@@ -788,13 +792,13 @@ def dict_to_sn(d: Any, defaults: dict = None, ns: str = "") -> SimpleNamespace:
788792
rc.append(report_dict_to_sn({k: all_reports_ns_map[k]}))
789793
combined_reports_dict = {key: value for ns in rc for key, value in vars(ns).items()}
790794

791-
datacat = SimpleNamespace(**combined_dict)
795+
datacat: CatalogNamespace = CatalogNamespace(**combined_dict)
792796
datacat.__setattr__("__namespace_list__", dataset_namespaces)
793-
reportcat = SimpleNamespace(**combined_reports_dict)
797+
reportcat: CatalogNamespace = CatalogNamespace(**combined_reports_dict)
794798
reportcat.__setattr__("__namespace_list__", report_namespaces)
795799

796800

797-
def _attach_schema_mock_functions(datacat: SimpleNamespace, catalogs: list) -> None:
801+
def _attach_schema_mock_functions(datacat: CatalogNamespace, catalogs: list) -> None:
798802
"""Recursively walk the datacat namespace and attach mock_data functions to
799803
the extract and load BlobEndpoints of each DatasetEndpoint, sourced from
800804
a schema module co-located with the dataset.
@@ -816,12 +820,12 @@ def _attach_schema_mock_functions(datacat: SimpleNamespace, catalogs: list) -> N
816820
datacat.<catalog>.<team_path_segments>.<dataset>.load.mock_data()
817821
818822
Args:
819-
datacat (SimpleNamespace): the top-level datacat namespace
823+
datacat (CatalogNamespace): the top-level datacat namespace
820824
catalogs (list): list of (catalog_namespace, catalog_name, catalog_path)
821825
tuples from get_all_catalogs()
822826
"""
823827

824-
def _walk(ns: SimpleNamespace) -> None:
828+
def _walk(ns: CatalogNamespace) -> None:
825829
for val in vars(ns).values():
826830
if isinstance(val, DatasetEndpoint):
827831
# __ns_str__ is e.g. "public.stf.nhsn_hrd_prelim";
@@ -857,7 +861,7 @@ def _walk(ns: SimpleNamespace) -> None:
857861
"mock_data",
858862
getattr(mod, func_name),
859863
)
860-
elif isinstance(val, SimpleNamespace):
864+
elif isinstance(val, CatalogNamespace):
861865
_walk(val)
862866

863867
_walk(datacat)

cfa/dataops/py.typed

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Package resources for generated type stubs."""
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from collections.abc import Sequence
2+
from types import SimpleNamespace
3+
from typing import Any, Literal, overload
4+
5+
import pandas as pd
6+
import polars as pl
7+
8+
from .reporting.catalog import NotebookEndpoint
9+
10+
def get_all_catalogs() -> list[tuple[str, str, str]]: ...
11+
12+
class CatalogNamespace(SimpleNamespace):
13+
pass
14+
15+
class DatasetEndpoint:
16+
config_path: str
17+
defaults: dict[str, Any]
18+
config: dict[str, Any]
19+
__ns_str__: str
20+
_ledger_location: dict[str, Any]
21+
22+
class BlobEndpoint:
23+
account: str
24+
container: str
25+
prefix: str
26+
ledger_location: dict[str, Any]
27+
is_ledger: bool
28+
__ns_str__: str
29+
def write_blob(
30+
self,
31+
file_buffer: bytes | Sequence[bytes],
32+
path_after_prefix: str,
33+
auto_version: bool = False,
34+
append: bool = False,
35+
) -> None: ...
36+
def read_blobs(
37+
self,
38+
version_spec: str | None = None,
39+
selection: Literal["newest", "oldest", "all"] = "newest",
40+
print_version: bool = True,
41+
) -> list[bytes]: ...
42+
def read_csv(self, suffix: str) -> pd.DataFrame: ...
43+
def get_versions(self) -> list[str]: ...
44+
def get_file_ext(
45+
self,
46+
version_spec: str | None = None,
47+
selection: Literal["newest", "oldest", "all"] = "newest",
48+
) -> str: ...
49+
def download_version_to_local(
50+
self,
51+
local_path: str,
52+
version_spec: str | None = None,
53+
force: bool = False,
54+
selection: Literal["newest", "oldest", "all"] = "newest",
55+
) -> bool: ...
56+
@overload
57+
def get_dataframe(
58+
self,
59+
output: Literal["pandas", "pd"] = "pandas",
60+
version_spec: str | None = None,
61+
selection: Literal["newest", "oldest"] = "newest",
62+
) -> pd.DataFrame: ...
63+
@overload
64+
def get_dataframe(
65+
self,
66+
output: Literal["polars", "pl"],
67+
version_spec: str | None = None,
68+
selection: Literal["newest", "oldest"] = "newest",
69+
) -> pl.DataFrame: ...
70+
@overload
71+
def get_dataframe(
72+
self,
73+
output: Literal["pl_lazy", "lazy"],
74+
version_spec: str | None = None,
75+
selection: Literal["newest", "oldest"] = "newest",
76+
) -> pl.LazyFrame: ...
77+
def ledger_entry(self, action: str) -> None: ...
78+
def save_dataframe(
79+
self,
80+
df: pd.DataFrame | pl.DataFrame,
81+
path_after_prefix: str,
82+
file_format: str = "parquet",
83+
auto_version: bool = False,
84+
) -> None: ...
85+
def save_file_to_blob(
86+
self,
87+
file_path: str,
88+
path_after_prefix: str,
89+
auto_version: bool = False,
90+
) -> None: ...
91+
def save_dir_to_blob(
92+
self,
93+
dir_path: str,
94+
path_after_prefix: str,
95+
auto_version: bool = False,
96+
) -> None: ...
97+
98+
def dict_to_sn(
99+
d: Any,
100+
defaults: dict[str, Any] | None = None,
101+
ns: str = "",
102+
) -> CatalogNamespace: ...
103+
104+
% for cls in model.classes:
105+
class ${cls.name}(${cls.base}):
106+
% if cls.attributes:
107+
% for attr in cls.attributes:
108+
${attr.name}: ${attr.type_name}
109+
% endfor
110+
% else:
111+
pass
112+
% endif
113+
114+
% endfor
115+
datacat: DataCatalog
116+
reportcat: ReportCatalog
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .catalog import datacat as datacat, reportcat as reportcat
2+
3+
__version__: str
4+
__all__: list[str]

0 commit comments

Comments
 (0)