From 1d3a75948b6c7b163938152d7b4302f5e57ee17b Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 17 Oct 2023 17:29:56 +0000 Subject: [PATCH 1/2] Document and enforce minimum dependency versions. --- cpp/test/generated/CMakeLists.txt | 14 +++++++++++--- docs/cpp/installation.md | 11 +++++++---- docs/python/quickstart.md | 3 ++- python/test_model/__init__.py | 7 +++++++ python/tests/roundtriputils.py | 6 +++--- tooling/internal/cpp/cmake.go | 14 +++++++++++--- .../cpp/include/detail/ndjson/serializers.h | 15 +++++++++------ tooling/internal/python/python.go | 11 +++++++++++ 8 files changed, 61 insertions(+), 20 deletions(-) diff --git a/cpp/test/generated/CMakeLists.txt b/cpp/test/generated/CMakeLists.txt index a12088c5..b7f6b753 100644 --- a/cpp/test/generated/CMakeLists.txt +++ b/cpp/test/generated/CMakeLists.txt @@ -6,7 +6,8 @@ # target_link_libraries( test_model_generated) # add_subdirectory() -find_package(date REQUIRED) +set(HOWARD_HINNANT_DATE_MINIMUM_VERSION "3.0.0") +find_package(date ${HOWARD_HINNANT_DATE_MINIMUM_VERSION} REQUIRED) if(VCPKG_TARGET_TRIPLET) set(HDF5_CXX_LIBRARIES hdf5::hdf5_cpp-shared) @@ -14,8 +15,14 @@ else() set(HDF5_CXX_LIBRARIES hdf5::hdf5_cpp) endif() -find_package(HDF5 REQUIRED COMPONENTS C CXX) -find_package(xtensor REQUIRED) +set(HDF5_MINIMUM_VERSION "1.10.5") +find_package(HDF5 ${HDF5_MINIMUM_VERSION} REQUIRED COMPONENTS C CXX) + +set(XTENSOR_MINIMUM_VERSION "0.21.10") +find_package(xtensor ${XTENSOR_MINIMUM_VERSION} REQUIRED) + +set(NLOHMANN_JSON_MINIMUM_VERSION "3.11.1") +find_package(nlohmann_json ${NLOHMANN_JSON_MINIMUM_VERSION} REQUIRED) add_library(test_model_generated OBJECT factories.cc protocols.cc @@ -31,6 +38,7 @@ target_link_libraries(test_model_generated PUBLIC ${HDF5_CXX_LIBRARIES} PUBLIC xtensor PUBLIC date::date + PUBLIC nlohmann_json::nlohmann_json ) add_library(test_model_generated_mocks OBJECT diff --git a/docs/cpp/installation.md b/docs/cpp/installation.md index 2d08af39..8764b1a6 100644 --- a/docs/cpp/installation.md +++ b/docs/cpp/installation.md @@ -7,11 +7,14 @@ In order to compile the C++ code that `yardl` generates, you will need to have a C++17 (or more recent) compiler and the following dependencies installed: -1. HDF5 with the [C++ API](https://support.hdfgroup.org/HDF5/doc/cpplus_RM/). -2. [xtensor](https://xtensor.readthedocs.io/en/latest/) +1. HDF5 with the [C++ API](https://support.hdfgroup.org/HDF5/doc/cpplus_RM/), + version 1.10.5 or later. +2. [xtensor](https://xtensor.readthedocs.io/en/latest/), version 0.21.10 or + later. 3. Howard Hinnant's [date](https://howardhinnant.github.io/date/date.html) - library. -4. [JSON for Modern C++](https://github.com/nlohmann/json). + library, version 3.0.0 or later. +4. [JSON for Modern C++](https://github.com/nlohmann/json), version: 3.11.1 or + later. ### Conda diff --git a/docs/python/quickstart.md b/docs/python/quickstart.md index 6ebf33ed..797d5c1f 100644 --- a/docs/python/quickstart.md +++ b/docs/python/quickstart.md @@ -6,7 +6,8 @@ ### Dependencies -The generated Python code requires Python 3.9 or newer and you need to have [NumPy](https://numpy.org/install/) installed. +The generated Python code requires Python 3.9 or newer and you need to have +[NumPy](https://numpy.org/install/) version 1.22.0 or later installed. ## Getting our Feet Wet diff --git a/python/test_model/__init__.py b/python/test_model/__init__.py index 854eab21..d6435e3a 100644 --- a/python/test_model/__init__.py +++ b/python/test_model/__init__.py @@ -1,5 +1,12 @@ # This file was generated by the "yardl" tool. DO NOT EDIT. +import numpy as np +from packaging import version + +MIN_NUMPY_VERSION = "1.22.0" +if version.parse(np.__version__) < version.parse(MIN_NUMPY_VERSION): + raise ImportError(f"Your installed numpy version is {np.__version__}, but version >= {MIN_NUMPY_VERSION} is required.") + from .yardl_types import * from .types import ( AcquisitionOrImage, diff --git a/python/tests/roundtriputils.py b/python/tests/roundtriputils.py index 9ae11bed..905cb407 100644 --- a/python/tests/roundtriputils.py +++ b/python/tests/roundtriputils.py @@ -2,7 +2,7 @@ import pathlib import subprocess import types -from typing import Callable, TypeVar, cast +from typing import Callable, TypeVar, Union, cast import test_model as tm from .factories import Format @@ -17,8 +17,8 @@ def invoke_translator( - input: bytes | str, input_format: Format, output_format: Format -) -> bytes | str: + input: Union[bytes, str], input_format: Format, output_format: Format +) -> Union[bytes, str]: if isinstance(input, str): print(input) input = input.encode("utf-8") diff --git a/tooling/internal/cpp/cmake.go b/tooling/internal/cpp/cmake.go index 5d6999d6..a6a8092a 100644 --- a/tooling/internal/cpp/cmake.go +++ b/tooling/internal/cpp/cmake.go @@ -28,7 +28,8 @@ func writeCMakeLists(env *dsl.Environment, options packaging.CppCodegenOptions) # target_link_libraries( %s) # add_subdirectory() -find_package(date REQUIRED) +set(HOWARD_HINNANT_DATE_MINIMUM_VERSION "3.0.0") +find_package(date ${HOWARD_HINNANT_DATE_MINIMUM_VERSION} REQUIRED) if(VCPKG_TARGET_TRIPLET) set(HDF5_CXX_LIBRARIES hdf5::hdf5_cpp-shared) @@ -36,8 +37,14 @@ else() set(HDF5_CXX_LIBRARIES hdf5::hdf5_cpp) endif() -find_package(HDF5 REQUIRED COMPONENTS C CXX) -find_package(xtensor REQUIRED) +set(HDF5_MINIMUM_VERSION "1.10.5") +find_package(HDF5 ${HDF5_MINIMUM_VERSION} REQUIRED COMPONENTS C CXX) + +set(XTENSOR_MINIMUM_VERSION "0.21.10") +find_package(xtensor ${XTENSOR_MINIMUM_VERSION} REQUIRED) + +set(NLOHMANN_JSON_MINIMUM_VERSION "3.11.1") +find_package(nlohmann_json ${NLOHMANN_JSON_MINIMUM_VERSION} REQUIRED) `, objectLibraryName) fmt.Fprintf(w, "add_library(%s OBJECT\n", objectLibraryName) @@ -62,6 +69,7 @@ find_package(xtensor REQUIRED) w.WriteStringln("PUBLIC ${HDF5_CXX_LIBRARIES}") w.WriteStringln("PUBLIC xtensor") w.WriteStringln("PUBLIC date::date") + w.WriteStringln("PUBLIC nlohmann_json::nlohmann_json") }) w.WriteString(")\n") diff --git a/tooling/internal/cpp/include/detail/ndjson/serializers.h b/tooling/internal/cpp/include/detail/ndjson/serializers.h index e3700070..1bd729d3 100644 --- a/tooling/internal/cpp/include/detail/ndjson/serializers.h +++ b/tooling/internal/cpp/include/detail/ndjson/serializers.h @@ -205,8 +205,9 @@ struct adl_serializer> { static void from_json(ordered_json const& j, yardl::DynamicNDArray& value) { value.resize(j.at("shape").get>()); auto data_array = j.at("data").get>(); - for (size_t i = 0; i < data_array.size(); ++i) { - value.flat(i) = data_array[i]; + size_t i = 0; + for (auto& element : value) { + element = data_array[i++]; } } }; @@ -225,8 +226,9 @@ struct adl_serializer> { static void from_json(ordered_json const& j, yardl::NDArray& value) { value.resize(j.at("shape").get>()); auto data_array = j.at("data").get>(); - for (size_t i = 0; i < data_array.size(); ++i) { - value.flat(i) = data_array[i]; + size_t i = 0; + for (auto& element : value) { + element = data_array[i++]; } } }; @@ -243,8 +245,9 @@ struct adl_serializer> { static void from_json(ordered_json const& j, yardl::FixedNDArray& value) { auto data_array = j.get>(); - for (size_t i = 0; i < data_array.size(); ++i) { - value.flat(i) = data_array[i]; + size_t i = 0; + for (auto& element : value) { + element = data_array[i++]; } } }; diff --git a/tooling/internal/python/python.go b/tooling/internal/python/python.go index dd71e38e..719f212e 100644 --- a/tooling/internal/python/python.go +++ b/tooling/internal/python/python.go @@ -82,6 +82,17 @@ func writePackageInitFile(packageDir string, ns *dsl.Namespace) error { w := formatting.NewIndentedWriter(&b, " ") common.WriteGeneratedFileHeader(w) + w.WriteStringln("import numpy as np") + w.WriteStringln("from packaging import version") + w.WriteStringln("") + + w.WriteStringln(`MIN_NUMPY_VERSION = "1.22.0"`) + w.WriteStringln("if version.parse(np.__version__) < version.parse(MIN_NUMPY_VERSION):") + w.Indented(func() { + w.WriteStringln(`raise ImportError(f"Your installed numpy version is {np.__version__}, but version >= {MIN_NUMPY_VERSION} is required.")`) + }) + w.WriteStringln("") + fmt.Fprintf(w, "from .yardl_types import *\n") typesMembers := make([]string, 0) From 562519d1c7b647d5c92ad760791015bdaf8457d8 Mon Sep 17 00:00:00 2001 From: John Stairs Date: Tue, 17 Oct 2023 19:55:28 +0000 Subject: [PATCH 2/2] Remove dependency on packaging --- python/test_model/__init__.py | 20 +++++++++++++++----- python/tests/test_init.py | 11 +++++++++++ tooling/internal/python/python.go | 27 +++++++++++++++++---------- 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 python/tests/test_init.py diff --git a/python/test_model/__init__.py b/python/test_model/__init__.py index d6435e3a..85c6b731 100644 --- a/python/test_model/__init__.py +++ b/python/test_model/__init__.py @@ -1,11 +1,21 @@ # This file was generated by the "yardl" tool. DO NOT EDIT. -import numpy as np -from packaging import version +from typing import Tuple as _Tuple +import re as _re +import numpy as _np -MIN_NUMPY_VERSION = "1.22.0" -if version.parse(np.__version__) < version.parse(MIN_NUMPY_VERSION): - raise ImportError(f"Your installed numpy version is {np.__version__}, but version >= {MIN_NUMPY_VERSION} is required.") +_MIN_NUMPY_VERSION = (1, 22, 0) + +def _parse_version(version: str) -> _Tuple[int, ...]: + try: + return tuple(map(int, version.split("."))) + except ValueError: + # ignore any prerelease suffix + version = _re.sub(r"[^0-9.]", "", version) + return tuple(map(int, version.split("."))) + +if _parse_version(_np.__version__) < _MIN_NUMPY_VERSION: + raise ImportError(f"Your installed numpy version is {_np.__version__}, but version >= {'.'.join(str(i) for i in _MIN_NUMPY_VERSION)} is required.") from .yardl_types import * from .types import ( diff --git a/python/tests/test_init.py b/python/tests/test_init.py new file mode 100644 index 00000000..7a6acb28 --- /dev/null +++ b/python/tests/test_init.py @@ -0,0 +1,11 @@ +import test_model as tm +from packaging import version +# pyright: basic + +def test_parse_version(): + assert tm._parse_version("1.2.3") == (1,2,3) + assert tm._parse_version("1.2.3") < (1,2,4) + assert tm._parse_version("2.2.3") > (1,9,9) + assert tm._parse_version("2.2.3") > (1,9) + assert tm._parse_version("1.26.1rc1") > (1,26,1) + version.parse diff --git a/tooling/internal/python/python.go b/tooling/internal/python/python.go index 719f212e..e259154b 100644 --- a/tooling/internal/python/python.go +++ b/tooling/internal/python/python.go @@ -82,16 +82,23 @@ func writePackageInitFile(packageDir string, ns *dsl.Namespace) error { w := formatting.NewIndentedWriter(&b, " ") common.WriteGeneratedFileHeader(w) - w.WriteStringln("import numpy as np") - w.WriteStringln("from packaging import version") - w.WriteStringln("") - - w.WriteStringln(`MIN_NUMPY_VERSION = "1.22.0"`) - w.WriteStringln("if version.parse(np.__version__) < version.parse(MIN_NUMPY_VERSION):") - w.Indented(func() { - w.WriteStringln(`raise ImportError(f"Your installed numpy version is {np.__version__}, but version >= {MIN_NUMPY_VERSION} is required.")`) - }) - w.WriteStringln("") + w.WriteStringln(`from typing import Tuple as _Tuple +import re as _re +import numpy as _np + +_MIN_NUMPY_VERSION = (1, 22, 0) + +def _parse_version(version: str) -> _Tuple[int, ...]: + try: + return tuple(map(int, version.split("."))) + except ValueError: + # ignore any prerelease suffix + version = _re.sub(r"[^0-9.]", "", version) + return tuple(map(int, version.split("."))) + +if _parse_version(_np.__version__) < _MIN_NUMPY_VERSION: + raise ImportError(f"Your installed numpy version is {_np.__version__}, but version >= {'.'.join(str(i) for i in _MIN_NUMPY_VERSION)} is required.") +`) fmt.Fprintf(w, "from .yardl_types import *\n")