2626}
2727
2828
29+ def _prohibited_import_violations (source : str , filename : str ) -> list [str ]:
30+ tree = ast .parse (source , filename = filename )
31+ violations : list [str ] = []
32+
33+ for node in ast .walk (tree ):
34+ if isinstance (node , ast .ImportFrom ) and node .module :
35+ candidate_modules = {node .module }
36+ candidate_modules .update (f"{ node .module } .{ alias .name } " for alias in node .names )
37+ for module_name in candidate_modules :
38+ if module_name in PROHIBITED_DIRECT_IMPORTS :
39+ violations .append (f"{ filename } :{ node .lineno } imports { module_name } " )
40+ break
41+ elif isinstance (node , ast .Import ):
42+ for alias in node .names :
43+ if alias .name in PROHIBITED_DIRECT_IMPORTS :
44+ violations .append (f"{ filename } :{ node .lineno } imports { alias .name } " )
45+ break
46+
47+ return violations
48+
49+
2950def test_runtime_plugin_code_uses_nat_api_facade_for_public_symbols () -> None :
3051 """Public plugin-authoring imports should flow through nvidia_nat_redis._nat_api."""
3152 root = Path (__file__ ).resolve ().parents [1 ]
@@ -36,14 +57,28 @@ def test_runtime_plugin_code_uses_nat_api_facade_for_public_symbols() -> None:
3657 if source_file .name == "_nat_api.py" :
3758 continue
3859
39- tree = ast .parse (source_file .read_text (encoding = "utf-8" ), filename = str (source_file ))
40- for node in ast .walk (tree ):
41- if isinstance (node , ast .ImportFrom ) and node .module in PROHIBITED_DIRECT_IMPORTS :
42- violations .append (f"{ source_file .relative_to (root )} :{ node .lineno } imports { node .module } " )
60+ violations .extend (
61+ _prohibited_import_violations (source_file .read_text (encoding = "utf-8" ), str (source_file .relative_to (root )))
62+ )
4363
4464 assert not violations , "\n " .join (violations )
4565
4666
67+ def test_import_guard_catches_prohibited_import_forms () -> None :
68+ source = """
69+ from nat.builder.builder import Builder
70+ from nat.builder import builder
71+ import nat.builder.builder
72+ import nat.plugins.redis.register
73+ """
74+
75+ assert _prohibited_import_violations (source , "sample.py" ) == [
76+ "sample.py:2 imports nat.builder.builder" ,
77+ "sample.py:3 imports nat.builder.builder" ,
78+ "sample.py:4 imports nat.builder.builder" ,
79+ ]
80+
81+
4782def test_nat_api_facade_exposes_plugin_authoring_symbols () -> None :
4883 from nvidia_nat_redis import _nat_api
4984
0 commit comments