Skip to content

Commit cb8fad1

Browse files
feat: Add support for native Python function implementations and integrate them into the test suite.
1 parent 6b4fde4 commit cb8fad1

File tree

15 files changed

+189
-35
lines changed

15 files changed

+189
-35
lines changed

src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGenerator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,12 @@ private Map<String, CharSequence> processDAG(String nameSpace,
313313
}
314314

315315
Set<String> nativeFunctionNames = context.getNativeFunctionNames();
316+
316317
if (!nativeFunctionNames.isEmpty()) {
317318
bundleWriter.newLine();
318319
bundleWriter.appendLine("rune_attempt_register_native_functions(");
319320
bundleWriter.indent();
320-
bundleWriter.appendLine("native_functions=[");
321+
bundleWriter.appendLine("function_names=[");
321322
bundleWriter.indent();
322323
for (String nativeFunctionName : nativeFunctionNames) {
323324
bundleWriter.appendLine("'" + nativeFunctionName + "',");
@@ -327,7 +328,6 @@ private Map<String, CharSequence> processDAG(String nameSpace,
327328
bundleWriter.unindent();
328329
bundleWriter.appendLine(")");
329330
}
330-
331331
bundleWriter.newLine();
332332
bundleWriter.newLine();
333333
bundleWriter.appendLine("# EOF");

src/main/java/com/regnosys/rosetta/generator/python/PythonCodeGeneratorContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ public void addAdditionalImport(String importStatement) {
111111
}
112112
}
113113

114+
public boolean hasNativeFunctions() {
115+
return !nativeFunctionNames.isEmpty();
116+
}
114117
public void addNativeFunctionName(String nativeFunctionName) {
115118
this.nativeFunctionNames.add(nativeFunctionName);
116119
}

src/main/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionGenerator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,9 @@ private String generateOperations(Function function, PythonCodeGeneratorContext
482482
}
483483
if (scope.hasObjectBuilders()) {
484484
context.addAdditionalImport("from rune.runtime.object_builder import ObjectBuilder");
485+
if (context.hasNativeFunctions()) {
486+
context.addAdditionalImport("from rune.runtime.native_registry import rune_attempt_register_native_functions");
487+
}
485488
for (String setName : scope.getObjectBuilderNames()) {
486489
writer.appendLine(setName + " = " + setName + ".to_model()");
487490
}

src/test/java/com/regnosys/rosetta/generator/python/functions/PythonFunctionExternalTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def condition_0_PositiveNearest():
7373

7474
String registrationExpected = """
7575
rune_attempt_register_native_functions(
76-
native_functions=[
76+
function_names=[
7777
'rosetta_dsl.test.functions.functions.RoundToNearest',
7878
]
7979
)

test/python_setup/setup_python_env.sh

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,38 @@
33
function error {
44
echo
55
echo "***************************************************************************"
6-
echo "* *"
7-
echo "* DEV ENV Initialization FAILED! *"
8-
echo "* *"
6+
echo "* *"
7+
echo "* DEV ENV Initialization FAILED! *"
8+
echo "* *"
99
echo "***************************************************************************"
1010
echo
1111
exit 1
1212
}
1313

1414
# Determine the Python executable
15-
if command -v python &>/dev/null; then
16-
PYEXE=python
17-
elif command -v python3 &>/dev/null; then
18-
PYEXE=python3
15+
# IMPORTANT: Find a python that is NOT currently inside a virtual environment we might be destroying
16+
if command -v python3 &>/dev/null && ! command -v python3 | grep -q ".pyenv"; then
17+
PYEXE=$(command -v python3)
18+
elif command -v python &>/dev/null && ! command -v python | grep -q ".pyenv"; then
19+
PYEXE=$(command -v python)
1920
else
21+
# Fallback to whatever is available but try to avoid the one in .pyenv if possible
22+
PYEXE=$(which -a python3 python | grep -v ".pyenv" | head -n 1)
23+
if [ -z "$PYEXE" ]; then
24+
PYEXE=python3
25+
if ! command -v $PYEXE &>/dev/null; then
26+
PYEXE=python
27+
fi
28+
fi
29+
fi
30+
31+
if ! command -v $PYEXE &>/dev/null; then
2032
echo "Python is not installed."
2133
error
2234
fi
2335

36+
echo "Using base Python: $PYEXE"
37+
2438
# Check Python version
2539
if ! $PYEXE -c 'import sys; assert sys.version_info >= (3,11)' > /dev/null 2>&1; then
2640
echo "Found $($PYEXE -V)"
@@ -74,29 +88,31 @@ ${PYEXE} -m pip install -r requirements.txt || error
7488
echo "***** Get and Install Runtime"
7589

7690

77-
export RUNE_RUNTIME_DIR=$(dirname "$RUNE_RUNTIME_FILE")
91+
# Set RUNE_RUNTIME_DIR to the local repository root for an editable install.
92+
# Example: RUNE_RUNTIME_DIR="[PATH_TO_RUNE]/rune-python-runtime/FINOS/rune-python-runtime"
93+
# If RUNE_RUNTIME_DIR is not specified or the directory does not exist, it will pull from the GitHub repo.
7894

79-
if [ -n "$RUNE_RUNTIME_FILE" ]; then
80-
# --- Local Installation Logic ---
81-
echo "Using local runtime source: $RUNE_RUNTIME_FILE"
82-
if [ -f "$RUNE_RUNTIME_FILE" ]; then
83-
${PYEXE} -m pip install "$RUNE_RUNTIME_FILE" --force-reinstall || error
84-
else
85-
echo "Error: Local file $RUNE_RUNTIME_FILE not found."
86-
error
87-
fi
95+
if [ -n "$RUNE_RUNTIME_DIR" ] && [ -d "$RUNE_RUNTIME_DIR" ]; then
96+
echo "Installing runtime as editable from: $RUNE_RUNTIME_DIR"
97+
${PYEXE} -m pip install -e "$RUNE_RUNTIME_DIR" || error
8898
else
8999
# --- Remote Repository Logic ---
90-
echo "No local source provided. Pulling from repo..."
100+
echo "RUNE_RUNTIME_DIR not specified or not found. Pulling from GitHub repo..."
91101
RUNTIMEURL="https://api.github.com/repos/finos/rune-python-runtime/releases/latest"
92102

93103
release_data=$(curl -s $RUNTIMEURL)
94104
download_url=$(echo "$release_data" | grep '"browser_download_url":' | head -n 1 | sed -E 's/.*"([^"]+)".*/\1/')
95105

106+
if [ -z "$download_url" ] || [ "$download_url" == "null" ]; then
107+
echo "Error: Could not determine download URL for the latest runtime release."
108+
error
109+
fi
110+
111+
echo "Downloading latest runtime from: $download_url"
96112
if command -v wget &>/dev/null; then
97-
wget "$download_url"
113+
wget -q "$download_url"
98114
elif command -v curl &>/dev/null; then
99-
curl -LO "$download_url"
115+
curl -sLO "$download_url"
100116
else
101117
echo "Neither wget nor curl is installed."
102118
error
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace rosetta_dsl.test.functions
2+
3+
enum RoundingModeEnum:
4+
Down
5+
Up
6+
7+
func RoundToNearest: <"Round a number to the supplied nearest, using the supplied rounding mode.">
8+
[codeImplementation]
9+
inputs:
10+
value number (1..1) <"The original (unrounded) number.">
11+
digits int (1..1) <"The number of digits to round to.">
12+
roundingMode RoundingModeEnum (1..1) <"The method of rounding (up to nearest/down to nearest.">
13+
output:
14+
roundedValue number (1..1)
15+
condition PositiveDigits:
16+
digits > 0
17+
18+
func RoundDown: <"Round a number down to the supplied nearest.">
19+
inputs:
20+
value number (1..1) <"The original (unrounded) number.">
21+
digits int (1..1) <"The number of digits to round to.">
22+
output:
23+
roundedValue number (1..1)
24+
set roundedValue:
25+
RoundToNearest(value, digits, RoundingModeEnum -> Down)
26+
27+
func RoundUp: <"Round a number up to the supplied nearest.">
28+
inputs:
29+
value number (1..1) <"The original (unrounded) number.">
30+
digits int (1..1) <"The number of digits to round to.">
31+
output:
32+
roundedValue number (1..1)
33+
set roundedValue:
34+
RoundToNearest(value, digits, RoundingModeEnum -> Up)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Unit tests for the RoundToNearest function.
3+
"""
4+
5+
from decimal import Decimal
6+
from rosetta_dsl.test.functions.functions.RoundUp import RoundUp
7+
from rosetta_dsl.test.functions.functions.RoundDown import RoundDown
8+
9+
10+
def test_round_to_nearest_down():
11+
"""Test rounding down to 2 decimal places."""
12+
value = Decimal("1.2345")
13+
digits = Decimal("2")
14+
15+
result = RoundDown(value, digits)
16+
assert result == Decimal("1.23")
17+
18+
19+
def test_round_to_nearest_up():
20+
"""Test rounding down to 2 decimal places."""
21+
value = Decimal("1.2345")
22+
digits = Decimal("2")
23+
24+
result = RoundUp(value, digits)
25+
assert result == Decimal("1.24")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# Build and install the native implementation package in editable mode
3+
4+
set -e
5+
6+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
7+
cd "$SCRIPT_DIR"
8+
9+
echo "Building and installing native implementation package from $SCRIPT_DIR..."
10+
# Debug: list files to ensure pyproject.toml is visible
11+
ls -F
12+
python -m pip install -e "$SCRIPT_DIR"
13+
14+
echo "Done. Installed from $SCRIPT_DIR"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[build-system]
2+
requires = ["setuptools>=62.0"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "rosetta-dsl-native-functions"
7+
version = "0.1.0"
8+
description = "Native Python implementations for Rosetta functions"
9+
dependencies = []
10+
11+
[tool.setuptools.packages.find]
12+
where = ["src"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Namespace package marker

0 commit comments

Comments
 (0)