nativegate 0.1.3__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. {nativegate-0.1.3 → nativegate-0.1.5}/PKG-INFO +1 -1
  2. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/config.py +20 -0
  3. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/dialect.py +9 -1
  4. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/PKG-INFO +1 -1
  5. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/SOURCES.txt +1 -0
  6. {nativegate-0.1.3 → nativegate-0.1.5}/pyproject.toml +1 -1
  7. nativegate-0.1.5/tests/test_config_unknown_keys.py +77 -0
  8. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_dialect.py +32 -0
  9. {nativegate-0.1.3 → nativegate-0.1.5}/README.md +0 -0
  10. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/__init__.py +0 -0
  11. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/__main__.py +0 -0
  12. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/buildinfo.py +0 -0
  13. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/cli.py +0 -0
  14. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/declared_invariants.py +0 -0
  15. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/discovery.py +0 -0
  16. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/driverbuild.py +0 -0
  17. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/drivers/__init__.py +0 -0
  18. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/drivers/cpp.py +0 -0
  19. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/drivers/fortran.py +0 -0
  20. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/__init__.py +0 -0
  21. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/cmake_gen.py +0 -0
  22. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/docker_gen.py +0 -0
  23. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/error_gen.py +0 -0
  24. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/f2py_gen.py +0 -0
  25. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/gateway_gen.py +0 -0
  26. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/golden_gen.py +0 -0
  27. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/k8s_gen.py +0 -0
  28. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/mcp_gen.py +0 -0
  29. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/middleware_gen.py +0 -0
  30. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/pybind_gen.py +0 -0
  31. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/pyproject_gen.py +0 -0
  32. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/python_pkg_gen.py +0 -0
  33. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/generators/test_gen.py +0 -0
  34. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/golden.py +0 -0
  35. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/invariants.py +0 -0
  36. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/ir.py +0 -0
  37. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/lattice.py +0 -0
  38. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/locking.py +0 -0
  39. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/oracle.py +0 -0
  40. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/__init__.py +0 -0
  41. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/cpp.py +0 -0
  42. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/cpp_ast.py +0 -0
  43. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/cpp_regex.py +0 -0
  44. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/fixed_form.py +0 -0
  45. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/fortran.py +0 -0
  46. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/fortran_fparser.py +0 -0
  47. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/parsers/fortran_regex.py +0 -0
  48. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/preprocess.py +0 -0
  49. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/structural_invariants.py +0 -0
  50. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/suggest.py +0 -0
  51. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/templates/__init__.py +0 -0
  52. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/templates/golden_test_template.py +0 -0
  53. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate/wire.py +0 -0
  54. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/dependency_links.txt +0 -0
  55. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/entry_points.txt +0 -0
  56. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/requires.txt +0 -0
  57. {nativegate-0.1.3 → nativegate-0.1.5}/nativegate.egg-info/top_level.txt +0 -0
  58. {nativegate-0.1.3 → nativegate-0.1.5}/setup.cfg +0 -0
  59. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_buildinfo.py +0 -0
  60. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cli_serve_verify.py +0 -0
  61. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cli_suggest.py +0 -0
  62. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cli_verify_aggregate.py +0 -0
  63. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cmake_gen.py +0 -0
  64. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cpp_ast.py +0 -0
  65. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cpp_driver.py +0 -0
  66. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_cpp_pipeline.py +0 -0
  67. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_declared_invariants.py +0 -0
  68. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_derived_types.py +0 -0
  69. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_docker_gen.py +0 -0
  70. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_driverbuild.py +0 -0
  71. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_endpoint_docstrings.py +0 -0
  72. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fixed_form.py +0 -0
  73. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fortran_doc_comments.py +0 -0
  74. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fortran_driver.py +0 -0
  75. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fortran_fparser.py +0 -0
  76. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fortran_libraries.py +0 -0
  77. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_fortran_pipeline.py +0 -0
  78. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_gateway_gen.py +0 -0
  79. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_generated_python_is_valid.py +0 -0
  80. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_golden.py +0 -0
  81. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_invariants.py +0 -0
  82. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_ir_contract.py +0 -0
  83. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_k8s_gen.py +0 -0
  84. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_lattice.py +0 -0
  85. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_legacy_cpp.py +0 -0
  86. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_legacy_fortran.py +0 -0
  87. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_locking.py +0 -0
  88. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_mcp_gen.py +0 -0
  89. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_middleware_gen.py +0 -0
  90. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_multifile_cpp.py +0 -0
  91. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_native_oracle.py +0 -0
  92. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_native_state_contract.py +0 -0
  93. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_oracle.py +0 -0
  94. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_preprocess_and_config.py +0 -0
  95. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_preprocessed_fortran.py +0 -0
  96. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_pybind_gen.py +0 -0
  97. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_pyproject_gen.py +0 -0
  98. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_quickstart.py +0 -0
  99. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_scalar_ref.py +0 -0
  100. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_service_gen.py +0 -0
  101. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_service_name_validation.py +0 -0
  102. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_shared_libraries.py +0 -0
  103. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_structural_invariants.py +0 -0
  104. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_suggest.py +0 -0
  105. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_verification_config.py +0 -0
  106. {nativegate-0.1.3 → nativegate-0.1.5}/tests/test_wire.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nativegate
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Make legacy C++/C/Fortran AI-ready and microservice-ready: expose native code as an MCP server, REST API and container
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -805,6 +805,26 @@ class ServiceConfig:
805
805
  "Run `ngate create-service` first."
