nativegate 0.1.0__tar.gz → 0.1.2__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 (103) hide show
  1. {nativegate-0.1.0 → nativegate-0.1.2}/PKG-INFO +8 -1
  2. {nativegate-0.1.0 → nativegate-0.1.2}/README.md +7 -0
  3. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/cli.py +27 -2
  4. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/pyproject_gen.py +14 -2
  5. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/PKG-INFO +8 -1
  6. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/SOURCES.txt +2 -0
  7. {nativegate-0.1.0 → nativegate-0.1.2}/pyproject.toml +1 -1
  8. nativegate-0.1.2/tests/test_pyproject_gen.py +24 -0
  9. nativegate-0.1.2/tests/test_service_name_validation.py +47 -0
  10. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/__init__.py +0 -0
  11. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/__main__.py +0 -0
  12. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/buildinfo.py +0 -0
  13. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/config.py +0 -0
  14. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/declared_invariants.py +0 -0
  15. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/discovery.py +0 -0
  16. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/driverbuild.py +0 -0
  17. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/drivers/__init__.py +0 -0
  18. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/drivers/cpp.py +0 -0
  19. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/drivers/fortran.py +0 -0
  20. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/__init__.py +0 -0
  21. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/cmake_gen.py +0 -0
  22. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/docker_gen.py +0 -0
  23. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/error_gen.py +0 -0
  24. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/f2py_gen.py +0 -0
  25. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/gateway_gen.py +0 -0
  26. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/golden_gen.py +0 -0
  27. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/k8s_gen.py +0 -0
  28. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/mcp_gen.py +0 -0
  29. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/middleware_gen.py +0 -0
  30. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/pybind_gen.py +0 -0
  31. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/python_pkg_gen.py +0 -0
  32. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/generators/test_gen.py +0 -0
  33. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/golden.py +0 -0
  34. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/invariants.py +0 -0
  35. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/ir.py +0 -0
  36. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/lattice.py +0 -0
  37. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/locking.py +0 -0
  38. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/oracle.py +0 -0
  39. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/__init__.py +0 -0
  40. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/cpp.py +0 -0
  41. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/cpp_ast.py +0 -0
  42. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/cpp_regex.py +0 -0
  43. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/fixed_form.py +0 -0
  44. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/fortran.py +0 -0
  45. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/fortran_fparser.py +0 -0
  46. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/parsers/fortran_regex.py +0 -0
  47. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/preprocess.py +0 -0
  48. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/structural_invariants.py +0 -0
  49. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/suggest.py +0 -0
  50. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/templates/__init__.py +0 -0
  51. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/templates/golden_test_template.py +0 -0
  52. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate/wire.py +0 -0
  53. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/dependency_links.txt +0 -0
  54. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/entry_points.txt +0 -0
  55. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/requires.txt +0 -0
  56. {nativegate-0.1.0 → nativegate-0.1.2}/nativegate.egg-info/top_level.txt +0 -0
  57. {nativegate-0.1.0 → nativegate-0.1.2}/setup.cfg +0 -0
  58. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_buildinfo.py +0 -0
  59. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cli_serve_verify.py +0 -0
  60. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cli_suggest.py +0 -0
  61. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cli_verify_aggregate.py +0 -0
  62. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cmake_gen.py +0 -0
  63. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cpp_ast.py +0 -0
  64. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cpp_driver.py +0 -0
  65. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_cpp_pipeline.py +0 -0
  66. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_declared_invariants.py +0 -0
  67. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_derived_types.py +0 -0
  68. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_docker_gen.py +0 -0
  69. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_driverbuild.py +0 -0
  70. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_endpoint_docstrings.py +0 -0
  71. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fixed_form.py +0 -0
  72. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fortran_doc_comments.py +0 -0
  73. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fortran_driver.py +0 -0
  74. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fortran_fparser.py +0 -0
  75. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fortran_libraries.py +0 -0
  76. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_fortran_pipeline.py +0 -0
  77. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_gateway_gen.py +0 -0
  78. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_generated_python_is_valid.py +0 -0
  79. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_golden.py +0 -0
  80. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_invariants.py +0 -0
  81. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_ir_contract.py +0 -0
  82. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_k8s_gen.py +0 -0
  83. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_lattice.py +0 -0
  84. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_legacy_cpp.py +0 -0
  85. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_legacy_fortran.py +0 -0
  86. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_locking.py +0 -0
  87. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_mcp_gen.py +0 -0
  88. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_middleware_gen.py +0 -0
  89. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_multifile_cpp.py +0 -0
  90. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_native_oracle.py +0 -0
  91. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_native_state_contract.py +0 -0
  92. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_oracle.py +0 -0
  93. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_preprocess_and_config.py +0 -0
  94. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_preprocessed_fortran.py +0 -0
  95. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_pybind_gen.py +0 -0
  96. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_quickstart.py +0 -0
  97. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_scalar_ref.py +0 -0
  98. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_service_gen.py +0 -0
  99. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_shared_libraries.py +0 -0
  100. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_structural_invariants.py +0 -0
  101. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_suggest.py +0 -0
  102. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_verification_config.py +0 -0
  103. {nativegate-0.1.0 → nativegate-0.1.2}/tests/test_wire.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nativegate
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -33,6 +33,11 @@ Requires-Dist: anyio>=4.0; extra == "test"
33
33
 
