rune-runtime 2.2.1__tar.gz → 2.2.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 (94) hide show
  1. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/CODE_OF_CONDUCT.md +4 -0
  2. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/ISSUE_TEMPLATE/Bug_report.md +4 -0
  3. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/ISSUE_TEMPLATE/Feature_request.md +4 -0
  4. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/ISSUE_TEMPLATE/Support_question.md +4 -0
  5. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/ISSUE_TEMPLATE/meeting-minutes.md +4 -0
  6. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/workflows/build-python-package.yml +4 -0
  7. rune_runtime-2.2.2/.github/workflows/check-license-headers.yml +39 -0
  8. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/workflows/cve-scanning-python.yml +4 -0
  9. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/workflows/license-scanning-python.yml +4 -0
  10. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/workflows/release.yml +4 -0
  11. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.github/workflows/run-tests.yml +8 -0
  12. rune_runtime-2.2.2/.pre-commit-config.yaml +25 -0
  13. {rune_runtime-2.2.1/src/rune_runtime.egg-info → rune_runtime-2.2.2}/PKG-INFO +17 -1
  14. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/README.md +14 -0
  15. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/build_wheel.sh +4 -0
  16. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/dev_clean_setup.sh +6 -0
  17. rune_runtime-2.2.2/netlify.toml +6 -0
  18. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/pyproject.toml +7 -1
  19. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/safety-policy.yml +4 -0
  20. rune_runtime-2.2.2/src/license/apply_headers.sh +6 -0
  21. rune_runtime-2.2.2/src/license/header.txt +2 -0
  22. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/base_data_class.py +1 -1
  23. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/metadata.py +28 -27
  24. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/version.py +3 -3
  25. {rune_runtime-2.2.1 → rune_runtime-2.2.2/src/rune_runtime.egg-info}/PKG-INFO +17 -1
  26. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune_runtime.egg-info/SOURCES.txt +4 -0
  27. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune_runtime.egg-info/requires.txt +2 -0
  28. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune_runtime.egg-info/scm_file_list.json +4 -0
  29. rune_runtime-2.2.2/src/rune_runtime.egg-info/scm_version.json +8 -0
  30. rune_runtime-2.2.2/src/rune_runtime.egg-info/top_level.txt +2 -0
  31. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_basic_types_with_meta.py +1 -1
  32. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_keys_and_references.py +57 -3
  33. rune_runtime-2.2.1/netlify.toml +0 -2
  34. rune_runtime-2.2.1/src/rune_runtime.egg-info/scm_version.json +0 -8
  35. rune_runtime-2.2.1/src/rune_runtime.egg-info/top_level.txt +0 -1
  36. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.gitignore +0 -0
  37. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/.vscode/settings.json +0 -0
  38. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/CONTRIBUTING.md +0 -0
  39. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/EASYCLA_CHANGEME.md +0 -0
  40. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/LICENSE +0 -0
  41. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/LICENSE.spdx +0 -0
  42. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/MAINTAINERS.md +0 -0
  43. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/NOTICE +0 -0
  44. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/docs/home.md +0 -0
  45. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/renovate.json +0 -0
  46. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/setup.cfg +0 -0
  47. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/__init__.py +0 -0
  48. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/conditions.py +0 -0
  49. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/cow.py +0 -0
  50. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/extensions/__init__.py +0 -0
  51. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/extensions/cached_json_rune_object_deserializer.py +0 -0
  52. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/func_proxy.py +0 -0
  53. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/native_registry.py +0 -0
  54. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/object_builder.py +0 -0
  55. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/object_registry.py +0 -0
  56. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/py.typed +0 -0
  57. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune/runtime/utils.py +0 -0
  58. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/src/rune_runtime.egg-info/dependency_links.txt +0 -0
  59. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/__init__.py +0 -0
  60. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/cdm/EUR-Vanilla-account.json +0 -0
  61. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/cdm/test_create_irs.py +0 -0
  62. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/cdm/test_trade_creation.py +0 -0
  63. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/cdm/test_validation.py +0 -0
  64. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/cdm/_bundle.py +0 -0
  65. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/cdm/base/math/Abs.py +0 -0
  66. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/cdm/base/math/RoundToNearest.py +0 -0
  67. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/cdm/base/math/RoundingModeEnum.py +0 -0
  68. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/test_native_registry.py +0 -0
  69. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/functions/specs/test_simple.py +0 -0
  70. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/local_data/ird-ex01-vanilla-swap-versioned.json +0 -0
  71. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/run_runtime_tests.sh +0 -0
  72. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_basic.py +0 -0
  73. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_conditions.py +0 -0
  74. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_enumtype.py +0 -0
  75. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_extension.py +0 -0
  76. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_metakey.py +0 -0
  77. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_metalocation.py +0 -0
  78. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/serializer-round-trip/test_metascheme.py +0 -0
  79. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_cached_json_rune_object_deserializer.py +0 -0
  80. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_complex_types_with_meta.py +0 -0
  81. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_cow.py +0 -0
  82. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_deep_keys_and_references.py +0 -0
  83. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_deferred_ref_deserialization.py +0 -0
  84. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_func_proxy.py +0 -0
  85. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_list_cardinality_with_meta.py +0 -0
  86. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_namespace_prefix.py +0 -0
  87. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_object_builder.py +0 -0
  88. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_operations.py +0 -0
  89. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_parametrized_basic_types.py +0 -0
  90. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_rune_parent.py +0 -0
  91. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_rune_str.py +0 -0
  92. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_subtype_identity.py +0 -0
  93. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_utils.py +0 -0
  94. {rune_runtime-2.2.1 → rune_runtime-2.2.2}/test/test_zoned_date_time.py +0 -0
