diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1fc8d4ee..a96696ac 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -448,3 +448,84 @@ jobs:
- name: Publish Packages
working-directory: interop/csharp/
run: dotnet nuget push RadixDlt.RadixEngineToolkit.*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }}
+ publish-python-package:
+ needs: [build, generate-uniffi-bindings]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ packages: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Fetch secrets
+ uses: radixdlt/public-iac-resuable-artifacts/fetch-secrets@main
+ with:
+ role_name: ${{ secrets.PYPI_SECRET_ROLE_NAME }}
+ app_name: 'radix-engine-toolkit'
+ step_name: 'pypi-credentials'
+ secret_prefix: 'PYPI'
+ secret_name: ${{ secrets.PYPI_SECRET_NAME }}
+ parse_json: true
+ - name: Print Env
+ run: env
+ - uses: actions/download-artifact@v3
+ with:
+ path: artifacts
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.11
+ - name: Install Dependencies
+ run: |
+ python3 -m pip install black build twine
+ sudo apt-get install mypy
+ - name: Copy bindings
+ run: |
+ mkdir ./interop/python/radix_engine_toolkit
+ cp \
+ ./artifacts/uniffi-bindings/radix_engine_toolkit_uniffi.py \
+ ./interop/python/radix_engine_toolkit/__init__.py
+ - name: Dynamic Library Script replacement
+ run: |
+ python3 ./interop/python/replacement.py ./interop/python/radix_engine_toolkit/__init__.py
+ rm ./interop/python/replacement.py
+ - name: Stubs generation
+ run: |
+ stubgen \
+ ./interop/python/radix_engine_toolkit/__init__.py \
+ --output ./interop/python/
+ - name: Code Formatting
+ run: |
+ black ./interop/python/
+ - name: Copy Dynamic Libraries
+ run: |
+ cp \
+ ./artifacts/radix-engine-toolkit-uniffi-aarch64-apple-darwin/libradix_engine_toolkit_uniffi.dylib \
+ ./interop/python/radix_engine_toolkit/aarch64-apple-darwin
+ cp \
+ ./artifacts/radix-engine-toolkit-uniffi-x86_64-apple-darwin/libradix_engine_toolkit_uniffi.dylib \
+ ./interop/python/radix_engine_toolkit/x86_64-apple-darwin
+ cp \
+ ./artifacts/radix-engine-toolkit-uniffi-aarch64-unknown-linux-gnu/libradix_engine_toolkit_uniffi.so \
+ ./interop/python/radix_engine_toolkit/aarch64-unknown-linux-gnu
+ cp \
+ ./artifacts/radix-engine-toolkit-uniffi-x86_64-unknown-linux-gnu/libradix_engine_toolkit_uniffi.so \
+ ./interop/python/radix_engine_toolkit/x86_64-unknown-linux-gnu
+ cp \
+ ./artifacts/radix-engine-toolkit-uniffi-x86_64-pc-windows-gnu/radix_engine_toolkit_uniffi.dll \
+ ./interop/python/radix_engine_toolkit/x86_64-pc-windows-gnu
+ - name: Build Package
+ working-directory: ./interop/python/
+ run: python3 -m build --wheel
+ - name: Check Builds
+ working-directory: ./interop/python/
+ run: |
+ python3 -m twine check dist/*
+ - name: Publish
+ working-directory: ./interop/python/
+ env:
+ TWINE_USERNAME: ${{ env.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ env.PYPI_PASSWORD }}
+ run: |
+ python3 -m twine upload dist/* --verbose
diff --git a/interop/python/README.md b/interop/python/README.md
new file mode 100644
index 00000000..39a0fb41
--- /dev/null
+++ b/interop/python/README.md
@@ -0,0 +1,12 @@
+
+
Radix Engine Toolkit
+
+ A Python wrapper around the Radix Engine Toolkit that provides Radix Ledger primitives to Python
+
+
+[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
+
+
+The (Python) Radix Engine Toolkit is a wrapper around the [Radix Engine Toolkit](https://github.com/radixdlt/radix-engine-toolkit/) library which exposes the Radix Engine and Scrypto primitives to Python. These primitives include: manifests, transactions, transaction construction and building, access rules, metadata, the SBOR codec, derivations, and many others. The purpose this and the other wrappers around the toolkit is to provide developers with the freedom of constructing transactions and interacting with the ledger in their language of their choice instead of being limited to using Rust .
+
+This library uses [UniFFI](https://github.com/mozilla/uniffi-rs) for interoperability between the core Rust Radix Engine Toolkit and Radix Engine Toolkit wrappers such as this Python wrapper. Thus, the entire codebase of this library is automatically generated and the Python code does not live in a repo by itself. Instead, this library is published directly to PyPi with each push that's made to the [Radix Engine Toolkit](https://github.com/radixdlt/radix-engine-toolkit/) repo. If you would like to submit an issue or open a PR then head to: https://github.com/radixdlt/radix-engine-toolkit/
\ No newline at end of file
diff --git a/interop/python/replacement.py b/interop/python/replacement.py
new file mode 100644
index 00000000..4ef00dba
--- /dev/null
+++ b/interop/python/replacement.py
@@ -0,0 +1,62 @@
+import platform
+import ctypes
+import sys
+import os
+import re
+
+def _uniffi_load_indirect() -> ctypes.CDLL:
+ """
+ This is how we find and load the dynamic library provided by the component.
+ The dynamic library is assumed to exist right beside the code and it's name if the same as the
+ target triple.
+
+ Currently, the supported architectures are:
+ * x86-64 and Arm64 Apple Darwin
+ * x86-64 and Arm64 Linux GNU
+ * x86-64 Win64
+ """
+
+ def library_file_name() -> str:
+ is_x86: bool = platform.machine() in ("AMD64", "x86_64")
+ is_arm: bool = platform.machine() == "arm64"
+ system: str = platform.system()
+
+ if is_x86 and system == "Darwin":
+ return "x86_64-apple-darwin"
+ elif is_arm and system == "Darwin":
+ return "aarch64-apple-darwin"
+ elif is_x86 and system == "Linux":
+ return "x86_64-unknown-linux-gnu"
+ elif is_arm and system == "Linux":
+ return "aarch64-unknown-linux-gnu"
+ elif is_x86 and system == "Windows":
+ return "x86_64-pc-windows-gnu"
+ else:
+ raise NotImplemented(f"No implementation of the Radix Engine Toolkit is available on your platform. Information detected: is_x86: {is_x86}, is_arm: {is_arm}, os: {system}")
+
+ file_name: str = library_file_name()
+ path: str = os.path.join(os.path.dirname(__file__), file_name)
+ return ctypes.cdll.LoadLibrary(path)
+
+def main() -> None:
+ # The first arg is the path of the file which we want to do the replacement for.
+ path: str = sys.argv[1]
+
+ # The regex expression used to read this function
+ regex: str = r'(def _uniffi_load_indirect\(\)\s*(->\s*.*)?:[\d\w\s\n:{}.\[\]=\(\)#,$`\'\"\-*\/><]*^$)'
+
+ # Open THIS file and read the function definition through the regex expression.
+ with open(os.path.abspath(__file__), 'r') as file:
+ new_func_def: str = re.findall(regex, file.read(), re.MULTILINE)[0][0]
+
+ # Open the replacement file, read it, apply regex replacement to it, and then write it again.
+ with open(path, 'r') as file:
+ content: str = file.read()
+ old_func_def: str = re.findall(regex, content, re.MULTILINE)[0][0]
+
+ new_content: str = content.replace(old_func_def, new_func_def)
+ with open(path, 'w') as file:
+ file.write(new_content)
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/interop/python/setup.py b/interop/python/setup.py
new file mode 100644
index 00000000..cf2792c9
--- /dev/null
+++ b/interop/python/setup.py
@@ -0,0 +1,42 @@
+import setuptools
+
+with open("README.md", "r", encoding="utf-8") as file:
+ long_description: str = file.read()
+
+setuptools.setup(
+ name="radix-engine-toolkit",
+ version="0.12.1dev1",
+ packages=["radix_engine_toolkit"],
+ author="radixdlt",
+ description="A Python wrapper around the Radix Engine Toolkit that provides Radix Ledger primitives to Python.",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ license='Apache-2.0',
+ python_requires='>=3.6',
+ install_requires=[],
+ url="https://github.com/radixdlt/radix-engine-toolkit",
+ project_urls={
+ "Bug Tracker": "https://github.com/radixdlt/radix-engine-toolkit/issues",
+ },
+ package_data={
+ "": [
+ "__init__.pyi",
+ "aarch64-apple-darwin",
+ "aarch64-unknown-linux-gnu",
+ "x86_64-apple-darwin",
+ "x86_64-pc-windows-gnu",
+ "x86_64-unknown-linux-gnu",
+ ]
+ },
+ classifiers=[
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: MIT License",
+ ],
+)
\ No newline at end of file