From 0ded66277a8c4667447c74ceddd6bb6923a7fb94 Mon Sep 17 00:00:00 2001 From: kmilo Date: Fri, 2 Oct 2026 12:46:04 -0400 Subject: [PATCH 01/12] fix: update OpenSSF Scorecard badge link in README Signed-off-by: kmilo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09dcd06..28310c7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![GitHub License](https://img.shields.io/github/license/hyperledger/fabric-chaincode-python) [![OpenSSF Best Practices](https://bestpractices.coreinfrastructure.org/projects/14928/badge)](https://bestpractices.coreinfrastructure.org/projects/14928) -[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/hyperledger/fabric-chaincode-python/badge)](https://scorecard.dev/viewer/?uri=github.com/hyperledger/fabric-chaincode-python) +[![OpenSSF Scorecard](https://github.com/hyperledger/fabric-chaincode-python/actions/workflows/CI.yml/badge.svg)](https://scorecard.dev/viewer/?uri=github.com/hyperledger/fabric-chaincode-python) [![Python Version](https://img.shields.io/pypi/pyversions/fabric-chaincode-python.svg)](https://pypi.org/project/fabric-chaincode-python/) [![GitHub Actions](https://github.com/hyperledger/fabric-chaincode-python/workflows/CI/badge.svg)](https://github.com/hyperledger/fabric-chaincode-python/actions?query=workflow%3ACI) From 75750c217800f6a0a9b68a52891276bc11d6bf16 Mon Sep 17 00:00:00 2001 From: kmilo Date: Fri, 2 Oct 2026 12:46:16 -0400 Subject: [PATCH 02/12] fix: update version to 2.5.3 and add classifiers in pyproject.toml Signed-off-by: kmilo --- pyproject.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 20490c5..0b916de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,11 +4,16 @@ build-backend = "setuptools.build_meta" [project] name = "fabric-chaincode-python" -version = "2.5.1" +version = "2.5.3" description = "Hyperledger Fabric chaincode shim and contract API for Python (experimental)" readme = "README.md" license = {text = "Apache License v2.0"} requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10" +] dependencies = [ "grpcio>=1.83.1,<2.0.0", "grpclib>=0.4.3", From 30f68768af89a82a23dfdc0c728919f945335653 Mon Sep 17 00:00:00 2001 From: kmilo Date: Fri, 2 Oct 2026 13:40:12 -0400 Subject: [PATCH 03/12] refactor: clean up import statements and update requirements for asset transfer examples Signed-off-by: kmilo --- .../asset-transfer-basic/main.py | 19 +------------------ .../asset-transfer-basic/requirements.txt | 3 +-- .../asset-transfer-sbe/main.py | 18 +----------------- .../asset-transfer-sbe/requirements.txt | 9 +-------- .../fabric-shim/asset-transfer-basic/main.py | 16 ---------------- .../asset-transfer-basic/requirements.txt | 3 +-- .../fabric-shim/asset-transfer-sbe/main.py | 15 --------------- .../asset-transfer-sbe/requirements.txt | 9 +-------- 8 files changed, 6 insertions(+), 86 deletions(-) diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py b/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py index 633f09c..2c8201c 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py +++ b/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py @@ -31,27 +31,10 @@ from __future__ import annotations import json -import sys from dataclasses import dataclass -from pathlib import Path from typing import List, Optional -# Make sure the repository root is on ``sys.path`` so that the -# ``src.fabric_contract_api`` packages can be imported when the file is -# run directly. Walk up the parent chain looking for the directory that -# contains ``src/``. -_HERE = Path(__file__).resolve().parent -REPO_ROOT = None -for parent in [_HERE, *_HERE.parents]: - if (parent / "src" / "fabric_contract_api").is_dir(): - REPO_ROOT = parent - break -if REPO_ROOT is None: - REPO_ROOT = Path(__file__).resolve().parents[3] -if str(REPO_ROOT) not in sys.path: - sys.path.insert(0, str(REPO_ROOT)) - -from src.fabric_contract_api import ( # noqa: E402 +from src.fabric_contract_api import ( Contract, ContractChaincode, TransactionContextInterface, diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-basic/requirements.txt b/examples/ccaas/fabric-contract-api/asset-transfer-basic/requirements.txt index 49da5b9..e108449 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-basic/requirements.txt +++ b/examples/ccaas/fabric-contract-api/asset-transfer-basic/requirements.txt @@ -1,5 +1,4 @@ # Fabric Chaincode Python - CCAAS Example # Install dependencies with: pip install -r requirements.txt -grpcio>=1.83.1 -protobuf>=3.20.0,<4.0.0 +fabric-chaincode-python>=2.5.2 diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py index 143341a..20a321e 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py +++ b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py @@ -42,26 +42,10 @@ from __future__ import annotations import json -import sys from dataclasses import dataclass -from pathlib import Path from typing import List -# Make sure the repository root is on ``sys.path`` so that the -# ``src.fabric_contract_api`` packages can be imported when the file is -# run directly. -_HERE = Path(__file__).resolve().parent -REPO_ROOT = None -for parent in [_HERE, *_HERE.parents]: - if (parent / "src" / "fabric_contract_api").is_dir(): - REPO_ROOT = parent - break -if REPO_ROOT is None: - REPO_ROOT = Path(__file__).resolve().parents[3] -if str(REPO_ROOT) not in sys.path: - sys.path.insert(0, str(REPO_ROOT)) - -from src.fabric_contract_api import ( # noqa: E402 +from src.fabric_contract_api import ( Contract, ContractChaincode, TransactionContextInterface, diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/requirements.txt b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/requirements.txt index 329f6e8..b0153e2 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/requirements.txt +++ b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/requirements.txt @@ -1,11 +1,4 @@ # Token Chaincode Example Requirements # Install with: pip install -r requirements.txt -# Parent package (fabric-chaincode-python) -# When running in the parent directory: -# -e ../.. - -# If running standalone, uncomment the direct dependencies: -# grpcio>=1.83.1 -# protobuf>=3.20.0,<4.0.0 -# grpclib>=0.4.3 +fabric-chaincode-python>=2.5.2 diff --git a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py index 8a0e4f1..a3a3004 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py @@ -23,22 +23,6 @@ """ import json -import sys -from pathlib import Path - -# Make sure the repository root is on ``sys.path`` so that the -# ``src.fabric_shim`` packages can be imported when the file is run directly. -# Walk up the parent chain looking for the directory that contains ``src/``. -_HERE = Path(__file__).resolve().parent -REPO_ROOT = None -for parent in [_HERE, *_HERE.parents]: - if (parent / "src" / "fabric_shim").is_dir(): - REPO_ROOT = parent - break -if REPO_ROOT is None: - REPO_ROOT = Path(__file__).resolve().parents[3] -if str(REPO_ROOT) not in sys.path: - sys.path.insert(0, str(REPO_ROOT)) from src.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface from src.fabric_shim.server import start diff --git a/examples/ccaas/fabric-shim/asset-transfer-basic/requirements.txt b/examples/ccaas/fabric-shim/asset-transfer-basic/requirements.txt index 49da5b9..e108449 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-basic/requirements.txt +++ b/examples/ccaas/fabric-shim/asset-transfer-basic/requirements.txt @@ -1,5 +1,4 @@ # Fabric Chaincode Python - CCAAS Example # Install dependencies with: pip install -r requirements.txt -grpcio>=1.83.1 -protobuf>=3.20.0,<4.0.0 +fabric-chaincode-python>=2.5.2 diff --git a/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py b/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py index f6ed8da..ee71d4e 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py @@ -21,21 +21,6 @@ import json import logging -import sys -from pathlib import Path - -# Make sure the repository root is on ``sys.path`` so that the -# ``src.fabric_shim`` packages can be imported when the file is run directly. -_HERE = Path(__file__).resolve().parent -REPO_ROOT = None -for parent in [_HERE, *_HERE.parents]: - if (parent / "src" / "fabric_shim").is_dir(): - REPO_ROOT = parent - break -if REPO_ROOT is None: - REPO_ROOT = Path(__file__).resolve().parents[3] -if str(REPO_ROOT) not in sys.path: - sys.path.insert(0, str(REPO_ROOT)) from fabric_protos.peer import proposal_response_pb2 as pb from src.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface diff --git a/examples/ccaas/fabric-shim/asset-transfer-sbe/requirements.txt b/examples/ccaas/fabric-shim/asset-transfer-sbe/requirements.txt index 329f6e8..b0153e2 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-sbe/requirements.txt +++ b/examples/ccaas/fabric-shim/asset-transfer-sbe/requirements.txt @@ -1,11 +1,4 @@ # Token Chaincode Example Requirements # Install with: pip install -r requirements.txt -# Parent package (fabric-chaincode-python) -# When running in the parent directory: -# -e ../.. - -# If running standalone, uncomment the direct dependencies: -# grpcio>=1.83.1 -# protobuf>=3.20.0,<4.0.0 -# grpclib>=0.4.3 +fabric-chaincode-python>=2.5.2 From cc843ee8c5bb2015df7a1752a5beed8062159b87 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 00:13:46 -0400 Subject: [PATCH 04/12] docs: update README examples to use virtualenv for running chaincode processes Signed-off-by: kmilo --- examples/README.md | 16 +++++++++------- examples/ccaas/README.md | 4 +++- examples/ccaas/fabric-contract-api/README.md | 12 ++++++------ examples/ccaas/fabric-shim/README.md | 12 ++++++------ 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/examples/README.md b/examples/README.md index 9e73f6b..a13650c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -47,13 +47,15 @@ asset-transfer-sbe) so you can compare implementations side-by-side. 2. **Read**: each example has its own `README.md` with the full packaging / install / approve / commit / run / invoke / query sequence. -3. **Run**: from the repository root, - ```bash - pip install -r requirements.txt - export CHAINCODE_ID="" - export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" - python examples/ccaas///main.py - ``` +3. **Run**: from the repository root — create/activate a virtualenv and install dependencies, then start the example: + ```bash + python3 -m venv .venv + source .venv/bin/activate + pip install -r requirements.txt + export CHAINCODE_ID="" + export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" + ./.venv/bin/python examples/ccaas///main.py + ``` ## What is CCAAS? diff --git a/examples/ccaas/README.md b/examples/ccaas/README.md index a804a27..b0754ec 100644 --- a/examples/ccaas/README.md +++ b/examples/ccaas/README.md @@ -46,7 +46,9 @@ style you prefer: 2. **Pick a scenario**: `asset-transfer-basic/` (CRUD) or `asset-transfer-sbe/` (with transfer). 3. **Open the example's `README.md`** — it has the full packaging, - install, approve, commit, run, invoke, and query sequence. + install, approve, commit, run, invoke, and query sequence. When running + examples locally prefer the repository virtualenv and invoke the + example with `./.venv/bin/python`. ## Common prerequisites diff --git a/examples/ccaas/fabric-contract-api/README.md b/examples/ccaas/fabric-contract-api/README.md index f1efb66..db9bf4a 100644 --- a/examples/ccaas/fabric-contract-api/README.md +++ b/examples/ccaas/fabric-contract-api/README.md @@ -47,12 +47,12 @@ examples: 2. Build the CCAAS package using the example's `connection.json` / `metadata.json`. 3. `peer lifecycle chaincode install / approveformyorg / commit`. -4. Start the chaincode process: - ```bash - export CHAINCODE_ID="" - export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" - python examples/ccaas/fabric-contract-api//main.py - ``` +4. Start the chaincode process (from the repository root, using the project's virtualenv): + ```bash + export CHAINCODE_ID="" + export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" + ./.venv/bin/python examples/ccaas/fabric-contract-api//main.py + ``` 5. `peer chaincode invoke` / `query` against the running chaincode — including the metadata query: ```bash diff --git a/examples/ccaas/fabric-shim/README.md b/examples/ccaas/fabric-shim/README.md index 31e6bf2..7bef10a 100644 --- a/examples/ccaas/fabric-shim/README.md +++ b/examples/ccaas/fabric-shim/README.md @@ -37,10 +37,10 @@ instructions. The common pattern is: 2. Build the CCAAS package using the example's `connection.json` / `metadata.json`. 3. `peer lifecycle chaincode install / approveformyorg / commit`. -4. Start the chaincode process: - ```bash - export CHAINCODE_ID="" - export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" - python examples/ccaas/fabric-shim//main.py - ``` +4. Start the chaincode process (use the repository virtualenv): + ```bash + export CHAINCODE_ID="" + export CHAINCODE_SERVER_ADDRESS="127.0.0.1:9999" + ./.venv/bin/python examples/ccaas/fabric-shim//main.py + ``` 5. `peer chaincode invoke` / `query` against the running chaincode. From 206424c8ae6035cb2dc4805e1ca4a3d269aee8ac Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 01:52:27 -0400 Subject: [PATCH 05/12] chore: update pyproject.toml for improved metadata and remove setup.py; add version module Signed-off-by: kmilo --- pyproject.toml | 29 +++++++--- setup.py | 57 -------------------- src/__init__.py | 2 +- src/{ => fabric_chaincode_python}/version.py | 2 +- 4 files changed, 25 insertions(+), 65 deletions(-) delete mode 100644 setup.py rename src/{ => fabric_chaincode_python}/version.py (68%) diff --git a/pyproject.toml b/pyproject.toml index 0b916de..4d0257c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,27 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools>=77", "wheel"] build-backend = "setuptools.build_meta" [project] name = "fabric-chaincode-python" -version = "2.5.3" -description = "Hyperledger Fabric chaincode shim and contract API for Python (experimental)" +dynamic = ["version"] +description = "Hyperledger Fabric chaincode shim and contract API for Python" readme = "README.md" -license = {text = "Apache License v2.0"} +license = "Apache-2.0" +license-files = ["LICENSE"] +authors = [{ name = "Hyperledger Community" }] requires-python = ">=3.10" +keywords = ["Hyperledger", "Fabric", "Chaincode", "Python"] classifiers = [ + "Intended Audience :: Developers", + "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.10" + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Topic :: Software Development :: Libraries", ] + dependencies = [ "grpcio>=1.83.1,<2.0.0", "grpclib>=0.4.3", @@ -21,9 +29,18 @@ dependencies = [ "hyperledger-fabric-protos>=0.3.7.dev1,<0.4.0", "jsonschema>=4.0.0" ] -keywords = ["Hyperledger", "Fabric", "Chaincode", "Python"] + +[project.urls] +Homepage = "https://github.com/hyperledger/fabric-chaincode-python" +Repository = "https://github.com/hyperledger/fabric-chaincode-python" +Issues = "https://github.com/hyperledger/fabric-chaincode-python/issues" + +[tool.setuptools.packages.find] +where = ["src"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] +[tool.setuptools.dynamic] +version = {attr = "fabric_chaincode_python.version.VERSION"} diff --git a/setup.py b/setup.py deleted file mode 100644 index 7daa708..0000000 --- a/setup.py +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# !/usr/bin/env python -import io -import os -from setuptools import setup, find_packages - -# Load the package version without importing the `src` package. Importing the -# package during an isolated build can fail because dependencies are not yet -# installed. Read and exec the version file into a temporary namespace instead. -ROOT_DIR = os.path.dirname(__file__) -version_ns: dict = {} -version_path = os.path.join(ROOT_DIR, 'src', 'version.py') -try: - with open(version_path, 'r', encoding='utf-8') as vf: - exec(vf.read(), version_ns) - VERSION = version_ns.get('VERSION', '0.0.0') -except FileNotFoundError: - VERSION = '0.0.0' - -def _read_requirements(path: str): - try: - with open(path) as fh: - return [line.strip() for line in fh if line.strip() and not line.startswith('#')] - except FileNotFoundError: - return [] - -requirements = _read_requirements('./requirements.txt') -test_requirements = _read_requirements('./requirements-test.txt') - -setup( - name='fabric-chaincode-python', - version=VERSION, - keywords=['Hyperledger Fabric', 'fabric-chaincode'], - license='Apache License v2.0', - description="Hyperledger Fabric Contract and Chaincode implementation for Python.", - long_description=io.open('README.md', encoding='utf-8').read(), - author='Hyperledger Community', - url='https://github.com/kmilodenisglez/fabric-chaincode-python/', - download_url='https://github.com/kmilodenisglez/fabric-chaincode-python/', - packages=find_packages(exclude=['docs', 'tests*']), - platforms='any', - install_requires=requirements, - tests_require=test_requirements, - zip_safe=False, - test_suite='test', - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Other Environment', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.8', - 'Topic :: Utilities', - 'License :: OSI Approved :: Apache Software License', - ], - include_package_data=True, -) diff --git a/src/__init__.py b/src/__init__.py index de2d2ef..0659625 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -2,4 +2,4 @@ # SPDX-License-Identifier: Apache-2.0 # -from .version import VERSION # noqa +from .fabric_chaincode_python.version import VERSION # noqa diff --git a/src/version.py b/src/fabric_chaincode_python/version.py similarity index 68% rename from src/version.py rename to src/fabric_chaincode_python/version.py index 44a7a9e..f92bb45 100644 --- a/src/version.py +++ b/src/fabric_chaincode_python/version.py @@ -1,3 +1,3 @@ # SPDX-License-Identifier: Apache-2.0 -VERSION = "0.9.0" +VERSION = "2.5.3" From 443d5399bb1997e6f6116ec45e4aba1da18e8ce1 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 01:52:50 -0400 Subject: [PATCH 06/12] fix: enhance release workflow with metadata checks and version verification Signed-off-by: kmilo --- .github/workflows/release.yml | 39 ++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1088fa2..72a7d0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,11 +42,48 @@ jobs: - name: Install build dependencies run: | python -m pip install --upgrade pip - python -m pip install build setuptools wheel + python -m pip install build twine packaging - name: Build distribution run: python -m build --sdist --wheel + - name: Check metadata + run: python -m twine check dist/* + + - name: Verify tag matches package version + if: startsWith(github.ref, 'refs/tags/') + run: | + python - <<'PY' + import glob + import os + import zipfile + from packaging.version import Version + + tag = os.environ["GITHUB_REF_NAME"] + if not tag.startswith("v"): + raise SystemExit(f"Unexpected tag (must start with 'v'): {tag}") + + wheels = glob.glob("dist/*.whl") + if not wheels: + raise SystemExit("No wheel found in dist/") + + with zipfile.ZipFile(wheels[0]) as zf: + meta = next(n for n in zf.namelist() if n.endswith(".dist-info/METADATA")) + version = None + for line in zf.read(meta).decode().splitlines(): + if line.startswith("Version:"): + version = line.split(":", 1)[1].strip() + break + if version is None: + raise SystemExit("METADATA without version") + + print(f"Git tag: {tag}") + print(f"Package version: {version}") + if Version(tag.removeprefix("v")) != Version(version): + raise SystemExit(f"::error::Version mismatch: tag={tag}, package={version}") + print("OK: tag and version match") + PY + # Trusted Publishing: no password required. The action obtains an ephemeral # OIDC token signed by GitHub, which PyPI validates against the # registered publisher. There are no secrets to rotate or leak. From 24017c281419b4b421be9b3080ffad9e7cbd258f Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 01:57:09 -0400 Subject: [PATCH 07/12] moved fabric_contract_api and fabric_shim sources to src/fabric-chaincode-python Signed-off-by: kmilo --- src/fabric_chaincode_python/__init__.py | 11 ++++ .../fabric_contract_api/README.md | 2 +- .../fabric_contract_api/__init__.py | 51 +++++++++++++++++++ .../contractapi/__init__.py | 0 .../contractapi/contract.py | 0 .../contractapi/contract_chaincode.py | 8 +-- .../contractapi/system_contract.py | 0 .../contractapi/transaction_context.py | 0 .../contractapi/utils/__init__.py | 0 .../contractapi/utils/undefined_interface.py | 0 .../fabric_contract_api/internal/__init__.py | 0 .../internal/contract_function.py | 0 .../internal/transaction_handler.py | 0 .../fabric_contract_api/internal/types.py | 0 .../internal/types_handler.py | 0 .../fabric_contract_api/internal/utils.py | 0 .../fabric_contract_api/metadata/__init__.py | 0 .../fabric_contract_api/metadata/metadata.py | 0 .../fabric_contract_api/metadata/schema.py | 0 .../metadata/schema/schema.json | 0 .../serializer/__init__.py | 0 .../serializer/json_transaction_serializer.py | 0 .../serializer/transaction_serializer.py | 0 .../fabric_shim/__init__.py | 0 .../fabric_shim/chaincode.py | 0 .../fabric_shim/handler.py | 12 ++--- .../fabric_shim/interfaces.py | 0 .../fabric_shim/iterators.py | 0 .../fabric_shim/logger.py | 0 .../fabric_shim/logging.py | 0 .../fabric_shim/msg_queue_handler.py | 2 +- .../fabric_shim/response.py | 0 .../fabric_shim/server.py | 6 +-- .../fabric_shim/stub.py | 16 +++--- .../fabric_shim/utils.py | 0 35 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 src/fabric_chaincode_python/__init__.py rename src/{ => fabric_chaincode_python}/fabric_contract_api/README.md (99%) create mode 100644 src/fabric_chaincode_python/fabric_contract_api/__init__.py rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/contract.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/contract_chaincode.py (98%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/system_contract.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/transaction_context.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/utils/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/contractapi/utils/undefined_interface.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/contract_function.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/transaction_handler.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/types.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/types_handler.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/internal/utils.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/metadata/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/metadata/metadata.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/metadata/schema.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/metadata/schema/schema.json (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/serializer/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/serializer/json_transaction_serializer.py (100%) rename src/{ => fabric_chaincode_python}/fabric_contract_api/serializer/transaction_serializer.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/__init__.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/chaincode.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/handler.py (96%) rename src/{ => fabric_chaincode_python}/fabric_shim/interfaces.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/iterators.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/logger.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/logging.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/msg_queue_handler.py (97%) rename src/{ => fabric_chaincode_python}/fabric_shim/response.py (100%) rename src/{ => fabric_chaincode_python}/fabric_shim/server.py (96%) rename src/{ => fabric_chaincode_python}/fabric_shim/stub.py (95%) rename src/{ => fabric_chaincode_python}/fabric_shim/utils.py (100%) diff --git a/src/fabric_chaincode_python/__init__.py b/src/fabric_chaincode_python/__init__.py new file mode 100644 index 0000000..70d78c5 --- /dev/null +++ b/src/fabric_chaincode_python/__init__.py @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Hyperledger Fabric Python Chaincode SDK. + +This package provides: +- fabric_shim: Low-level chaincode shim API +- fabric_contract_api: High-level contract API +""" + +from .version import VERSION # noqa: F401 + +__all__ = ["VERSION"] diff --git a/src/fabric_contract_api/README.md b/src/fabric_chaincode_python/fabric_contract_api/README.md similarity index 99% rename from src/fabric_contract_api/README.md rename to src/fabric_chaincode_python/fabric_contract_api/README.md index 3ba7cc0..aa3af54 100644 --- a/src/fabric_contract_api/README.md +++ b/src/fabric_chaincode_python/fabric_contract_api/README.md @@ -44,7 +44,7 @@ src/fabric_contract_api/ ## Quick start ```python -from src.fabric_contract_api import ( +from fabric_chaincode_python.fabric_contract_api import ( Contract, ContractChaincode, TransactionContextInterface, diff --git a/src/fabric_chaincode_python/fabric_contract_api/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/__init__.py new file mode 100644 index 0000000..678cf88 --- /dev/null +++ b/src/fabric_chaincode_python/fabric_contract_api/__init__.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Hyperledger Fabric Contract API for Python. + +This package provides a high-level, contract-oriented programming model on +top of the low-level :mod:`fabric_shim` chaincode shim. It is the Python +equivalent of Hyperledger's ``fabric-contract-api-go``. + +Public surface (mirrors the Go layout):: + + fabric_contract_api.contractapi # Contract, ContractChaincode, ... + fabric_contract_api.serializer # TransactionSerializer, JSONSerializer + fabric_contract_api.metadata # Metadata types & schema helpers + fabric_contract_api.internal # Introspection & type validation helpers + +The most useful imports for application developers are:: + + from fabric_contract_api.contractapi import Contract, ContractChaincode + from fabric_contract_api.contractapi import TransactionContext, TransactionContextInterface +""" + +from .contractapi import ( # noqa: F401 (re-exported for convenience) + Contract, + ContractChaincode, + ContractInterface, + EvaluationContractInterface, + IgnoreContractInterface, + SystemContract, + SystemContractName, + TransactionContext, + TransactionContextInterface, + SettableTransactionContextInterface, +) +from .serializer import ( # noqa: F401 + TransactionSerializer, + JSONSerializer, +) + +__all__ = [ + "Contract", + "ContractChaincode", + "ContractInterface", + "EvaluationContractInterface", + "IgnoreContractInterface", + "SystemContract", + "SystemContractName", + "TransactionContext", + "TransactionContextInterface", + "SettableTransactionContextInterface", + "TransactionSerializer", + "JSONSerializer", +] diff --git a/src/fabric_contract_api/contractapi/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/__init__.py similarity index 100% rename from src/fabric_contract_api/contractapi/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/__init__.py diff --git a/src/fabric_contract_api/contractapi/contract.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/contract.py similarity index 100% rename from src/fabric_contract_api/contractapi/contract.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/contract.py diff --git a/src/fabric_contract_api/contractapi/contract_chaincode.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/contract_chaincode.py similarity index 98% rename from src/fabric_contract_api/contractapi/contract_chaincode.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/contract_chaincode.py index 5cfecc6..4470ec1 100644 --- a/src/fabric_contract_api/contractapi/contract_chaincode.py +++ b/src/fabric_chaincode_python/fabric_contract_api/contractapi/contract_chaincode.py @@ -29,9 +29,9 @@ def my_transaction(self, ctx, key: str) -> str: import re from typing import Any, Dict, List, Optional, Tuple, Type -from src.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface -from src.fabric_shim.response import ResponseCode, success as _shim_success, error as _shim_error -from src.fabric_shim.logging import LOGGER +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface +from fabric_chaincode_python.fabric_shim.response import ResponseCode, success as _shim_success, error as _shim_error +from fabric_chaincode_python.fabric_shim.logging import LOGGER from ..internal.contract_function import CallType, ContractFunction from ..internal.transaction_handler import ( @@ -162,7 +162,7 @@ def start(self, cc_id: Optional[str] = None, address: Optional[str] = None, """ # Local import to avoid a hard dependency cycle when this module is # imported eagerly by user code. - from src.fabric_shim.server import start as _shim_start + from fabric_chaincode_python.fabric_shim.server import start as _shim_start _shim_start( cc=self, diff --git a/src/fabric_contract_api/contractapi/system_contract.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/system_contract.py similarity index 100% rename from src/fabric_contract_api/contractapi/system_contract.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/system_contract.py diff --git a/src/fabric_contract_api/contractapi/transaction_context.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/transaction_context.py similarity index 100% rename from src/fabric_contract_api/contractapi/transaction_context.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/transaction_context.py diff --git a/src/fabric_contract_api/contractapi/utils/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/utils/__init__.py similarity index 100% rename from src/fabric_contract_api/contractapi/utils/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/utils/__init__.py diff --git a/src/fabric_contract_api/contractapi/utils/undefined_interface.py b/src/fabric_chaincode_python/fabric_contract_api/contractapi/utils/undefined_interface.py similarity index 100% rename from src/fabric_contract_api/contractapi/utils/undefined_interface.py rename to src/fabric_chaincode_python/fabric_contract_api/contractapi/utils/undefined_interface.py diff --git a/src/fabric_contract_api/internal/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/internal/__init__.py similarity index 100% rename from src/fabric_contract_api/internal/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/__init__.py diff --git a/src/fabric_contract_api/internal/contract_function.py b/src/fabric_chaincode_python/fabric_contract_api/internal/contract_function.py similarity index 100% rename from src/fabric_contract_api/internal/contract_function.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/contract_function.py diff --git a/src/fabric_contract_api/internal/transaction_handler.py b/src/fabric_chaincode_python/fabric_contract_api/internal/transaction_handler.py similarity index 100% rename from src/fabric_contract_api/internal/transaction_handler.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/transaction_handler.py diff --git a/src/fabric_contract_api/internal/types.py b/src/fabric_chaincode_python/fabric_contract_api/internal/types.py similarity index 100% rename from src/fabric_contract_api/internal/types.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/types.py diff --git a/src/fabric_contract_api/internal/types_handler.py b/src/fabric_chaincode_python/fabric_contract_api/internal/types_handler.py similarity index 100% rename from src/fabric_contract_api/internal/types_handler.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/types_handler.py diff --git a/src/fabric_contract_api/internal/utils.py b/src/fabric_chaincode_python/fabric_contract_api/internal/utils.py similarity index 100% rename from src/fabric_contract_api/internal/utils.py rename to src/fabric_chaincode_python/fabric_contract_api/internal/utils.py diff --git a/src/fabric_contract_api/metadata/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/metadata/__init__.py similarity index 100% rename from src/fabric_contract_api/metadata/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/metadata/__init__.py diff --git a/src/fabric_contract_api/metadata/metadata.py b/src/fabric_chaincode_python/fabric_contract_api/metadata/metadata.py similarity index 100% rename from src/fabric_contract_api/metadata/metadata.py rename to src/fabric_chaincode_python/fabric_contract_api/metadata/metadata.py diff --git a/src/fabric_contract_api/metadata/schema.py b/src/fabric_chaincode_python/fabric_contract_api/metadata/schema.py similarity index 100% rename from src/fabric_contract_api/metadata/schema.py rename to src/fabric_chaincode_python/fabric_contract_api/metadata/schema.py diff --git a/src/fabric_contract_api/metadata/schema/schema.json b/src/fabric_chaincode_python/fabric_contract_api/metadata/schema/schema.json similarity index 100% rename from src/fabric_contract_api/metadata/schema/schema.json rename to src/fabric_chaincode_python/fabric_contract_api/metadata/schema/schema.json diff --git a/src/fabric_contract_api/serializer/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/serializer/__init__.py similarity index 100% rename from src/fabric_contract_api/serializer/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/serializer/__init__.py diff --git a/src/fabric_contract_api/serializer/json_transaction_serializer.py b/src/fabric_chaincode_python/fabric_contract_api/serializer/json_transaction_serializer.py similarity index 100% rename from src/fabric_contract_api/serializer/json_transaction_serializer.py rename to src/fabric_chaincode_python/fabric_contract_api/serializer/json_transaction_serializer.py diff --git a/src/fabric_contract_api/serializer/transaction_serializer.py b/src/fabric_chaincode_python/fabric_contract_api/serializer/transaction_serializer.py similarity index 100% rename from src/fabric_contract_api/serializer/transaction_serializer.py rename to src/fabric_chaincode_python/fabric_contract_api/serializer/transaction_serializer.py diff --git a/src/fabric_shim/__init__.py b/src/fabric_chaincode_python/fabric_shim/__init__.py similarity index 100% rename from src/fabric_shim/__init__.py rename to src/fabric_chaincode_python/fabric_shim/__init__.py diff --git a/src/fabric_shim/chaincode.py b/src/fabric_chaincode_python/fabric_shim/chaincode.py similarity index 100% rename from src/fabric_shim/chaincode.py rename to src/fabric_chaincode_python/fabric_shim/chaincode.py diff --git a/src/fabric_shim/handler.py b/src/fabric_chaincode_python/fabric_shim/handler.py similarity index 96% rename from src/fabric_shim/handler.py rename to src/fabric_chaincode_python/fabric_shim/handler.py index 3e2db56..a6ef721 100644 --- a/src/fabric_shim/handler.py +++ b/src/fabric_chaincode_python/fabric_shim/handler.py @@ -7,12 +7,12 @@ from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.peer import chaincode_pb2 as cc_pb2 from fabric_protos.peer import proposal_response_pb2 as pr_pb -from src.fabric_shim.stub import ChaincodeStub -from src.fabric_shim.msg_queue_handler import MsgQueueHandler, QueueMessage -from src.fabric_shim.response import new_error_msg, ResponseCode -from src.fabric_shim.utils import generate_logging_prefix -from src.fabric_shim.logging import LOGGER -from src.fabric_shim.interfaces import Chaincode +from fabric_chaincode_python.fabric_shim.stub import ChaincodeStub +from fabric_chaincode_python.fabric_shim.msg_queue_handler import MsgQueueHandler, QueueMessage +from fabric_chaincode_python.fabric_shim.response import new_error_msg, ResponseCode +from fabric_chaincode_python.fabric_shim.utils import generate_logging_prefix +from fabric_chaincode_python.fabric_shim.logging import LOGGER +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode class STATES: diff --git a/src/fabric_shim/interfaces.py b/src/fabric_chaincode_python/fabric_shim/interfaces.py similarity index 100% rename from src/fabric_shim/interfaces.py rename to src/fabric_chaincode_python/fabric_shim/interfaces.py diff --git a/src/fabric_shim/iterators.py b/src/fabric_chaincode_python/fabric_shim/iterators.py similarity index 100% rename from src/fabric_shim/iterators.py rename to src/fabric_chaincode_python/fabric_shim/iterators.py diff --git a/src/fabric_shim/logger.py b/src/fabric_chaincode_python/fabric_shim/logger.py similarity index 100% rename from src/fabric_shim/logger.py rename to src/fabric_chaincode_python/fabric_shim/logger.py diff --git a/src/fabric_shim/logging.py b/src/fabric_chaincode_python/fabric_shim/logging.py similarity index 100% rename from src/fabric_shim/logging.py rename to src/fabric_chaincode_python/fabric_shim/logging.py diff --git a/src/fabric_shim/msg_queue_handler.py b/src/fabric_chaincode_python/fabric_shim/msg_queue_handler.py similarity index 97% rename from src/fabric_shim/msg_queue_handler.py rename to src/fabric_chaincode_python/fabric_shim/msg_queue_handler.py index 64b1c09..05f9357 100644 --- a/src/fabric_shim/msg_queue_handler.py +++ b/src/fabric_chaincode_python/fabric_shim/msg_queue_handler.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 import asyncio -from src.fabric_shim.logging import LOGGER +from fabric_chaincode_python.fabric_shim.logging import LOGGER class QueueMessage: diff --git a/src/fabric_shim/response.py b/src/fabric_chaincode_python/fabric_shim/response.py similarity index 100% rename from src/fabric_shim/response.py rename to src/fabric_chaincode_python/fabric_shim/response.py diff --git a/src/fabric_shim/server.py b/src/fabric_chaincode_python/fabric_shim/server.py similarity index 96% rename from src/fabric_shim/server.py rename to src/fabric_chaincode_python/fabric_shim/server.py index 12086eb..8341e36 100644 --- a/src/fabric_shim/server.py +++ b/src/fabric_chaincode_python/fabric_shim/server.py @@ -8,9 +8,9 @@ import grpc import queue -from src.fabric_shim.handler import Handler -from src.fabric_shim.interfaces import Chaincode -from src.fabric_shim.logging import LOGGER +from fabric_chaincode_python.fabric_shim.handler import Handler +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode +from fabric_chaincode_python.fabric_shim.logging import LOGGER from fabric_protos.peer import chaincode_shim_pb2_grpc as ccshim_grpc_pb2 from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 diff --git a/src/fabric_shim/stub.py b/src/fabric_chaincode_python/fabric_shim/stub.py similarity index 95% rename from src/fabric_shim/stub.py rename to src/fabric_chaincode_python/fabric_shim/stub.py index c7446c3..a2f7bb8 100644 --- a/src/fabric_shim/stub.py +++ b/src/fabric_chaincode_python/fabric_shim/stub.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -from src.fabric_shim.interfaces import ChaincodeStubInterface -from src.fabric_shim.utils import ( +from fabric_chaincode_python.fabric_shim.interfaces import ChaincodeStubInterface +from fabric_chaincode_python.fabric_shim.utils import ( COMPOSITEKEY_NS, MIN_UNICODE_RUNE_VALUE, validate_composite_key_attribute, @@ -11,7 +11,7 @@ from fabric_protos.msp import identities_pb2 as id_pb from fabric_protos.peer import chaincode_event_pb2 as e_pb from collections.abc import Sequence -from src.fabric_shim.logging import LOGGER +from fabric_chaincode_python.fabric_shim.logging import LOGGER VALIDATION_PARAMETER: str = 'VALIDATION_PARAMETER' @@ -147,7 +147,7 @@ async def get_state_by_range(self, start_key: str, end_key: str): to mean "unbounded". """ collection = '' - from src.fabric_shim.iterators import StateQueryIterator + from fabric_chaincode_python.fabric_shim.iterators import StateQueryIterator from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.ledger.queryresult import kv_query_result_pb2 as kv_pb @@ -172,7 +172,7 @@ async def get_state_by_range_with_pagination(self, start_key: str, end_key: str, :meth:`aclose` method of the iterator when you want to stop early. """ collection = '' - from src.fabric_shim.iterators import StateQueryIterator + from fabric_chaincode_python.fabric_shim.iterators import StateQueryIterator from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.ledger.queryresult import kv_query_result_pb2 as kv_pb @@ -198,7 +198,7 @@ async def get_query_result(self, query: str): Returns an async iterator yielding :class:`KV` records. """ collection = '' - from src.fabric_shim.iterators import QueryResultIterator + from fabric_chaincode_python.fabric_shim.iterators import QueryResultIterator from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.ledger.queryresult import kv_query_result_pb2 as kv_pb @@ -218,7 +218,7 @@ async def get_query_result_with_pagination(self, query: str, page_size: int, bookmark: str = ""): """Paginated version of :meth:`get_query_result`.""" collection = '' - from src.fabric_shim.iterators import QueryResultIterator + from fabric_chaincode_python.fabric_shim.iterators import QueryResultIterator from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.ledger.queryresult import kv_query_result_pb2 as kv_pb @@ -244,7 +244,7 @@ async def get_history_for_key(self, key: str): Yields :class:`KeyModification` records (``tx_id``, ``value``, ``timestamp``, ``is_delete``). """ - from src.fabric_shim.iterators import HistoryQueryIterator + from fabric_chaincode_python.fabric_shim.iterators import HistoryQueryIterator from fabric_protos.peer import chaincode_shim_pb2 as ccshim_pb2 from fabric_protos.ledger.queryresult import kv_query_result_pb2 as kv_pb diff --git a/src/fabric_shim/utils.py b/src/fabric_chaincode_python/fabric_shim/utils.py similarity index 100% rename from src/fabric_shim/utils.py rename to src/fabric_chaincode_python/fabric_shim/utils.py From fd7a60106b618da0ad0cc60d78aca8ff72c4b1f1 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 01:58:00 -0400 Subject: [PATCH 08/12] refactor: update import paths to reflect new package structure and remove deprecated files Signed-off-by: kmilo --- RELEASING.md | 2 +- examples/README.md | 4 +- examples/STRUCTURE.md | 6 +-- examples/ccaas/fabric-contract-api/README.md | 2 +- .../asset-transfer-basic/README.md | 2 +- .../asset-transfer-basic/main.py | 2 +- .../asset-transfer-sbe/README.md | 2 +- .../asset-transfer-sbe/main.py | 2 +- examples/ccaas/fabric-shim/README.md | 2 +- .../fabric-shim/asset-transfer-basic/main.py | 6 +-- .../fabric-shim/asset-transfer-sbe/main.py | 6 +-- src/fabric_contract_api/__init__.py | 51 ------------------- tests/run_fake_peer.py | 4 +- tests/test_asset_transfer_sbe.py | 2 +- tests/test_contract_api.py | 4 +- tests/test_handler.py | 4 +- tests/test_simple_contract.py | 2 +- 17 files changed, 26 insertions(+), 77 deletions(-) delete mode 100644 src/fabric_contract_api/__init__.py diff --git a/RELEASING.md b/RELEASING.md index 2f3dbb8..6837ac9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -92,7 +92,7 @@ Artifacts are available in GitHub Actions run details: ```bash pip install dist/fabric-chaincode-python-*.whl -python -c "import src.fabric_shim; print('✓ Package imported successfully')" +python -c "import fabric_chaincode_python; print('✓ Package imported successfully')" ``` ## PyPI Publishing (Optional) diff --git a/examples/README.md b/examples/README.md index a13650c..81ec9a7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -80,8 +80,8 @@ for more details on the CCAAS architecture. ## Related Documentation -- [`fabric_shim` package](../src/fabric_shim/) — low-level chaincode shim. -- [`fabric_contract_api` package](../src/fabric_contract_api/) — high-level +- [`fabric_shim` package](../src/fabric_chaincode_python/fabric_shim/) — low-level chaincode shim. +- [`fabric_contract_api` package](../src/fabric_chaincode_python/fabric_contract_api/) — high-level contract API (Python port of `fabric-contract-api-go`). - [Hyperledger Fabric Official Docs](https://hyperledger-fabric.readthedocs.io/) - [CCAAS Architecture](https://hyperledger-fabric.readthedocs.io/en/latest/cc_service.html) diff --git a/examples/STRUCTURE.md b/examples/STRUCTURE.md index 0f93e7d..0f43db8 100644 --- a/examples/STRUCTURE.md +++ b/examples/STRUCTURE.md @@ -75,7 +75,7 @@ swap: - Single self-contained file. - Resolves the repository root at runtime by walking up the parent - chain looking for `src/fabric_shim` (or `src/fabric_contract_api`). + chain looking for `src/fabric_chaincode_python/fabric_shim` (or `src/fabric_chaincode_python/fabric_contract_api`). This makes the example runnable from any working directory. - Exposes a `build_chaincode()` factory (used by tests) and a `main()` entry point. @@ -153,8 +153,8 @@ Each README follows this template: Each example: -- References the parent `src/fabric_shim/` and / or - `src/fabric_contract_api/` for the framework. +- References the parent `src/fabric_chaincode_python/fabric_shim/` and / or + `src/fabric_chaincode_python/fabric_contract_api/` for the framework. - Uses the parent `requirements.txt` or specifies its own subset. - Operates independently once deployed — the chaincode process does not need the source tree at runtime as long as `PYTHONPATH=/app` is set diff --git a/examples/ccaas/fabric-contract-api/README.md b/examples/ccaas/fabric-contract-api/README.md index db9bf4a..f04f2a2 100644 --- a/examples/ccaas/fabric-contract-api/README.md +++ b/examples/ccaas/fabric-contract-api/README.md @@ -3,7 +3,7 @@ These examples use the high-level **`fabric_contract_api`** package, which is the Python equivalent of Go's [`fabric-contract-api-go`](https://github.com/hyperledger/fabric-contract-api-go). -You subclass [`Contract`](../../../src/fabric_contract_api/contractapi/contract.py) +You subclass [`Contract`](../../../src/fabric_chaincode_python/fabric_contract_api/contractapi/contract.py) and declare each transaction as a public async method — the framework handles dispatch, argument conversion, metadata generation, and schema validation automatically. diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-basic/README.md b/examples/ccaas/fabric-contract-api/asset-transfer-basic/README.md index d12e86b..f4e0e26 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-basic/README.md +++ b/examples/ccaas/fabric-contract-api/asset-transfer-basic/README.md @@ -168,5 +168,5 @@ examples/ccaas/fabric-contract-api/asset-transfer-basic/ ## References - [fabric-shim version of this sample](../../fabric-shim/asset-transfer-basic/) -- [fabric_contract_api documentation](../../../src/fabric_contract_api/README.md) +- [fabric_contract_api documentation](../../../../src/fabric_chaincode_python/fabric_contract_api/README.md) - [Hyperledger Fabric Chaincode as a Service](https://hyperledger-fabric.readthedocs.io/en/latest/cc_service.html) diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py b/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py index 2c8201c..ce82b3e 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py +++ b/examples/ccaas/fabric-contract-api/asset-transfer-basic/main.py @@ -34,7 +34,7 @@ from dataclasses import dataclass from typing import List, Optional -from src.fabric_contract_api import ( +from fabric_chaincode_python.fabric_contract_api import ( Contract, ContractChaincode, TransactionContextInterface, diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/README.md b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/README.md index afbe2e9..3bf89e3 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/README.md +++ b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/README.md @@ -137,5 +137,5 @@ examples/ccaas/fabric-contract-api/asset-transfer-sbe/ ## References - [fabric-shim version of this sample](../../fabric-shim/asset-transfer-sbe/) -- [fabric_contract_api documentation](../../../src/fabric_contract_api/README.md) +- [fabric_contract_api documentation](../../../../src/fabric_chaincode_python/fabric_contract_api/README.md) - [Hyperledger Fabric SBE docs](https://hyperledger-fabric.readthedocs.io/en/latest/chaincode4no.html#state-based-endorsement) diff --git a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py index 20a321e..d26b712 100644 --- a/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py +++ b/examples/ccaas/fabric-contract-api/asset-transfer-sbe/main.py @@ -45,7 +45,7 @@ from dataclasses import dataclass from typing import List -from src.fabric_contract_api import ( +from fabric_chaincode_python.fabric_contract_api import ( Contract, ContractChaincode, TransactionContextInterface, diff --git a/examples/ccaas/fabric-shim/README.md b/examples/ccaas/fabric-shim/README.md index 7bef10a..a8d224c 100644 --- a/examples/ccaas/fabric-shim/README.md +++ b/examples/ccaas/fabric-shim/README.md @@ -1,7 +1,7 @@ # fabric-shim Examples (CCAAS) These examples use the low-level **`fabric_shim`** package directly. You -subclass [`Chaincode`](../../../src/fabric_shim/interfaces.py) and write +subclass [`Chaincode`](../../../src/fabric_chaincode_python/fabric_shim/interfaces.py) and write `init` / `invoke` methods, dispatching on the function name by hand. ## When to choose fabric-shim diff --git a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py index a3a3004..342adb6 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py @@ -24,9 +24,9 @@ import json -from src.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface -from src.fabric_shim.server import start -from src.fabric_shim.response import ResponseCode +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface +from fabric_chaincode_python.fabric_shim.server import start +from fabric_chaincode_python.fabric_shim.response import ResponseCode from fabric_protos.peer import proposal_response_pb2 as pb diff --git a/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py b/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py index ee71d4e..25f9c8d 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py @@ -23,9 +23,9 @@ import logging from fabric_protos.peer import proposal_response_pb2 as pb -from src.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface -from src.fabric_shim.response import ResponseCode -from src.fabric_shim.server import start +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode, ChaincodeStubInterface +from fabric_chaincode_python.fabric_shim.response import ResponseCode +from fabric_chaincode_python.fabric_shim.server import start logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) diff --git a/src/fabric_contract_api/__init__.py b/src/fabric_contract_api/__init__.py deleted file mode 100644 index 678cf88..0000000 --- a/src/fabric_contract_api/__init__.py +++ /dev/null @@ -1,51 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -"""Hyperledger Fabric Contract API for Python. - -This package provides a high-level, contract-oriented programming model on -top of the low-level :mod:`fabric_shim` chaincode shim. It is the Python -equivalent of Hyperledger's ``fabric-contract-api-go``. - -Public surface (mirrors the Go layout):: - - fabric_contract_api.contractapi # Contract, ContractChaincode, ... - fabric_contract_api.serializer # TransactionSerializer, JSONSerializer - fabric_contract_api.metadata # Metadata types & schema helpers - fabric_contract_api.internal # Introspection & type validation helpers - -The most useful imports for application developers are:: - - from fabric_contract_api.contractapi import Contract, ContractChaincode - from fabric_contract_api.contractapi import TransactionContext, TransactionContextInterface -""" - -from .contractapi import ( # noqa: F401 (re-exported for convenience) - Contract, - ContractChaincode, - ContractInterface, - EvaluationContractInterface, - IgnoreContractInterface, - SystemContract, - SystemContractName, - TransactionContext, - TransactionContextInterface, - SettableTransactionContextInterface, -) -from .serializer import ( # noqa: F401 - TransactionSerializer, - JSONSerializer, -) - -__all__ = [ - "Contract", - "ContractChaincode", - "ContractInterface", - "EvaluationContractInterface", - "IgnoreContractInterface", - "SystemContract", - "SystemContractName", - "TransactionContext", - "TransactionContextInterface", - "SettableTransactionContextInterface", - "TransactionSerializer", - "JSONSerializer", -] diff --git a/tests/run_fake_peer.py b/tests/run_fake_peer.py index 4d8f946..7f623af 100644 --- a/tests/run_fake_peer.py +++ b/tests/run_fake_peer.py @@ -9,8 +9,8 @@ from fabric_protos.peer import chaincode_pb2 as cc_pb2 from fabric_protos.peer import proposal_response_pb2 as pr_pb -from src.fabric_shim.handler import Handler -from src.fabric_shim.interfaces import Chaincode +from fabric_chaincode_python.fabric_shim.handler import Handler +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode class TestChaincode(Chaincode): diff --git a/tests/test_asset_transfer_sbe.py b/tests/test_asset_transfer_sbe.py index 7684aba..71f1f84 100644 --- a/tests/test_asset_transfer_sbe.py +++ b/tests/test_asset_transfer_sbe.py @@ -37,7 +37,7 @@ def _load_example_module(): example_main = _load_example_module() -from src.fabric_contract_api import ( # noqa: E402 +from fabric_chaincode_python.fabric_contract_api import ( # noqa: E402 Contract, ContractChaincode, ) diff --git a/tests/test_contract_api.py b/tests/test_contract_api.py index 60b4caf..8970e3c 100644 --- a/tests/test_contract_api.py +++ b/tests/test_contract_api.py @@ -28,7 +28,7 @@ # Allow running tests directly from the repository root. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -from src.fabric_contract_api import ( # noqa: E402 +from fabric_chaincode_python.fabric_contract_api import ( # noqa: E402 Contract, ContractChaincode, JSONSerializer, @@ -207,7 +207,7 @@ async def test_unknown_function(self): async def test_json_serializer_round_trip(self): s = JSONSerializer() - from src.fabric_contract_api.metadata import ParameterMetadata, ComponentMetadata + from fabric_chaincode_python.fabric_contract_api.metadata import ParameterMetadata, ComponentMetadata value, err = s.from_string("true", bool, ParameterMetadata(name="param0", schema={"type": "boolean"}), ComponentMetadata()) self.assertIsNone(err) self.assertEqual(value, True) diff --git a/tests/test_handler.py b/tests/test_handler.py index f90f0a6..7a5243c 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -11,8 +11,8 @@ from fabric_protos.peer import chaincode_pb2 as cc_pb2 from fabric_protos.peer import proposal_response_pb2 as pr_pb -from src.fabric_shim.handler import Handler -from src.fabric_shim.interfaces import Chaincode +from fabric_chaincode_python.fabric_shim.handler import Handler +from fabric_chaincode_python.fabric_shim.interfaces import Chaincode class FakeContext: diff --git a/tests/test_simple_contract.py b/tests/test_simple_contract.py index 4d880e9..d88f2cf 100644 --- a/tests/test_simple_contract.py +++ b/tests/test_simple_contract.py @@ -43,7 +43,7 @@ def _load_example_module(): example_main = _load_example_module() -from src.fabric_contract_api import ( # noqa: E402 +from fabric_chaincode_python.fabric_contract_api import ( # noqa: E402 Contract, ContractChaincode, ) From 1734ce7321e9a63b58eb49f1c78196e223b4f2f4 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 01:58:05 -0400 Subject: [PATCH 09/12] docs: update roadmap to reflect completed tasks for PyPI publishing and migration Signed-off-by: kmilo --- ROADMAP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index bff4201..e3f3331 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -12,9 +12,9 @@ - [ ] Legacy chaincode mode (optional) - [ ] Complete documentation -- [ ] Publish to PyPI +- [x] Publish to PyPI ## Q1-Q2 2027 - [ ] Community adoption -- [ ] Migration to official project (if applicable) +- [x] Migration to official project (if applicable) From 4a194e2755cf9358f7dd3f9a27b4cf040a6681f0 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 02:24:17 -0400 Subject: [PATCH 10/12] feat: add OSSF Scorecard workflow for automated security analysis Signed-off-by: kmilo --- .github/workflows/ossf-scorecard.yml | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/ossf-scorecard.yml diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml new file mode 100644 index 0000000..ec6171e --- /dev/null +++ b/.github/workflows/ossf-scorecard.yml @@ -0,0 +1,41 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Scorecard + +on: + branch_protection_rule: {} # re-evaluate if branch protection rules change + push: + branches: [main] + schedule: + - cron: '23 6 1,16 * *' # 1st and 16th of each month at 06:23 UTC — keeps the badge fresh + workflow_dispatch: {} # allows manually triggering the first run + +permissions: read-all + +jobs: + scorecard: + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + security-events: write # Upload SARIF to code scanning + id-token: write # OIDC: required for publish_results + + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + persist-credentials: false + + - name: Run Scorecard analysis + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + with: + results_file: results.sarif + results_format: sarif + repo_token: ${{ secrets.GITHUB_TOKEN }} + publish_results: true # publishes the badge results + + - name: Upload to code-scanning + uses: github/codeql-action/upload-sarif@1190a975f95ce23525efb6a3fc21ea29567c1b52 # v3 + with: + sarif_file: results.sarif \ No newline at end of file From 8da9e666ab91c6dbf3b00c69a700c50121318e55 Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 02:27:44 -0400 Subject: [PATCH 11/12] fix: update actions/checkout version to v7.0.1 for improved stability Signed-off-by: kmilo --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index ec6171e..82d5cc8 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -23,7 +23,7 @@ jobs: id-token: write # OIDC: required for publish_results steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false From c08495b79f0ac9e7a065257889583a61ed85930f Mon Sep 17 00:00:00 2001 From: kmilo Date: Sun, 4 Oct 2026 02:38:45 -0400 Subject: [PATCH 12/12] feat: add editable install for local development in test workflow Signed-off-by: kmilo --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 79f8c1d..818b71f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,6 +35,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install -r requirements.txt -r requirements-dev.txt + python -m pip install -e . - name: Run tests run: python -m pytest -q