806
806
  )
807
807
  data = yaml.safe_load(config_path.read_text()) or {}
808
+ # Unknown top-level keys are an error, not dead weight. A new
809
+ # nativegate.yaml key read by an older nativegate (the exact case
810
+ # this project shipped CI against: `dialect: cd` under a nativegate
811
+ # that pre-dated it) would otherwise vanish the same way a letter
812
+ # transposed in `dialete:` would — the service generates from an
813
+ # assumption the user never wrote.
814
+ known_keys = {
815
+ "name", "language", "expose", "parser", "clang", "libraries",
816
+ "include_paths", "api", "dialect",
817
+ "fortran", # namespace for fortran.defines
818
+ # verification layer 3 keys
819
+ "state", "invariants", "ranges", "lattice",
820
+ }
821
+ unknown = sorted(set(data) - known_keys)
822
+ if unknown:
823
+ raise ConfigError(
824
+ f"{config_path}: unknown key(s) {', '.join(unknown)}. "
825
+ "Check the spelling against this nativegate version's supported "
826
+ "keys, or upgrade nativegate if the key is new."
827
+ )
808
828
  expose = _load_expose(data.get("expose"), config_path)
809
829
  clang_data = data.get("clang") or {}
810
830
  # `None` means "no restriction": `expose: all` or an empty `expose:`
@@ -41,7 +41,15 @@ import re
41
41
 
42
42
  DIALECT_CHOICES = ("cs", "cd")
43
43
 
44
- _PREFIX_RE = re.compile(r"^(CS|CD)(?=[ \t])", re.IGNORECASE | re.MULTILINE)
44
+ # Statement labels are dialect-marked too ("CD900 DGAMMA = RES"), so no
45
+ # space lookahead: any line starting with CS/CD is prefix-marked, with a
46
+ # following space, tab, digit or line end allowed out to avoid tripping
47
+ # on prose comments like "CSIMPLER". A live statement never starts with
48
+ # literal letters in column 1 — the statement field begins in column 7 —
49
+ # so there is no legitimate Fortran that begins "CS"/"CD" here. A comment
50
+ # that merely starts with those letters is, at worst, re-marked; the
51
+ # only real hazard would be more labels lost to a stricter pattern.
52
+ _PREFIX_RE = re.compile(r"^(CS|CD)(?=[ \t\n]|\d|$)", re.IGNORECASE | re.MULTILINE)
45
53
 
46
54
 
47
55
  def resolve_dialect(raw: str | None) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nativegate
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Make legacy C++/C/Fortran AI-ready and microservice-ready: expose native code as an MCP server, REST API and container
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -57,6 +57,7 @@ tests/test_cli_serve_verify.py
57
57
  tests/test_cli_suggest.py
58
58
  tests/test_cli_verify_aggregate.py
59
59
  tests/test_cmake_gen.py
60
+ tests/test_config_unknown_keys.py
60
61
  tests/test_cpp_ast.py
61
62
  tests/test_cpp_driver.py
62
63
  tests/test_cpp_pipeline.py
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
13
13
 
14
14
  [project]
15
15
  name = "nativegate"
16
- version = "0.1.3"
16
+ version = "0.1.5"
17
17
  description = "Make legacy C++/C/Fortran AI-ready and microservice-ready: expose native code as an MCP server, REST API and container"
18
18
  readme = "README.md"
19
19
  requires-python = ">=3.10"