34
34
  # nativegate — make legacy C++ and Fortran AI-ready and microservice-ready
35
35
 
36
+ [![CI](https://github.com/ravikings/nativegate/actions/workflows/ci.yml/badge.svg)](https://github.com/ravikings/nativegate/actions/workflows/ci.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/nativegate)](https://pypi.org/project/nativegate/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/nativegate)](https://pypi.org/project/nativegate/)
39
+ [![MCP server (Glama)](https://glama.ai/mcp/servers/ravikings/nativegate/badges/score.svg)](https://glama.ai/mcp/servers/ravikings/nativegate)
40
+
36
41
  Expose existing C++ and Fortran code to Python as deployable microservices —
37
42
  without hand-writing bindings, and without touching the numerics.
38
43
 
@@ -41,6 +46,8 @@ bindings, the CMake build, an installable Python package, a FastAPI service,
41
46
  smoke tests, a numerical regression baseline, and a Dockerfile.
42
47
 
43
48
  ```bash
49
+ pip install "nativegate[clang,build]" # from PyPI; needs cmake/ninja + a C/C++/Fortran toolchain for the build step
50
+
44
51
  ngate quickstart libraries/geometry/geometry.hpp --name demo --build
45
52
  pip install services/demo/dist/*.whl
46
53
  ```
@@ -1,5 +1,10 @@
1
1
  # nativegate — make legacy C++ and Fortran AI-ready and microservice-ready
2
2
 
3
+ [![CI](https://github.com/ravikings/nativegate/actions/workflows/ci.yml/badge.svg)](https://github.com/ravikings/nativegate/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/nativegate)](https://pypi.org/project/nativegate/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/nativegate)](https://pypi.org/project/nativegate/)
6
+ [![MCP server (Glama)](https://glama.ai/mcp/servers/ravikings/nativegate/badges/score.svg)](https://glama.ai/mcp/servers/ravikings/nativegate)
7
+
3
8
  Expose existing C++ and Fortran code to Python as deployable microservices —
4
9
  without hand-writing bindings, and without touching the numerics.
5
10
 
@@ -8,6 +13,8 @@ bindings, the CMake build, an installable Python package, a FastAPI service,
8
13
  smoke tests, a numerical regression baseline, and a Dockerfile.
9
14
 
10
15
  ```bash
16
+ pip install "nativegate[clang,build]" # from PyPI; needs cmake/ninja + a C/C++/Fortran toolchain for the build step
17
+
11
18
  ngate quickstart libraries/geometry/geometry.hpp --name demo --build
12
19
  pip install services/demo/dist/*.whl
13
20
  ```
@@ -60,6 +60,7 @@ from .ir import (
60
60
  IRSchemaError,
61
61
  ModuleIR,
62
62
  NativeTypeError,
63
+ is_valid_python_name,
63
64
  module_from_dict,
64
65
  module_to_dict,
65
66
  validate as validate_ir,
@@ -1241,6 +1242,22 @@ def clean(name: str) -> None:
1241
1242
  def _scaffold_service(name: str, language: str, force: bool) -> Path:
1242
1243
  import shutil
1243
1244
 
1245
+ # Reject before anything is written: the name becomes a Python package
1246
+ # (services/<name>/python/<name>/), so `specfun-gamma` would fail at
1247
+ # generate time with `from ._native.specfun-gamma import ...` — invalid
1248
+ # syntax caught by the generated-file compile() gate, three commands
1249
+ # and a wasted scaffold after the user typed the name. Saying so here
1250
+ # costs nothing (DEFECTS D9, found on first contact with netlib
1251
+ # specfun, where quickstart defaults the name to the file stem and
1252
+ # `specfun-gamma` is the obvious thing to type).
1253
+ if not is_valid_python_name(name):
1254
+ raise click.ClickException(
1255
+ f"Service name '{name}' is not usable: it becomes a Python package, "
1256
+ "so it must be a valid identifier (letters, digits, underscores, "
1257
+ "not starting with a digit, not a Python keyword). "
1258
+ "Use underscores instead of dashes."
1259
+ )
1260
+
1244
1261
  service_dir = Path(SERVICES_DIR) / name
1245
1262
  if service_dir.exists():
1246
1263
  if not force:
@@ -1259,7 +1276,11 @@ def _scaffold_service(name: str, language: str, force: bool) -> Path:
1259
1276
  (service_dir / "python" / name / "__init__.py").write_text(
1260
1277
  "# Run `ngate expose` then `ngate generate` to populate this package.\n"
1261
1278
  )
1262
- (service_dir / "pyproject.toml").write_text(pyproject_gen.generate_pyproject(name, language))
1279
+ (service_dir / "pyproject.toml").write_text(
1280
+ pyproject_gen.generate_pyproject(
1281
+ name, language, has_readme=(service_dir / "README.md").exists()
1282
+ )
1283
+ )
1263
1284
  return service_dir
1264
1285
 
1265
1286
 
@@ -1298,7 +1319,11 @@ def _restore_scaffold(service_dir: Path, config: ServiceConfig) -> None:
1298
1319
  pyproject = service_dir / "pyproject.toml"
1299
1320
  if not pyproject.exists():
1300
1321
  pyproject.write_text(
1301
- pyproject_gen.generate_pyproject(config.name, config.language)
1322
+ pyproject_gen.generate_pyproject(
1323
+ config.name,
1324
+ config.language,
1325
+ has_readme=(service_dir / "README.md").exists(),
1326
+ )
1302
1327
  )
1303
1328
  click.echo(f"Restored missing {pyproject}")
1304
1329
 
@@ -29,7 +29,8 @@ _DEPENDENCIES = {
29
29
 
30
30
 
31
31
  def generate_pyproject(
32
- service_name: str, language: str, needs_numpy: bool = False
32
+ service_name: str, language: str, needs_numpy: bool = False,
33
+ has_readme: bool = False
33
34
  ) -> str:
34
35
  """The generated service's pyproject.toml.
35
36
 
@@ -38,6 +39,15 @@ def generate_pyproject(
38
39
  import. A C++ service with no such pointer does not get the dependency —
39
40
  declaring it unconditionally would put numpy in every image for the
40
41
  benefit of the services that do not use it.
42
+
43
+ `has_readme` is True when services/<name>/README.md exists on disk at
44
+ generate time. The `[project] readme` field then carries it, because a
45
+ wheel with no long_description is rejected by `twine check --strict`
46
+ (the whole project builds with that gate on) and lands on PyPI's
47
+ package page with the raw `text/x-rst` heading anyway. When the file
48
+ does not exist the field must stay out: setuptools refuses to build a
49
+ distribution whose declared readme is missing, and a service that has
50
+ never had a README should still build.
41
51
  """
42
52
  build_requires = _BUILD_REQUIRES.get(language, _BUILD_REQUIRES["cpp"])
43
53
  dependencies = _DEPENDENCIES.get(language, _DEPENDENCIES["cpp"])
@@ -45,6 +55,8 @@ def generate_pyproject(
45
55
  build_requires = build_requires.rstrip("]") + ', "numpy"]'
46
56
  dependencies = dependencies.rstrip("]") + ', "numpy"]'
47
57
 
58
+ readme_lines = 'readme = "README.md"\n' if has_readme else ""
59
+
48
60
  return f"""[build-system]
49
61
  requires = {build_requires}
50
62
  build-backend = "scikit_build_core.build"
@@ -52,7 +64,7 @@ build-backend = "scikit_build_core.build"
52
64
  [project]
53
65
  name = "{service_name}"
54
66
  version = "1.0.0"
55
- requires-python = ">=3.10"
67
+ {readme_lines}requires-python = ">=3.10"
56
68
  dependencies = {dependencies}
57
69
 
58
70
  [tool.scikit-build]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nativegate
3
- Version: 0.1.0
3
+ Version: 0.1.2
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
@@ -33,6 +33,11 @@ Requires-Dist: anyio>=4.0; extra == "test"
33
33
 
34
34
  # nativegate — make legacy C++ and Fortran AI-ready and microservice-ready
35
35
 
36
+ [![CI](https://github.com/ravikings/nativegate/actions/workflows/ci.yml/badge.svg)](https://github.com/ravikings/nativegate/actions/workflows/ci.yml)
37
+ [![PyPI](https://img.shields.io/pypi/v/nativegate)](https://pypi.org/project/nativegate/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/nativegate)](https://pypi.org/project/nativegate/)
39
+ [![MCP server (Glama)](https://glama.ai/mcp/servers/ravikings/nativegate/badges/score.svg)](https://glama.ai/mcp/servers/ravikings/nativegate)
40
+
36
41
  Expose existing C++ and Fortran code to Python as deployable microservices —
37
42
  without hand-writing bindings, and without touching the numerics.
38
43
 
@@ -41,6 +46,8 @@ bindings, the CMake build, an installable Python package, a FastAPI service,
41
46
  smoke tests, a numerical regression baseline, and a Dockerfile.
42
47
 
43
48
  ```bash
49
+ pip install "nativegate[clang,build]" # from PyPI; needs cmake/ninja + a C/C++/Fortran toolchain for the build step
50
+
44
51
  ngate quickstart libraries/geometry/geometry.hpp --name demo --build
45
52
  pip install services/demo/dist/*.whl
46
53
  ```
@@ -89,9 +89,11 @@ tests/test_oracle.py
89
89
  tests/test_preprocess_and_config.py
90
90
  tests/test_preprocessed_fortran.py
91
91
  tests/test_pybind_gen.py
92
+ tests/test_pyproject_gen.py
92
93
  tests/test_quickstart.py
93
94
  tests/test_scalar_ref.py
94
95
  tests/test_service_gen.py
96
+ tests/test_service_name_validation.py
95
97
  tests/test_shared_libraries.py
96
98
  tests/test_structural_invariants.py
97
99
  tests/test_suggest.py
@@ -13,7 +13,7 @@ build-backend = "setuptools.build_meta"
13
13
 
14
14
  [project]
15
15
  name = "nativegate"
16
- version = "0.1.0"
16
+ version = "0.1.2"
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,24 @@
1
+ """pyproject_gen: the [project] readme field is conditional on the file existing.
2
+
3
+ Regression for the netlib-specfun contact (DEFECTS D9-D11 record, 2026-09-23):
4
+ a generated service with no README.md shipped a pyproject.toml with no
5
+ long_description at all, and `twine check --strict` rejected every wheel
6
+ built from it. The field must appear only when the file is really there,
7
+ because setuptools refuses a distribution whose declared readme is missing.
8
+ """
9
+
10
+ from nativegate.generators import pyproject_gen
11
+
12
+
13
+ def test_no_readme_field_without_file():
14
+ pyproject = pyproject_gen.generate_pyproject("demo", "fortran")
15
+ assert "readme" not in pyproject
16
+
17
+
18
+ def test_readme_field_with_file():
19
+ pyproject = pyproject_gen.generate_pyproject(
20
+ "demo", "fortran", has_readme=True
21
+ )
22
+ assert 'readme = "README.md"' in pyproject
23
+ # still valid TOML-shaped and carries the content type setuptools infers
24
+ assert "[project]" in pyproject
@@ -0,0 +1,47 @@
1
+ """D9 regression: dash-named services are rejected at scaffold time, not
2
+ at generate time with invalid generated Python.
3
+
4
+ First contact with netlib specfun (2026-09-23): `ngate quickstart gamma.f
5
+ --name specfun-gamma` scaffolded cleanly, generated
6
+ `from ._native.specfun-gamma import ...` — invalid syntax — and only
7
+ nativegate's generated-file compile() gate refused to write it, three
8
+ commands after the moment the user should have been told.
9
+ """
10
+
11
+ import pytest
12
+ from click.testing import CliRunner
13
+
14
+ from nativegate import cli
15
+
16
+
17
+ @pytest.fixture()
18
+ def scratch(tmp_path, monkeypatch):
19
+ monkeypatch.chdir(tmp_path)
20
+ return tmp_path
21
+
22
+
23
+ @pytest.mark.parametrize("bad", ["specfun-gamma", "2lib", "class"])
24
+ def test_create_service_rejects_non_identifier_names(scratch, bad):
25
+ result = CliRunner().invoke(cli.main, ["create-service", bad, "--language", "fortran"])
26
+ assert result.exit_code != 0
27
+ assert "Python package" in result.output
28
+ # nothing may have been written
29
+ assert not (scratch / "services").exists()
30
+
31
+
32
+ def test_quickstart_rejects_dash_named_service_and_explains(tmp_path, monkeypatch):
33
+ monkeypatch.chdir(tmp_path)
34
+ source = tmp_path / "gamma.f"
35
+ source.write_text(" DOUBLE PRECISION FUNCTION DGAMMA(X)\n END\n")
36
+ result = CliRunner().invoke(
37
+ cli.main, ["quickstart", str(source), "--name", "specfun-gamma"]
38
+ )
39
+ assert result.exit_code != 0
40
+ assert "--name specfun_gamma" not in result.output
41
+
42
+
43
+ def test_valid_underscored_name_still_scaffolds(scratch):
44
+ result = CliRunner().invoke(
45
+ cli.main, ["create-service", "specfun_gamma", "--language", "fortran"]
46
+ )
47
+ assert result.exit_code == 0, result.output
File without changes
File without changes