diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml new file mode 100644 index 0000000..82d5cc8 --- /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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + 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 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. 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 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) 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/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) diff --git a/examples/README.md b/examples/README.md index 9e73f6b..81ec9a7 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? @@ -78,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/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..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. @@ -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-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 633f09c..ce82b3e 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 fabric_chaincode_python.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/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 143341a..d26b712 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 fabric_chaincode_python.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/README.md b/examples/ccaas/fabric-shim/README.md index 31e6bf2..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 @@ -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. diff --git a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py index 8a0e4f1..342adb6 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-basic/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-basic/main.py @@ -23,26 +23,10 @@ """ 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 -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-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..25f9c8d 100644 --- a/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py +++ b/examples/ccaas/fabric-shim/asset-transfer-sbe/main.py @@ -21,26 +21,11 @@ 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 -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/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 diff --git a/pyproject.toml b/pyproject.toml index 20490c5..4d0257c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,27 @@ [build-system] -requires = ["setuptools", "wheel"] +requires = ["setuptools>=77", "wheel"] build-backend = "setuptools.build_meta" [project] name = "fabric-chaincode-python" -version = "2.5.1" -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.11", + "Topic :: Software Development :: Libraries", +] + dependencies = [ "grpcio>=1.83.1,<2.0.0", "grpclib>=0.4.3", @@ -16,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/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_contract_api/__init__.py b/src/fabric_chaincode_python/fabric_contract_api/__init__.py similarity index 100% rename from src/fabric_contract_api/__init__.py rename to src/fabric_chaincode_python/fabric_contract_api/__init__.py 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 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" 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, )