@@ -1,3 +1,7 @@
1
+ <!--
2
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ SPDX-License-Identifier: Apache-2.0
4
+ -->
1
5
  # Code of Conduct for {project name}
2
6
 
3
7
  Please see the [Community Code of Conduct](https://www.finos.org/code-of-conduct).
@@ -1,3 +1,7 @@
1
+ <!--
2
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ SPDX-License-Identifier: Apache-2.0
4
+ -->
1
5
  ---
2
6
  name: 🐛 Bug Report
3
7
  about: If something isn't working as expected 🤔.
@@ -1,3 +1,7 @@
1
+ <!--
2
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ SPDX-License-Identifier: Apache-2.0
4
+ -->
1
5
  ---
2
6
  name: 🚀 Feature Request
3
7
  about: I have a suggestion (and may want to implement it 🙂)!
@@ -1,3 +1,7 @@
1
+ <!--
2
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ SPDX-License-Identifier: Apache-2.0
4
+ -->
1
5
  ---
2
6
  name: 🤗 Support Question
3
7
  about: If you have a question about configuration, usage, etc. 💬
@@ -1,3 +1,7 @@
1
+ <!--
2
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ SPDX-License-Identifier: Apache-2.0
4
+ -->
1
5
  ---
2
6
  name: "\U0001F91D {project name} Meeting Minutes"
3
7
  about: To track {project name} meeting agenda and attendance
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  name: Build and Release Wheel
2
6
 
3
7
  on:
@@ -0,0 +1,39 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
5
+ name: Check License Headers
6
+
7
+ on:
8
+ push:
9
+ branches:
10
+ - main
11
+ - develop
12
+ pull_request:
13
+ branches:
14
+ - main
15
+ - develop
16
+
17
+ jobs:
18
+ license-headers:
19
+ name: Verify license headers
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - name: Checkout repository
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Set up Python 3.11
27
+ uses: actions/setup-python@v5
28
+ with:
29
+ python-version: '3.11'
30
+
31
+ - name: Install licenseheaders
32
+ run: pip install licenseheaders
33
+
34
+ - name: Check license headers
35
+ run: |
36
+ src/license/apply_headers.sh
37
+ git diff --exit-code || \
38
+ (echo "One or more files are missing a license header. Run 'src/license/apply_headers.sh' locally to fix." && \
39
+ exit 1)
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  name: CVE Scanning for Python
2
6
 
3
7
  on:
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  name: License Scanning for Python
2
6
 
3
7
  on:
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  name: Publish to PyPi
2
6
 
3
7
  on:
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  name: Run Unit Tests
2
6
 
3
7
  on:
@@ -5,6 +9,10 @@ on:
5
9
  branches:
6
10
  - main
7
11
  - develop
12
+ pull_request:
13
+ branches:
14
+ - main
15
+ - develop
8
16
 
9
17
  jobs:
10
18
  test:
@@ -0,0 +1,25 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
5
+ repos:
6
+ - repo: local
7
+ hooks:
8
+ - id: license-headers-dirs
9
+ name: Add/check license headers (src, test, .github)
10
+ entry: licenseheaders
11
+ args: [-t, src/license/header.txt, --dir, src, --dir, test, --dir, .github]
12
+ language: python
13
+ additional_dependencies: [licenseheaders>=0.8.8]
14
+ types_or: [python, yaml, shell, toml]
15
+ pass_filenames: false
16
+ - id: license-headers-files
17
+ name: Add/check license headers (root files)
18
+ entry: licenseheaders
19
+ args: [-t, src/license/header.txt, --additional-extensions, python=.toml,
20
+ -f, build_wheel.sh, dev_clean_setup.sh, netlify.toml, pyproject.toml,
21
+ safety-policy.yml, .pre-commit-config.yaml]
22
+ language: python
23
+ additional_dependencies: [licenseheaders>=0.8.8]
24
+ types_or: [python, yaml, shell, toml]
25
+ pass_filenames: false
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rune-runtime
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: rune-runtime: the Rune DSL runtime for Python
5
5
  Author: Daniel Schwartz, Plamen Neykov, Others (See AUTHORS)
6
6
  License-Expression: Apache-2.0
@@ -30,6 +30,8 @@ Requires-Dist: pytest>=9.0.2; extra == "dev"
30
30
  Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
31
31
  Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
32
32
  Requires-Dist: types-python-dateutil>=2.9.0.20250809; extra == "dev"
33
+ Requires-Dist: licenseheaders>=0.8.8; extra == "dev"
34
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
33
35
  Dynamic: license-file
34
36
 
35
37
  [![FINOS - Incubating](https://cdn.jsdelivr.net/gh/finos/contrib-toolbox@master/images/badge-incubating.svg)](https://community.finos.org/docs/governance/Software-Projects/stages/incubating)
@@ -59,6 +61,20 @@ Use [dev_clean_setup.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/m
59
61
  ```sh
60
62
  ./dev_clean_setup.sh
61
63
  ```
64
+
65
+ This script installs all dev dependencies and registers the pre-commit hooks, including the license-header check. Every `.py` file under `src/` and `test/` must carry the Apache 2.0 SPDX header:
66
+
67
+ ```python
68
+ # Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
69
+ # SPDX-License-Identifier: Apache-2.0
70
+ ```
71
+
72
+ To add missing headers to all files at any time, run:
73
+
74
+ ```sh
75
+ src/license/apply_headers.sh
76
+ ```
77
+
62
78
  ### 3. Build
63
79
  Use [build_wheel.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/main/build_wheel.sh) to build the package
64
80
  ```sh
@@ -25,6 +25,20 @@ Use [dev_clean_setup.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/m
25
25
  ```sh
26
26
  ./dev_clean_setup.sh
27
27
  ```
28
+
29
+ This script installs all dev dependencies and registers the pre-commit hooks, including the license-header check. Every `.py` file under `src/` and `test/` must carry the Apache 2.0 SPDX header:
30
+
31
+ ```python
32
+ # Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
33
+ # SPDX-License-Identifier: Apache-2.0
34
+ ```
35
+
36
+ To add missing headers to all files at any time, run:
37
+
38
+ ```sh
39
+ src/license/apply_headers.sh
40
+ ```
41
+
28
42
  ### 3. Build
29
43
  Use [build_wheel.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/main/build_wheel.sh) to build the package
30
44
  ```sh
@@ -1,4 +1,8 @@
1
1
  #!/bin/bash
2
+ ##
3
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
4
+ ## SPDX-License-Identifier: Apache-2.0
5
+ ##
2
6
 
3
7
  function error
4
8
  {
@@ -1,4 +1,8 @@
1
1
  #!/bin/bash
2
+ ##
3
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
4
+ ## SPDX-License-Identifier: Apache-2.0
5
+ ##
2
6
 
3
7
  function error
4
8
  {
@@ -34,3 +38,5 @@ ${PY_EXE} -m venv --clear .pydevenv || error
34
38
 
35
39
 
36
40
  python -m pip install -e .[dev] --config-settings editable_mode=compat || error
41
+
42
+ pre-commit install || error
@@ -0,0 +1,6 @@
1
+ #
2
+ # Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
5
+ [build]
6
+ ignore = "/bin/false"
@@ -1,3 +1,7 @@
1
+ #
2
+ # Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ #
1
5
  [build-system]
2
6
  requires = ["setuptools>=64", "setuptools_scm>=8", "wheel>=0.45.1", "pytest-runner>=6.0.1"]
3
7
  build-backend = "setuptools.build_meta"
@@ -15,7 +19,9 @@ optional-dependencies.dev = [
15
19
  "pytest>=9.0.2",
16
20
  "pytest-cov>=7.0.0",
17
21
  "pytest-mock>=3.14.1",
18
- "types-python-dateutil>=2.9.0.20250809"
22
+ "types-python-dateutil>=2.9.0.20250809",
23
+ "licenseheaders>=0.8.8",
24
+ "pre-commit>=3.0.0"
19
25
  ]
20
26
  description = "rune-runtime: the Rune DSL runtime for Python"
21
27
  readme = "README.md"
@@ -1,3 +1,7 @@
1
+ ##
2
+ ## Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
3
+ ## SPDX-License-Identifier: Apache-2.0
4
+ ##
1
5
  security:
2
6
  ignore-cvss-severity-below: 6
3
7
  ignore-cvss-unknown-severity: False
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ # Applies the Apache 2.0 SPDX license header to all tracked source files.
3
+ set -e
4
+ TMPL="$(dirname "$0")/header.txt"
5
+ licenseheaders -t "$TMPL" --dir src --dir test --dir .github
6
+ licenseheaders -t "$TMPL" --additional-extensions python=.toml -f build_wheel.sh dev_clean_setup.sh netlify.toml pyproject.toml safety-policy.yml .pre-commit-config.yaml
@@ -0,0 +1,2 @@
1
+ Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
2
+ SPDX-License-Identifier: Apache-2.0
@@ -107,7 +107,7 @@ class BaseDataClass(BaseModel, ComplexTypeMetaDataMixin):
107
107
  @model_serializer(mode='wrap')
108
108
  def _serialize_refs(self, serializer, info):
109
109
  '''should replace objects with refs while serializing'''
110
- res = serializer(self, info)
110
+ res = serializer(self)
111
111
  refs = self._get_rune_refs_container()
112
112
  for property_nm, (key, ref_type) in refs.items():
113
113
  res[property_nm] = {ref_type.rune_ref_tag: key}
@@ -1,15 +1,16 @@
1
1
  '''Classes representing annotated basic Rune types'''
2
- import uuid
3
2
  import datetime
4
3
  import importlib
5
- from enum import Enum
6
- from functools import partial, lru_cache
4
+ import uuid
5
+ from collections.abc import Iterable
7
6
  from decimal import Decimal
8
- from typing import Any, Never, get_args, Iterable
9
- from typing_extensions import Self, Tuple
10
- from pydantic import (PlainSerializer, PlainValidator, WrapValidator,
11
- WrapSerializer)
7
+ from enum import Enum
8
+ from functools import lru_cache, partial
9
+ from typing import Any, Never, Self, get_args
10
+
11
+ from pydantic import PlainSerializer, PlainValidator, WrapSerializer, WrapValidator
12
12
  from pydantic_core import PydanticCustomError
13
+
13
14
  # from rune.runtime.object_registry import get_object
14
15
 
15
16
  DEFAULT_META = '_ALLOWED_METADATA'
@@ -135,7 +136,7 @@ class Reference(BaseReference):
135
136
  class UnresolvedReference(BaseReference):
136
137
  '''used by the deserialization to hold temporarily unresolved references'''
137
138
  def __init__(self, key):
138
- rune_type, self.key = list(key.items())[0]
139
+ rune_type, self.key = next(iter(key.items()))
139
140
  self.key_type = KeyType.from_rune(rune_type)
140
141
 
141
142
  def get_reference(self, parent):
@@ -219,7 +220,7 @@ class BaseMetaDataMixin:
219
220
  self.set_meta(key=key)
220
221
  try:
221
222
  self._get_object_map(KeyType.INTERNAL)[key] = self
222
- except: # noqa
223
+ except:
223
224
  self.set_meta(key=None)
224
225
  raise
225
226
  return key
@@ -238,7 +239,7 @@ class BaseMetaDataMixin:
238
239
  self.set_meta(check_allowed=True, **{key_type.key_tag: key})
239
240
  try:
240
241
  self._get_object_map(key_type)[key] = self
241
- except: # noqa
242
+ except:
242
243
  self.set_meta(check_allowed=True, **{key_type.key_tag: None})
243
244
  raise
244
245
  return self
@@ -298,13 +299,14 @@ class BaseMetaDataMixin:
298
299
  f'not allowed for {property_nm}. Allowed types '
299
300
  f'are: {allowed_ref_types.get(property_nm, {})}')
300
301
 
301
- field_type = self.__class__.__annotations__.get(property_nm)
302
+ # Pydantic includes inherited fields and resolves their annotations.
303
+ field_type = type(self).model_fields[property_nm].annotation # type: ignore
302
304
  allowed_type = _get_basic_type(field_type)
303
305
  if not (isinstance(allowed_type, str)
304
306
  or isinstance(ref.target, allowed_type)):
305
- raise ValueError("Can't set reference. Incompatible types: "
306
- f"expected {allowed_type}, "
307
- f"got {ref.target.__class__}")
307
+ raise TypeError("Can't set reference. Incompatible types: "
308
+ f"expected {allowed_type}, "
309
+ f"got {ref.target.__class__}")
308
310
 
309
311
  refs = self.__dict__.setdefault(REFS_CONTAINER, {})
310
312
  if property_nm not in refs:
@@ -416,9 +418,8 @@ class ComplexTypeMetaDataMixin(BaseMetaDataMixin):
416
418
  prefix = namespace_prefix
417
419
  if prefix is None:
418
420
  prefix = cls._get_rune_namespace_prefix()
419
- if prefix:
420
- if not rune_type.startswith(prefix + '.'):
421
- import_path = prefix + '.' + rune_type
421
+ if prefix and not rune_type.startswith(prefix + '.'):
422
+ import_path = prefix + '.' + rune_type
422
423
  rune_module = importlib.import_module(import_path)
423
424
  return getattr(rune_module, rune_class_name)
424
425
  return cls # support for legacy json
@@ -453,14 +454,14 @@ class ComplexTypeMetaDataMixin(BaseMetaDataMixin):
453
454
  'Expected either {my_type} or dict but '
454
455
  'got {type}.',
455
456
  {'type': type(obj), 'my_type': cls})
456
- metadata = {k: obj[k] for k in obj.keys() if k.startswith('@')}
457
+ metadata = {k: obj[k] for k in obj if k.startswith('@')}
457
458
 
458
459
  # References deserialization treatment
459
460
  if aux := cls._create_unresolved_ref(metadata):
460
461
  return aux
461
462
 
462
463
  # Model creation
463
- for k in metadata.keys():
464
+ for k in metadata:
464
465
  obj.pop(k)
465
466
 
466
467
  rune_cls = cls._type_to_cls(metadata)
@@ -483,7 +484,7 @@ class ComplexTypeMetaDataMixin(BaseMetaDataMixin):
483
484
 
484
485
  @classmethod
485
486
  @lru_cache
486
- def validator(cls, allowed_meta: tuple[str] | tuple[Never, ...] = tuple()):
487
+ def validator(cls, allowed_meta: tuple[str] | tuple[Never, ...] = ()):
487
488
  '''default validator for the specific class'''
488
489
  allowed = set(allowed_meta)
489
490
  return PlainValidator(partial(cls.deserialize, allowed_meta=allowed),
@@ -492,16 +493,16 @@ class ComplexTypeMetaDataMixin(BaseMetaDataMixin):
492
493
 
493
494
  class BasicTypeMetaDataMixin(BaseMetaDataMixin):
494
495
  '''holds the metadata associated with an instance'''
495
- _INPUT_TYPES: Any | Tuple[Any, ...] = str # to be overridden by subclasses
496
+ _INPUT_TYPES: Any | tuple[Any, ...] = str # to be overridden by subclasses
496
497
  _OUTPUT_TYPE: Any = str # to be overridden by subclasses
497
498
  _JSON_OUTPUT = str | dict
498
499
 
499
500
  @classmethod
500
501
  def _check_type(cls, value):
501
502
  if not isinstance(value, cls._INPUT_TYPES):
502
- raise ValueError(f'{cls.__name__} can be instantiated only with '
503
- f'one of the following type(s): {cls._INPUT_TYPES},'
504
- f' however the value is of type {type(value)}')
503
+ raise TypeError(f'{cls.__name__} can be instantiated only with '
504
+ f'one of the following type(s): {cls._INPUT_TYPES},'
505
+ f' however the value is of type {type(value)}')
505
506
 
506
507
  @classmethod
507
508
  def serialise(cls, obj, base_type) -> dict:
@@ -514,7 +515,7 @@ class BasicTypeMetaDataMixin(BaseMetaDataMixin):
514
515
  def deserialize(cls, obj, handler, base_types, allowed_meta: set[str]):
515
516
  '''method used as pydantic `validator`'''
516
517
  if isinstance(obj, list):
517
- identity = lambda x: x # noqa: E731
518
+ identity = lambda x: x
518
519
  processed = [cls.deserialize(item, identity, base_types, allowed_meta) for item in obj]
519
520
  return handler(processed)
520
521
  model = obj
@@ -649,7 +650,7 @@ class _EnumWrapper(BaseMetaDataMixin):
649
650
  '''wrapper for enums with metadata'''
650
651
  def __init__(self, enum_instance=_EnumWrapperDefaultVal.NOT_SET):
651
652
  if not isinstance(enum_instance, Enum):
652
- raise ValueError("enum_instance must be an instance of an Enum")
653
+ raise TypeError("enum_instance must be an instance of an Enum")
653
654
  self._enum_instance = enum_instance
654
655
 
655
656
  @property
@@ -723,7 +724,7 @@ class EnumWithMetaMixin:
723
724
 
724
725
  @classmethod
725
726
  @lru_cache
726
- def validator(cls, allowed_meta: tuple[str] | tuple[Never, ...] = tuple()):
727
+ def validator(cls, allowed_meta: tuple[str] | tuple[Never, ...] = ()):
727
728
  '''default validator for the specific class'''
728
729
  allowed = set(allowed_meta)
729
730
  return PlainValidator(partial(cls.deserialize, allowed_meta=allowed),
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.2.1'
22
- __version_tuple__ = version_tuple = (2, 2, 1)
21
+ __version__ = version = '2.2.2'
22
+ __version_tuple__ = version_tuple = (2, 2, 2)
23
23
 
24
- __commit_id__ = commit_id = 'g05cabe482'
24
+ __commit_id__ = commit_id = 'g951f0332c'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rune-runtime
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: rune-runtime: the Rune DSL runtime for Python
5
5
  Author: Daniel Schwartz, Plamen Neykov, Others (See AUTHORS)
6
6
  License-Expression: Apache-2.0
@@ -30,6 +30,8 @@ Requires-Dist: pytest>=9.0.2; extra == "dev"
30
30
  Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
31
31
  Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
32
32
  Requires-Dist: types-python-dateutil>=2.9.0.20250809; extra == "dev"
33
+ Requires-Dist: licenseheaders>=0.8.8; extra == "dev"
34
+ Requires-Dist: pre-commit>=3.0.0; extra == "dev"
33
35
  Dynamic: license-file
34
36
 
35
37
  [![FINOS - Incubating](https://cdn.jsdelivr.net/gh/finos/contrib-toolbox@master/images/badge-incubating.svg)](https://community.finos.org/docs/governance/Software-Projects/stages/incubating)
@@ -59,6 +61,20 @@ Use [dev_clean_setup.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/m
59
61
  ```sh
60
62
  ./dev_clean_setup.sh
61
63
  ```
64
+
65
+ This script installs all dev dependencies and registers the pre-commit hooks, including the license-header check. Every `.py` file under `src/` and `test/` must carry the Apache 2.0 SPDX header:
66
+
67
+ ```python
68
+ # Copyright (c) 2023-2026 CLOUDRISK Limited and FT Advisory LLC
69
+ # SPDX-License-Identifier: Apache-2.0
70
+ ```
71
+
72
+ To add missing headers to all files at any time, run:
73
+
74
+ ```sh
75
+ src/license/apply_headers.sh
76
+ ```
77
+
62
78
  ### 3. Build
63
79
  Use [build_wheel.sh](https://github.com/Cloudrisk/rune-python-runtime/blob/main/build_wheel.sh) to build the package
64
80
  ```sh
@@ -1,4 +1,5 @@
1
1
  .gitignore
2
+ .pre-commit-config.yaml
2
3
  CONTRIBUTING.md
3
4
  EASYCLA_CHANGEME.md
4
5
  LICENSE
@@ -18,12 +19,15 @@ safety-policy.yml
18
19
  .github/ISSUE_TEMPLATE/Support_question.md
19
20
  .github/ISSUE_TEMPLATE/meeting-minutes.md
20
21
  .github/workflows/build-python-package.yml
22
+ .github/workflows/check-license-headers.yml
21
23
  .github/workflows/cve-scanning-python.yml
22
24
  .github/workflows/license-scanning-python.yml
23
25
  .github/workflows/release.yml
24
26
  .github/workflows/run-tests.yml
25
27
  .vscode/settings.json
26
28
  docs/home.md
29
+ src/license/apply_headers.sh
30
+ src/license/header.txt
27
31
  src/rune/runtime/__init__.py
28
32
  src/rune/runtime/base_data_class.py
29
33
  src/rune/runtime/conditions.py
@@ -7,3 +7,5 @@ pytest>=9.0.2
7
7
  pytest-cov>=7.0.0
8
8
  pytest-mock>=3.14.1
9
9
  types-python-dateutil>=2.9.0.20250809
10
+ licenseheaders>=0.8.8
11
+ pre-commit>=3.0.0
@@ -6,11 +6,13 @@
6
6
  ".github/ISSUE_TEMPLATE/Support_question.md",
7
7
  ".github/ISSUE_TEMPLATE/meeting-minutes.md",
8
8
  ".github/workflows/build-python-package.yml",
9
+ ".github/workflows/check-license-headers.yml",
9
10
  ".github/workflows/cve-scanning-python.yml",
10
11
  ".github/workflows/license-scanning-python.yml",
11
12
  ".github/workflows/release.yml",
12
13
  ".github/workflows/run-tests.yml",
13
14
  ".gitignore",
15
+ ".pre-commit-config.yaml",
14
16
  ".vscode/settings.json",
15
17
  "CONTRIBUTING.md",
16
18
  "EASYCLA_CHANGEME.md",
@@ -26,6 +28,8 @@
26
28
  "pyproject.toml",
27
29
  "renovate.json",
28
30
  "safety-policy.yml",
31
+ "src/license/apply_headers.sh",
32
+ "src/license/header.txt",
29
33
  "src/rune/runtime/__init__.py",
30
34
  "src/rune/runtime/base_data_class.py",
31
35
  "src/rune/runtime/conditions.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.2.2",
3
+ "distance": 0,
4
+ "node": "g951f0332ce7c55a8a5e4d1b6bf932f809d83c5be",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-15"
8
+ }
@@ -0,0 +1,2 @@
1
+ license
2
+ rune
@@ -235,7 +235,7 @@ def test_annotated_date_fail():
235
235
 
236
236
  def test_date_with_meta_fail():
237
237
  '''test instantiation failure with an incorrect type'''
238
- with pytest.raises(ValueError):
238
+ with pytest.raises(TypeError):
239
239
  DateWithMeta(10)
240
240
 
241
241
 
@@ -1,4 +1,5 @@
1
1
  '''test key generation/retrieval runtime functions'''
2
+ import json
2
3
  from decimal import Decimal
3
4
  from typing_extensions import Annotated
4
5
  import pytest
@@ -62,6 +63,23 @@ class DummyLoan2(BaseDataClass):
62
63
  'repayment': {'@ref', '@ref:external'}
63
64
  }
64
65
 
66
+
67
+ class NamedLoan(DummyLoan2):
68
+ '''Inherit both reference fields without redeclaring their annotations.'''
69
+ label: str = 'loan'
70
+
71
+
72
+ class ExtendedNamedLoan(NamedLoan):
73
+ '''Reference fields can be inherited through multiple levels.'''
74
+ pass
75
+
76
+
77
+ class LoanBook(BaseDataClass):
78
+ '''Key targets and inherited references in separate list fields.'''
79
+ cashflows: list[CashFlow]
80
+ loans: list[NamedLoan]
81
+
82
+
65
83
  class DummyLoan3(BaseDataClass):
66
84
  '''number test class'''
67
85
  loan: Annotated[NumberWithMeta,
@@ -220,6 +238,38 @@ def test_ref_assign():
220
238
  assert id(model.loan) == id(model.repayment)
221
239
 
222
240
 
241
+ @pytest.mark.parametrize('model_type', [NamedLoan, ExtendedNamedLoan])
242
+ def test_ref_assign_to_inherited_field(model_type):
243
+ '''Inherited reference fields retain their declared target type.'''
244
+ model = model_type(loan=CashFlow(currency='EUR', amount=100),
245
+ repayment=CashFlow(currency='EUR', amount=101))
246
+
247
+ model.repayment = Reference(model.loan)
248
+
249
+ assert model.repayment is model.loan
250
+ assert model.resolve_ref_key('repayment') == model.loan.get_meta('@key')
251
+
252
+
253
+ @pytest.mark.parametrize('validate_model', [False, True])
254
+ def test_deserialize_inherited_references_in_lists(validate_model):
255
+ '''Inherited references resolve before optional model validation.'''
256
+ data = json.dumps({
257
+ 'cashflows': [{
258
+ '@key:external': 'cashflow1', 'currency': 'EUR', 'amount': '100',
259
+ }],
260
+ 'loans': [{
261
+ 'loan': {'@ref:external': 'cashflow1'},
262
+ 'repayment': {'@ref:external': 'cashflow1'},
263
+ }],
264
+ })
265
+
266
+ book = LoanBook.rune_deserialize(data, validate_model=validate_model)
267
+
268
+ assert book.loans[0].loan is book.cashflows[0]
269
+ assert book.loans[0].repayment is book.cashflows[0]
270
+ assert book.loans[0].resolve_ref_key('repayment') == 'cashflow1'
271
+
272
+
223
273
  def test_ref_assign_from_cow_wrapped_object():
224
274
  '''test use a ref from a COW-wrapped object'''
225
275
  model = DummyLoan2(loan=CashFlow(currency='EUR', amount=100),
@@ -445,13 +495,17 @@ def test_reference_non_metadata_target_with_ext_key_raises():
445
495
  Reference('not_an_object', 'ext_key')
446
496
 
447
497
 
448
- def test_bind_property_rejects_wrong_type():
498
+ @pytest.mark.parametrize('model_type', [DummyLoan2, NamedLoan, ExtendedNamedLoan])
499
+ def test_bind_property_rejects_wrong_type(model_type):
449
500
  '''reject refs that don't match field type'''
450
- model = DummyLoan2(loan=CashFlow(currency='EUR', amount=100),
501
+ model = model_type(loan=CashFlow(currency='EUR', amount=100),
451
502
  repayment=CashFlow(currency='EUR', amount=101))
503
+ original_repayment = model.repayment
452
504
  other = OtherThing(name='other')
453
- with pytest.raises(ValueError):
505
+ with pytest.raises(TypeError, match='Incompatible types'):
454
506
  model.repayment = Reference(other)
507
+ assert model.repayment is original_repayment
508
+ assert model.resolve_ref_key('repayment') is None
455
509
 
456
510
 
457
511
  def test_bind_property_rejects_non_replaceable():
@@ -1,2 +0,0 @@
1
- [build]
2
- ignore = "/bin/false"
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.2.1",
3
- "distance": 0,
4
- "node": "g05cabe4820d4659485de75d071c296b38c2e4d45",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-09-16"
8
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes