Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
fe61f19
Merge pull request #138 from finos/chore/remove-trailing-spaces
dschwartznyc Jan 22, 2026
57ebabb
feat: Refactor Python object generation to accept a shared dependency…
dschwartz-ftadvisory Jan 27, 2026
0537325
refactor: Introduce PythonCodeGeneratorContext to manage per-namespac…
dschwartz-ftadvisory Jan 27, 2026
2630c9c
Refactor: Pass `PythonCodeGeneratorContext` object to `PythonModelObj…
dschwartz-ftadvisory Jan 27, 2026
0afdc84
refactor: use enumImports in PythonGeneratorContext in Attribute and …
dschwartz-ftadvisory Jan 27, 2026
9a3c57e
Refactor function generation to include enum imports and integrate ge…
dschwartz-ftadvisory Jan 28, 2026
87a508d
refactor: move create fully qualified and create bundle name function…
dschwartz-ftadvisory Jan 28, 2026
449da27
feat: Implement Python function generation with runtime module attrib…
dschwartz-ftadvisory Jan 28, 2026
0078908
Enhance Python environment setup script
dschwartznyc Jan 29, 2026
da3cf65
feat: Add support for functions referencing types, refactor expressio…
dschwartz-ftadvisory Jan 29, 2026
81916cd
refactor: Move object name generation utilities to `RuneToPythonMappe…
dschwartz-ftadvisory Jan 30, 2026
7f6473e
feat: Implement support for custom Rosetta types in Python function i…
dschwartz-ftadvisory Jan 30, 2026
3bafffd
feat: Improve Python generation for Rosetta enumerations and add a ne…
dschwartz-ftadvisory Jan 30, 2026
3ca7b20
Remove `sys.modules` class guardian from Python test expectation
dschwartz-ftadvisory Jan 30, 2026
0598416
Refactor: Renamed two Python function generation test methods
dschwartz-ftadvisory Jan 30, 2026
0ce4554
refactor: rename Python function test methods
dschwartz-ftadvisory Jan 30, 2026
fe798c1
refactor: Centralize Python type generation, including cardinality an…
dschwartz-ftadvisory Jan 30, 2026
c8d3d31
feat: Add JUnit and Python unit tests for Rosetta function aliases, i…
dschwartz-ftadvisory Feb 2, 2026
da9b33c
feat: Implement condition generation for Python functions
dschwartz-ftadvisory Feb 3, 2026
fe9c33b
feat: add JUunit and Python unit tests for Simple and Post condtions …
dschwartz-ftadvisory Feb 4, 2026
f73c158
feat: Add support for functions calling other functions, introduce en…
dschwartz-ftadvisory Feb 4, 2026
88d4637
feat: Add option to reuse Python virtual environments and standardize…
dschwartz-ftadvisory Feb 4, 2026
1b81942
fix: complete venv path standardization and update function tests
dschwartz-ftadvisory Feb 4, 2026
6b6c0f3
chore: Remove hardcoded local path for RUNE_RUNTIME_FILE.
dschwartz-ftadvisory Feb 4, 2026
8d7e0f7
feat: Add `FilterQuantity` Rosetta DSL definition and its Python unit…
dschwartz-ftadvisory Feb 4, 2026
b0d304a
feat: Refine quantity filtering condition in Rosetta
dschwartz-ftadvisory Feb 4, 2026
2211374
feat: Implement and test metadata key and reference entities with a n…
dschwartz-ftadvisory Feb 4, 2026
2571832
feat: Enhance Python function generation with `RosettaTypeAlias` supp…
dschwartz-ftadvisory Feb 4, 2026
4c08a60
refactor: Improve Python executable discovery in shell scripts and up…
dschwartz-ftadvisory Feb 4, 2026
830f312
refactor: Split `test_functions.py` into multiple granular test files…
dschwartz-ftadvisory Feb 4, 2026
a3688e4
Refactor: Update metadata test to use `IntWithMeta`.
dschwartz-ftadvisory Feb 4, 2026
fb58cab
refactor: Adjust KeyEntity metadata assignment in tests
dschwartz-ftadvisory Feb 5, 2026
4eb1fb3
Refactor Rosetta and Python unit tests by deleting individual operato…
dschwartz-ftadvisory Feb 5, 2026
e430343
Refactor Python unit tests to organize them into a Hybrid Feature-Cen…
dschwartz-ftadvisory Feb 5, 2026
2cb76c7
feat: Add a Rosetta function and Python test for incomplete object re…
dschwartz-ftadvisory Feb 5, 2026
8eda4aa
feat: Enhance Python generator for object creation in functions, incl…
dschwartz-ftadvisory Feb 5, 2026
9fd0ad4
feat: Add Python generation for Rosetta Choice types and address mult…
dschwartz-ftadvisory Feb 6, 2026
63c0594
chore: Apply CLI robustness, POM fixes, and updated Docs from advance…
dschwartz-ftadvisory Feb 7, 2026
8a74ff2
feat: update two Rosetta test input files
dschwartz-ftadvisory Feb 7, 2026
ebb0cbb
fix: Update .gitignore to exclude build directory and update Rosetta …
dschwartz-ftadvisory Feb 7, 2026
c05dcd8
add bacj head_content.rosetta
dschwartz-ftadvisory Feb 7, 2026
17e33ae
feat: Fix `join` operator code generation, enhance CLI validation log…
dschwartz-ftadvisory Feb 7, 2026
9786297
refactor: Decompose Python expression generator tests into granular, …
dschwartz-ftadvisory Feb 7, 2026
39a8047
feat: Implement generation for nested filter map count, list comparis…
dschwartz-ftadvisory Feb 7, 2026
c58aab7
feat: Introduce new Python generator tests for basic types, inheritan…
dschwartz-ftadvisory Feb 7, 2026
1144a70
feat: Add Rosetta definitions and Python unit tests covering inherita…
dschwartz-ftadvisory Feb 7, 2026
4efb662
feat: Implement the `reverse` list operator, rename list function par…
dschwartz-ftadvisory Feb 7, 2026
e3648be
refactor: split Python function generation tests into dedicated class…
dschwartz-ftadvisory Feb 7, 2026
2fb276d
Refactor Python function generation by removing dedicated if-block ge…
dschwartz-ftadvisory Feb 7, 2026
6697f5f
fix: Strictly map Rune `number` to Python `Decimal` for literals and …
dschwartz-ftadvisory Feb 8, 2026
4878326
feat: enhance function dependency tracking, add optional string forwa…
dschwartz-ftadvisory Feb 8, 2026
13d903f
docs: Update generation issues documentation with resolved problems a…
dschwartz-ftadvisory Feb 8, 2026
4d394c5
feat: implement Python code generation for Rosetta switch expressions…
dschwartz-ftadvisory Feb 8, 2026
7bfd39f
chore: updated documentation on dev issues and their resolution
dschwartz-ftadvisory Feb 8, 2026
b813ff0
fix: Improve Python code generation order by enhancing dependency gra…
dschwartz-ftadvisory Feb 8, 2026
89210db
docs: Document robust dependency management and enum metadata fixes, …
dschwartz-ftadvisory Feb 8, 2026
d179f4a
doc: update with issues for externally defined data sources
dschwartz-ftadvisory Feb 8, 2026
4248283
feat: Refactor CLI to return exit codes and add options for validatio…
dschwartz-ftadvisory Feb 8, 2026
31149c3
Merge branch 'main' into feature/cli-refactor-tests
dschwartznyc Feb 19, 2026
ed25877
Merge branch 'main' into feature/cli-refactor-tests
dschwartznyc Feb 19, 2026
712ec5b
fix: updated JUnit and Python Unit tests to not use Functions since t…
dschwartz-ftadvisory Feb 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ __pycache__
**/test/cdm_tests/common-domain-model/
src/generated/
*.todo
/build/
157 changes: 157 additions & 0 deletions docs/FUNCTION_SUPPORT_DEV_ISSUES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Development Documentation: Function Support Dev Issues

## Resolved Issues

### 1. Inconsistent Numeric Types (Literals and Constraints)
Rune `number` and `int` were handled inconsistently, leading to precision loss and `TypeError` when interacting with Python `Decimal` fields in financial models.

* **Resolution**: Strictly mapped Rune `number` to Python `Decimal`. Literals and constraints are now explicitly wrapped in `Decimal('...')` during generation.
* **Status**: Fixed ([`fe798c1`](https://github.com/finos/rune-python-generator/commit/fe798c1))
* **Summary of Impact**:
* **Precision**: Ensures that calculations involving monetary amounts maintain exact precision, avoiding the pitfalls of floating-point arithmetic.
* **Type Safety**: Prevents runtime crashes when Pydantic models expect `Decimal` but receive `float` or `int`.

### 2. Redundant Logic Generation
Expression logic was previously duplicated between different generator components, leading to maintenance overhead and potential diverging implementations of the same DSL logic.

* **Resolution**: Centralized all expression logic within `PythonExpressionGenerator`. Removed `generateIfBlocks` from the higher-level function generator to prevent duplicate emission of conditional statements.
* **Status**: Fixed ([`2fb276d`](https://github.com/finos/rune-python-generator/commit/2fb276d))
* **Summary of Impact**:
* **Maintainability**: Logic changes (like the Switch fix) only need to be implemented in one place.
* **Code Quality**: The generated Python is cleaner and follows a predictable pattern for side-effecting blocks.

### 3. Missing Function Dependencies (Recursion & Enums)
The dependency provider failed to identify imports for types deeply nested in expressions or those referenced via `REnumType`.

* **Resolution**: Implemented recursive tree traversal in `PythonFunctionDependencyProvider` and added explicit handling for Enum types to ensure they are captured in the import list.
* **Status**: Fixed ([`4878326`](https://github.com/finos/rune-python-generator/commit/4878326))
* **Summary of Impact**:
* **Runtime Stability**: Resolves `NameError` exceptions in generated code where functions used types that were not imported at the top of the module.
* **Enum Integration**: Functions can now safely use Rosetta-defined enums in conditions and assignments.

### 4. Robust Dependency Management (DAG Population)
The generator's dependency graph (DAG) previously failed to capture attribute-level dependencies and function-internal dependencies, leading to `NameError` exceptions when a class or function was defined after it was referenced.

* **Resolution**: Implemented recursive dependency extraction in `PythonModelObjectGenerator` and `PythonFunctionGenerator`. The DAG now includes edges for all class attributes, function inputs/outputs, and symbol references, guaranteeing a topologically correct definition order in `_bundle.py` for acyclic dependencies.
* **Status**: Fixed ([`b813ff0`](https://github.com/finos/rune-python-generator/commit/b813ff0))
* **Verification**: `PythonFunctionOrderTest` (Java) confirms strict ordering for linear dependencies.
* **Summary of Impact**:
* **Generation Stability**: Eliminates `NameError` causing build failures.
* **Correctness**: Ensures that the generated Python code adheres to the "define-before-use" principle required by the language.

### 5. Mapping of [metadata id] for Enums
Enums with metadata constraints failed to support validation and key referencing because they were generated as plain Python `Enum` classes, which cannot carry metadata payloads or validators.

* **Resolution**: Enums with explicit metadata (e.g., `[metadata id]`, `[metadata reference]`) are now wrapped in `Annotated[Enum, serializer(), validator()]`. The `serializer` and `validator` methods are provided by the `EnumWithMetaMixin` runtime class.
* **Status**: Fixed ([`b813ff0`](https://github.com/finos/rune-python-generator/commit/b813ff0))
* **Summary of Impact**:
* **Feature Parity**: Brings Enums up to par with complex types regarding metadata support.
* **Validation**: Enables `@key` and `@ref` validation for Enum fields.

### 6. Inconsistent Type Mapping (Centralization)
Type string generation was scattered across multiple classes, making it impossible to implement global features like string forward-referencing or custom cardinality formatting.

* **Resolution**: Centralized type mapping and formatting in `RuneToPythonMapper`, adding a flexible `formatPythonType` method that handles both legacy and modern typing styles.
* **Status**: Fixed ([`fe798c1`](https://github.com/finos/rune-python-generator/commit/fe798c1))
* **Summary of Impact**:
* **Extensibility**: Enabled the groundwork for "String Forward References".
* **Cardinality Control**: Standardized how `list[...]` and `Optional[...]` are generated.

### 7. Switch Expression Support
`generateSwitchOperation` returned a block of statements instead of a single expression, causing `SyntaxError` during variable assignment.

* **Resolution**: Encapsulated switch logic within a unique helper function closure (`_switch_fn_0`) and returned a call to that function.
* **Status**: Fixed ([`4d394c5`](https://github.com/finos/rune-python-generator/commit/4d394c5))

---

## Unresolved Issues and Proposals

### 1. Constructor-Related Issues

#### Issue: Fragile Object Building (Direct Constructors)
**Problem**: The generator relies on a magic `_get_rune_object` helper which bypasses IDE checks and is hard to debug.
* **Recommendation**: Refactor `PythonFunctionGenerator` to use direct Python constructor calls (e.g., `MyClass(attr=val)`).
* **Status**: **Unresolved**. The codebase currently uses `_get_rune_object`.

#### Issue: Constructor Keyword Arguments SyntaxError
**Problem**: Python forbids duplicate or invalid keyword arguments.
* **Recommendation**: Use unique counters for missing/duplicate keys.
* **Proposed Fix**: The generator should use unique fallback keys (`unknown_0`, `unknown_1`, etc.) when property names are missing or invalid.
* **Recommended Code Changes**: Use an `AtomicInteger` for unique fallback keys in `PythonExpressionGenerator.java`.

#### Issue: Partial Object Construction (Required Fields)
**Problem**: Pydantic's default constructor enforces validation immediately, breaking multi-step `set` operations.
* **Recommendation**: Use `model_construct()`.
* **Proposed Solution**: Use `model_construct(**kwargs)` for initial object creation to skip validation, allowing the object to be filled via subsequent `set` calls before final consumption.

---

### 2. Bundle Generation and Dependency Issues

#### Issue: Circular Dependencies / Out-of-Order Definitions (The "Topological Sort" Limitation)
**Manifestation**: `NameError: name 'cdm_base_datetime_BusinessCenterTime' is not defined` during CDM import.

**Problem**: The generator uses a Directed Acyclic Graph (DAG) to order definitions in `_bundle.py`. However, the current implementation only adds edges for **inheritance (SuperTypes)**. It ignores **Attribute types**, leading to out-of-order definitions. Furthermore, Rosetta allows recursive/circular types (e.g., A has attribute B, B has attribute A), which a DAG cannot resolve by design.

**Reproducing Tests**:
* **JUnit**: `PythonFunctionOrderTest.testFunctionDependencyOrder` (asserts ClassA defined before ClassB).
* **Python**: `test_functions_order.py` (triggers NameError during Pydantic decorator execution).

**Proposed Alternatives & Recommendation**:
Use **String Forward References + `model_rebuild()`** (The official "Pydantic Way" for v2).
* **The Hybrid DAG Strategy**: We will continue to use the DAG to organize the definition order of classes, but we will limit its scope to **Inheritance only** (`SuperType`). By using String Forward References for attributes, we eliminate the need for the DAG to handle the complex "web" of references, avoiding cycle detection failures while ensuring that parent classes are always defined before their children.

#### Issue: FQN Type Hints for Clean API (Dots vs. Underscores)
**Problem**: The current generator uses "bundle-mangled" names with underscores (e.g., `cdm_base_datetime_AdjustableDate`) in function and constructor signatures to avoid collisions.

**Proposal**: Use fully qualified, period-delimited names (e.g., `"cdm.base.datetime.AdjustableDate"`) in all signatures.
* **Mechanism**: Utilize a `_type_registry` mapping at the end of the `_bundle.py` that links Rosetta FQNs to the bundled Python class definitions.
* **Dependency**: This approach **requires** implementing the **String Forward Reference** solution for circular dependencies.
* **Benefits**: API Purity (matches CDM/Rosetta names exactly), Consistency, and Encapsulation.

#### Issue: Bundle Loading Performance (Implicit Overhead)
**Problem**: The current "Bundle" architecture results in significant "load-all-at-once" overhead for large models like CDM.

**Proposal**: Evolve the bundle architecture to support **Partitioned Loading** or **Lazy Rebuilds**.
* **Status**: **Unresolved**. Prerequisite is the fix for Circular Dependencies via String Forward References.

---

---

### 3. Support for External Data Sources

#### Issue: Unmapped External Function Calls (`[codeImplementation]`)
**Problem**: The Rosetta runtime allows functions to be marked with `[codeImplementation]`, indicating logic provided by the host language (e.g., loading XML codelists in Java). The Python generator does not yet emit the syntax to delegate these calls to Python external implementations.
* **Manifestation**: Validation functions like `ValidateFpMLCodingSchemeDomain` are either omitted or generate empty/invalid bodies.
* **Recommendation**: Update the generator to emit calls to a standard Python registry/dispatcher for external functions.

#### Issue: Conditions on Basic Types (Strings)
**Problem**: The DSL allows attaching validation logic directly to basic types (e.g., `typeAlias BusinessCenter: string condition IsValid...`). This feature, introduced to support data validation against external sources, is not supported in the Python generator.
* **Gap**: The generator may not correctly wrap simple types to attach validators or trigger validation upon assignment.
* **Status**: **Unresolved Gap**.

#### Issue: Missing Standard Library for External Data
**Problem**: The CDM Python implementation lacks the infrastructure to replicate the Java version's ability to load external data (e.g., FpML coding schemes) and validate values against them.
* **Impact**: Even if the generator called the validation function, the runtime mechanism to perform the check does not exist.
* **Recommendation**: Implement a Python equivalent of the Java `CodelistLoader` and expose it via the runtime library.

---

## Backlog

### Enum Wrappers (Global Proposal)
* **Problem**: While explicit metadata is now supported via `Annotated`, plain Enums do not have a uniform wrapper object at runtime. This leads to inconsistent behavior if code expects to attach metadata dynamically to any enum instance.
* **Proposal**: Wrap *all* enums in a proxy class (`_EnumWrapper`) that holds the enum value and a metadata dictionary.
* **Relevant Tests**:
* Java: `PythonEnumMetadataTest.testEnumWithoutMetadata` (Disabled)
* Python: `test_enum_metadata.py::test_enum_metadata_behavior` (Skipped)
* **Usage Note**: This is a proposed architectural change, not a defect fix.

---

### General Support Suggestions

* **Type Unification Support**: Evaluate if `BaseDataClass` inheriting from a metadata mixin provides a scalable way to handle metadata across various model sizes.
* **Operator Support**: Consider standardizing helper functions like `rune_with_meta` and `rune_default` to simplify generated code.
101 changes: 101 additions & 0 deletions head_content.rosetta
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
namespace rosetta_dsl.test.semantic.collections : <"generate Python unit tests from Rosetta.">

type CountItem:
name string (0..1)
value int (0..1)

type CountContainer:
field1 int (0..*)
field2 CountItem (0..*)

type CountTest:
bValue CountContainer (1..*)
condition TestCond:
if bValue -> field2 count > 0
then True
else False

type SumTest:
aValue int (1..1)
bValue int (1..1)
target int (1..1)
condition TestCond:
if [aValue, bValue] sum = target
then True
else False

type MinTest:
a int (1..1)
condition Test:
if [a, 1] min = 1
then True
else False

type MaxTest:
a int (1..1)
condition Test:
if [a, 1, 20] max = 20
then True
else False

type LastTest:
aValue int (1..1)
bValue int (1..1)
cValue int (1..1)
target int (1..1)
condition TestCond:
if [aValue, bValue, cValue] last = target
then True
else False

type SortTest:
condition Test:
if [3, 2, 1] sort first = 1
then True
else False

type JoinTest:
field1 string (1..1)
field2 string (1..1)
condition TestCond:
if [field1, field2] join "" = "ab"
then True
else False

type FilterItem:
fi int (1..1)

type FilterTest:
fis FilterItem (1..*)
target int (1..1)
condition TestCondFilter:
if [fis filter i [i->fi = target]] count = 1
then True
else False

type FlattenItem:
field1 int (1..*)

type FlattenContainer:
fieldList FlattenItem (1..*)

type FlattenBar:
numbers int (0..*)

type FlattenFoo:
bars FlattenBar (0..*)
condition TestCondFoo:
[1, 2, 3] = (bars
extract numbers
then flatten)

func FlattenTest: <"Test flatten operation in a condition">
inputs:
bValue FlattenContainer (1..*) <"Test value">
output:
result int (1..*)
set result:
bValue
extract fieldList
then extract field1
then flatten
6 changes: 5 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,11 @@
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>plugin.properties</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.regnosys.rosetta.generator.python.PythonCodeGenCLI</mainClass>
<mainClass>com.regnosys.rosetta.generator.python.PythonCodeGeneratorCLI</mainClass>
</transformer>
</transformers>
<filters>
Expand Down Expand Up @@ -475,6 +478,7 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration combine.self="override">
<doclint>none</doclint>
<failOnError>false</failOnError>
</configuration>
<executions>
Expand Down
Loading