@@ -0,0 +1,77 @@
1
+ """Unknown top-level keys in nativegate.yaml must fail loading.
2
+
3
+ Found in the wild (2026-09-23): specfun-py's CI installed a nativegate
4
+ that pre-dated the `dialect:` key and generated the service anyway from
5
+ an assumption never written down — the unknown key was swallowed as dead
6
+ weight. A typo in any key was equally silent. Now both are config errors
7
+ that say what is wrong and what to do about it.
8
+ """
9
+
10
+ import shutil
11
+ import textwrap
12
+
13
+ import pytest
14
+
15
+ from nativegate.config import ConfigError, ServiceConfig
16
+
17
+
18
+ @pytest.fixture()
19
+ def scratch(tmp_path, monkeypatch):
20
+ service = tmp_path / "demo"
21
+ (service / "native").mkdir(parents=True)
22
+ monkeypatch.chdir(tmp_path)
23
+ return service
24
+
25
+
26
+ def _config_for_lines(text):
27
+ def _load(service):
28
+ (service / "nativegate.yaml").write_text(text)
29
+ return ServiceConfig.load(service)
30
+ return _load
31
+
32
+
33
+ def test_unknown_key_is_an_error(scratch):
34
+ (scratch / "nativegate.yaml").write_text(textwrap.dedent("""\
35
+ name: demo
36
+ language: fortran
37
+ diatect: cd
38
+ expose:
39
+ functions: []
40
+ """))
41
+ with pytest.raises(ConfigError) as exc:
42
+ ServiceConfig.load(scratch)
43
+ assert "diatect" in str(exc.value)
44
+ assert "upgrade nativegate" in str(exc.value)
45
+
46
+
47
+ def test_future_key_is_a_config_error_not_a_silent_noop(scratch):
48
+ (scratch / "nativegate.yaml").write_text(textwrap.dedent("""\
49
+ name: demo
50
+ language: fortran
51
+ dialect: cd
52
+ expose:
53
+ functions: []
54
+ """))
55
+ # fine on a nativegate that knows the key
56
+ config = ServiceConfig.load(scratch)
57
+ assert config.dialect == "cd"
58
+
59
+
60
+ def test_all_declared_keys_still_load(scratch):
61
+ (scratch / "nativegate.yaml").write_text(textwrap.dedent("""\
62
+ name: demo
63
+ language: fortran
64
+ dialect: cd
65
+ parser: auto
66
+ libraries:
67
+ - petro
68
+ include_paths: []
69
+ fortran:
70
+ defines: []
71
+ expose:
72
+ all: true
73
+ """))
74
+ config = ServiceConfig.load(scratch)
75
+ assert config.dialect == "cd"
76
+ assert config.libraries == ["petro"]
77
+ assert config.expose.all is True
@@ -45,10 +45,42 @@ CD DOUBLE PRECISION
45
45
  1 C,CONV,EPS
46
46
  DGAMMA = 24.0D0
47
47
  RETURN
48
+ C the labelled assignment is dialect-marked as well: label in cols 1-5,
49
+ C prefix occupying cols 1-2, so the 9 lands in column 3 — still inside
50
+ C the label field (columns 1-5) after two blanks are substituted.
51
+ 900 DGAMMA = 24.0D0
52
+ RETURN
53
+ END
54
+ """)
55
+
56
+ DUAL_WITH_MARKED_LABEL = textwrap.dedent("""\
57
+ CD DOUBLE PRECISION FUNCTION DGAMMA(X)
58
+ CS REAL FUNCTION GAMMA(X)
59
+ GO TO 900
60
+ CD900 DGAMMA = 24.0D0
61
+ CS900 GAMMA = 24.0E0
62
+ RETURN
63
+ 900 CONTINUE
64
+ RETURN
48
65
  END
49
66
  """)
50
67
 
51
68
 
69
+ def test_apply_dialect_marks_statement_labels_too():
70
+ """`CD900 DGAMMA = ...` is the line that exposed the strict space-lookahead
71
+ regex: labels start at column 1 and the prefix replaces its first two
72
+ characters, so the following character is a digit, not a space. A
73
+ space-lookahead regex would leave the label line inert and the resolved
74
+ file compiles with "label 900 referenced but never defined"."""
75
+ out = apply_dialect(DUAL_WITH_MARKED_LABEL, "cd")
76
+ lines = out.splitlines()
77
+ # label survives in the label field (digits starting column 3 is legal)
78
+ assert " 900 DGAMMA = 24.0D0" in lines
79
+ # the CS half is commented, label included
80
+ assert "C 900 GAMMA = 24.0E0" in lines
81
+ assert "C REAL FUNCTION GAMMA(X)" in lines
82
+
83
+
52
84
  def test_apply_dialect_selects_cd():
53
85
  out = apply_dialect(DUAL_DIALECT, "cd")
54
86
  lines = out.splitlines()
File without changes
File without changes
File without changes
File without changes