Minimal pycapnp
This commit is contained in:
66
.github/workflows/docs.yml
vendored
66
.github/workflows/docs.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, main]
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v3
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "pyproject.toml"
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install project and dependencies
|
||||
run: |
|
||||
# This replaces requirements.txt, setup.py build, and pip install .
|
||||
# It reads pyproject.toml, builds pycapnp, and installs it into a fast .venv
|
||||
uv sync --all-extras
|
||||
|
||||
- name: Build documentation
|
||||
run: |
|
||||
# uv run executes sphinx within the isolated virtual environment
|
||||
uv run sphinx-build docs build/html
|
||||
|
||||
- name: Upload documentation artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: html-docs
|
||||
path: build/html
|
||||
|
||||
deploy:
|
||||
needs: docs
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Download documentation artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: html-docs
|
||||
path: build/html
|
||||
|
||||
- name: Add .nojekyll file
|
||||
run: touch build/html/.nojekyll
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./build/html
|
||||
publish_branch: gh-pages
|
||||
119
.github/workflows/wheels.yml
vendored
119
.github/workflows/wheels.yml
vendored
@@ -3,135 +3,48 @@ name: Build
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
name: Build wheels on ${{ matrix.os }} ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
wheels:
|
||||
strategy:
|
||||
max-parallel: 99
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
arch: x86_64
|
||||
- os: ubuntu-latest
|
||||
arch: i686
|
||||
# Native ARM runner — no QEMU, drastically faster than emulated aarch64
|
||||
- os: ubuntu-24.04-arm
|
||||
arch: aarch64
|
||||
|
||||
- os: macos-15
|
||||
arch: x86_64
|
||||
- os: macos-15
|
||||
arch: arm64
|
||||
# Disabled until someone figures out how to build capnproto for arm64 and x86_64 simultaneously
|
||||
# - os: macOS-latest
|
||||
# arch: universal2
|
||||
|
||||
- os: windows-2022
|
||||
arch: AMD64
|
||||
- os: windows-2022
|
||||
arch: x86
|
||||
# Does not build currently
|
||||
# - os: windows-2019
|
||||
# arch: ARM64
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v3.4.1
|
||||
with:
|
||||
output-dir: wheelhouse
|
||||
- uses: pypa/cibuildwheel@v3.4.1
|
||||
env:
|
||||
CIBW_BUILD: cp312-*
|
||||
CIBW_ARCHS: ${{ matrix.arch }}
|
||||
# Drop EOL CPython 3.8 (also avoids the macOS x86_64-only-installer warning)
|
||||
CIBW_SKIP: "cp38-*"
|
||||
CIBW_TEST_REQUIRES: pytest pytest-asyncio
|
||||
CIBW_TEST_COMMAND: pytest {project}
|
||||
# Force libcapnp to build for the target arch on macOS (the runner
|
||||
# is arm64, so without this an x86_64 wheel ends up linking against
|
||||
# an arm64 libkj/libcapnp and failing to load at test time).
|
||||
CIBW_TEST_REQUIRES: pytest
|
||||
CIBW_TEST_COMMAND: python -m pytest {project}/test
|
||||
CIBW_CONFIG_SETTINGS: force-bundled-libcapnp=true
|
||||
CMAKE_OSX_ARCHITECTURES: "${{ runner.os == 'macOS' && matrix.arch || '' }}"
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: cibw-wheels-${{ matrix.os }}-${{ matrix.arch }}-${{ strategy.job-index }}
|
||||
path: ./wheelhouse/*.whl
|
||||
name: wheels-${{ matrix.arch }}-${{ matrix.os }}
|
||||
path: wheelhouse/*.whl
|
||||
|
||||
# Exotic arches built via QEMU emulation — very slow (often >1h), so only run
|
||||
# on release tags rather than every push/PR.
|
||||
build_wheels_exotic:
|
||||
name: Build wheels (exotic) ${{ matrix.arch }}
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [ppc64le, s390x]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Build wheels
|
||||
uses: pypa/cibuildwheel@v3.4.1
|
||||
with:
|
||||
output-dir: wheelhouse
|
||||
env:
|
||||
CIBW_ARCHS: ${{ matrix.arch }}
|
||||
CIBW_SKIP: "cp38-*"
|
||||
# Tests under QEMU are extremely slow; skip them for these arches.
|
||||
CIBW_TEST_SKIP: "*"
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: cibw-wheels-${{ matrix.arch }}
|
||||
path: ./wheelhouse/*.whl
|
||||
|
||||
build_sdist:
|
||||
name: Build source distribution
|
||||
source:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Build sdist
|
||||
run: pipx run build --sdist
|
||||
|
||||
- uses: astral-sh/setup-uv@v3
|
||||
- run: uv build --sdist
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: cibw-sdist
|
||||
name: sdist
|
||||
path: dist/*.tar.gz
|
||||
|
||||
lint:
|
||||
name: Lint and format with ruff
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Lint and format with ruff
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --group lint
|
||||
ruff check .
|
||||
ruff format --check .
|
||||
|
||||
# upload_pypi:
|
||||
# needs: [build_wheels, build_sdist]
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
||||
# steps:
|
||||
# - uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# # unpacks default artifact into dist/
|
||||
# # if `name: artifact` is omitted, the action will create extra parent dir
|
||||
# name: artifact
|
||||
# path: dist
|
||||
|
||||
# - uses: pypa/gh-action-pypi-publish@v1.5.0
|
||||
# with:
|
||||
# user: __token__
|
||||
# password: ${{ secrets.PYPI_PASSWORD_RELEASE }}
|
||||
|
||||
# # password: ${{ secrets.PYPI_PASSWORD }}
|
||||
# # repository_url: https://test.pypi.org/legacy/
|
||||
- uses: astral-sh/setup-uv@v3
|
||||
- run: uvx ruff@0.16.8 check .
|
||||
- run: uvx ruff@0.16.8 format --check .
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -53,3 +53,7 @@ example
|
||||
|
||||
# capnp files
|
||||
*.capnp
|
||||
|
||||
.venv/
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
|
||||
408
CHANGELOG.md
408
CHANGELOG.md
@@ -1,408 +0,0 @@
|
||||
## v2.2.4 (2026-07-03)
|
||||
- Fix memory leak in `_DynamicCapabilityClient._send_helper()` (#398)
|
||||
- Fix SIGSEGV (NULL pointer dereference) on malformed Text field
|
||||
- Update default bundled capnproto to 1.4.0
|
||||
- Migrate project dependency management, testing, and CI to `uv`
|
||||
- Consolidate configurations into `pyproject.toml` (removed `Pipfile` and `requirements.txt`)
|
||||
- Apply small ruff linting fixes
|
||||
|
||||
## v2.2.3 (2026-05-30)
|
||||
- Fix test failures on Python 3.14 (#394)
|
||||
- Refine documentation for PyCustomMessageBuilder (#395)
|
||||
- Replace black and flake8 with ruff for linting and formatting
|
||||
- ci: deploy docs to gh-pages on tagged releases
|
||||
- Add scripts/release-pypi.sh for downloading CI artifacts and uploading to PyPI
|
||||
|
||||
## v2.2.2 (2026-01-16)
|
||||
- Revert Data fields to bytes and add get_data_as_view for zero-copy access (#390)
|
||||
- Fix use-after-free in async write causing corruption with large payloads (#392)
|
||||
- Update macOS builds from 13 to 15 (#393)
|
||||
|
||||
## v2.2.1 (2025-10-21)
|
||||
- Make `message.to_dict()` return bytes for DATA type field (#386)
|
||||
|
||||
## v2.2.0 (2025-09-12)
|
||||
- Add binary support in dictionaries via base64 encoding (#351)
|
||||
- Add structure-free read_multiple_bytes_packed (#378)
|
||||
- Support python custom message builder and make Data field's type return MemoryView (#380)
|
||||
|
||||
## v2.1.0 (2025-09-04)
|
||||
- Add Python 3.13 support
|
||||
* Python 3.8 is still enabled but will be disabled if changes are needed that prevent compatibility with newer versions of Python (e.g. 3.14)
|
||||
* Disabling experimental Python 3.14 build as it currently causes build issues
|
||||
- Upgrade to Cython version 3
|
||||
- Include \_custom_build in sdist
|
||||
- Remove usage of deprecated kj::mvCapture functions
|
||||
- Make license information SPDX-compatible
|
||||
- Generate a new certificate that is compatible with strict x509 checking
|
||||
- Avoid storm of 'warning: moving a temporary object prevents copy elision'
|
||||
- cibuildwheel updates
|
||||
- Fix deprecation warning when importing a schema
|
||||
|
||||
## v2.0.0 (2024-01-19)
|
||||
- Updated link for mailing list in README
|
||||
|
||||
## v2.0.0b2 (2023-11-25)
|
||||
- Fix broken test in test_load (#329)
|
||||
- Update README example to async code (#331)
|
||||
- Fix 'AttributeError: '\_UnixSelectorEventLoop' object has no attribute 'call_soon'
|
||||
- Delete and update some Python 3.7-specific todo notes
|
||||
- Make a server fail early when the KJ loop is not running
|
||||
- Update documentation to async code (#331) (#332)
|
||||
- Fix retransmit bug for large messages causing message corruption
|
||||
- Unlock the GIL for all capnp functions that do IO
|
||||
- Handle exceptions from server callbacks
|
||||
- Disable the use of ninja for windows builds
|
||||
- DynamicCapabilityClient fix
|
||||
- Make `reraise_kj_exception` available to downstream
|
||||
- Support `_DynamicListReader` in `_setDynamicField`
|
||||
- Fix re-raising of KjException
|
||||
- Allow cancellation of all capability contexts
|
||||
- Corner case for cancelled server methods that raise exceptions
|
||||
- Some fixes to the magic import system
|
||||
|
||||
## v2.0.0b1 (2023-10-03)
|
||||
- Update to bundled capnproto-1.0.1
|
||||
- Remove support for Python 3.7
|
||||
- Use custom build backend to support build args (#328)
|
||||
- Update Cython version and Python to 3.12 (#320)
|
||||
- Wrap all capnp code in a context-manager to avoid segfaults (#317)
|
||||
- Schema loading from the wire (#307)
|
||||
- Make pycapnp more GIL friendly (#308)
|
||||
- Use cibuildwheel in ci (#309)
|
||||
- Integrate the KJ event loop into Python's asyncio event loop (#310)
|
||||
- Allow capability implementation methods to be `async` (#312)
|
||||
- Allow reading and writing messages from sockets in `async` mode (#313)
|
||||
- Remove the synchronous RPC mode (#315)
|
||||
|
||||
## v1.3.0 (2023-01-26)
|
||||
- Update to bundled capnproto-0.10.3
|
||||
- Add Python 3.11 to Github Actions builds (#306)
|
||||
- Prevent race condition in example code (#305)
|
||||
|
||||
## v1.2.2 (2022-12-01)
|
||||
- Update bundled bundled capnp to 0.8.1 due to CVE-2022-46149
|
||||
- Bundle lib/capnp_api.h and helpers/capabilityHelper.cpp (#301)
|
||||
- Avoid reading random values for reader options from dangling reference (#300)
|
||||
|
||||
## v1.2.1 (2022-09-11)
|
||||
- Fix packaging for Apple Silicon
|
||||
|
||||
## v1.2.0 (2022-08-29)
|
||||
- Added support for Apple Silicon
|
||||
|
||||
## v1.1.1 (2022-05-23)
|
||||
- Added Python 3.10 support
|
||||
- aarch64 wheel support
|
||||
- Fix doc string for `_DynamicResizableListBuilder`
|
||||
- fix for unreleased buffers under mmap (issue 280)
|
||||
|
||||
## v1.1.0 (2021-06-09)
|
||||
- Validated compatibility with Python 3.10.0b2
|
||||
- Remove all bare except
|
||||
- Improve `_StructModuleWhich` to inherit from `enum.Enum`
|
||||
- Add Union on top level union messages
|
||||
- Fixed memory leak in `_SegmentArrayMessageReader`
|
||||
- Removed many pycodestyle warnings
|
||||
- Avoid crash if `__file__` is not set by importer
|
||||
- Fixed module.pyx `_set_<field>` for boolean fields
|
||||
- Fixed setup.py.tmpl support for `*.c++` files
|
||||
- Fixed `_gen.py` for python3 as `dict_keys` object are not indexable.
|
||||
- Add test data to sdist
|
||||
- Add `pyproject.yaml`
|
||||
- Add missing inheritance to `_Schema` for `_StructSchema`
|
||||
|
||||
## v1.0.0 (2020-11-20)
|
||||
- Validated Python 3.9 (3.7 and 3.8 are also supported)
|
||||
- Updated package to include LICENSE file
|
||||
- Updated examples to avoid run_forever() as ctrl+c will not work
|
||||
- Adding xfail to pytest cases which fail sometimes due to network port oddities (please use asyncio, as Python handles things more gracefully)
|
||||
|
||||
## v1.0.0b2 (2020-06-14)
|
||||
- Minimum capnproto version is now 0.8.0
|
||||
- Added asyncio ssl calculator test
|
||||
- Added poll_once to TwoPartyServer API
|
||||
- More cleanup
|
||||
- Fix absolute and circular imports
|
||||
- Fix Promise aliasing issue (Promise to \_Promise)
|
||||
- Documentation update
|
||||
- Updated installation instructions
|
||||
- Added RPC documentation for asyncio
|
||||
|
||||
## v1.0.0b1 (2019-12-26)
|
||||
- Python 3.7+ required (asyncio support)
|
||||
- TLS/SSL support using asyncio
|
||||
- Windows support
|
||||
- General cleanup
|
||||
- May be incompatible with code written for pycapnp 0.6.4 and lower
|
||||
- Removing pypandoc/pandoc packaging requirement
|
||||
- Minimum capnproto version is now 0.7.0
|
||||
|
||||
## v0.6.4 (2019-01-31)
|
||||
- Fix bugs in `read_multiple_bytes` (thanks to @tsh56)
|
||||
- Remove end-of-life Python versions 2.6, 3.2, and 3.3. Add CI tests for 3.6
|
||||
- Expose SchemaParser in Cython header
|
||||
|
||||
## v0.6.3 (2018-01-14)
|
||||
- Bump bundled capnp version to v0.6.1 (thanks to @E8Yuval)
|
||||
- Fix a memleak in RemotePromise (thanks to @E8Yuval)
|
||||
|
||||
## v0.6.2 (2017-11-30)
|
||||
- Add support for buffers/memoryviews in `from_bytes` (thanks to @aldanor)
|
||||
|
||||
## v0.6.1 (2017-07-27)
|
||||
- Fixed upload to PyPi (forgot to cythonize)
|
||||
|
||||
## v0.6.0 (2017-07-27)
|
||||
- Update bundled capnp version to v0.6.0 and fix related problems (thanks to @benmoran)
|
||||
- Fix memleak with KjException (thanks to @tsh56)
|
||||
|
||||
## v0.5.12 (2017-04-18)
|
||||
- Bump bundled capnp version to v0.5.3.1
|
||||
|
||||
## v0.5.11 (2017-04-10)
|
||||
- Make enums hashable (thanks to @madeleine-empirical)
|
||||
- Rework logic on when to build bundled libcapnp. Fixes cross-compilation (thanks to @benizl)
|
||||
- Add traversal_limit_in_words and nesting_limit to RPC classes (thanks to @asilversempirical)
|
||||
- Include class attributes in __dir__. This allows for code completion of class methods (thanks to @chaoflow )
|
||||
- Allow setting lists with python tuples (thanks to @chaoflow)
|
||||
- Fix traversal_limit_in_words and nesting_limit being ignored by `from_bytes` (thanks to @plesner)
|
||||
|
||||
## v0.5.10 (2016-11-28)
|
||||
- Fix bug that prevented event loop from actually being lazy initialized
|
||||
- Fix possible recursive loop in KjException
|
||||
- Add `clear_write_flag` method to builder classes
|
||||
|
||||
## v0.5.9 (2016-07-07)
|
||||
- Make the event loop be lazy initialized
|
||||
- Add support for segment (de)serialization (thanks to @gcv). See to_segments/from_segments methods.
|
||||
- Fix response objects not referencing parents correctly
|
||||
- Add test for large reads
|
||||
|
||||
## v0.5.8 (2016-05-27)
|
||||
- Fix build problem with Cython v0.24
|
||||
- Include the changelog in the manifest (should fix install problems if pandoc is present)
|
||||
- Include the traceback in exceptions
|
||||
- Make sure to encode to utf-8, not the default encoding (thanks to @novas0x2a)
|
||||
- Add --libcapnp-url option in installer to allow installing arbitrary libcapnp versions
|
||||
- Support mmap objects for reading with from_bytes (thanks to @bpiwowar)
|
||||
- Change read_multiple and read_multiple_packed to copy by default
|
||||
- Fix mistakenly discarding the file parameter on reads
|
||||
- Add reraise_kj_exception to the prettyPrint functions. (thanks to @kdienes)
|
||||
- Fix KjException init (missing wrapper). (thanks to @E8-Storage)
|
||||
- Add `result_type` to InterfaceMethodSchema
|
||||
|
||||
|
||||
## v0.5.7 (2015-06-16)
|
||||
- Update bundled libcapnp to v0.5.2
|
||||
- Add warnings for using old restorer methods. You should use `bootstrap` instead
|
||||
- Fix warning from PyEventPort
|
||||
- Handle AnyPointers better as arguments to RPC functions
|
||||
- Add support for using keyword arguments with a named struct in an RPC
|
||||
- Add bootstrap method to TwoPartyServer
|
||||
- Add `init` method to lists
|
||||
- Add support for unix sockets in RPC
|
||||
|
||||
## v0.5.6 (2015-04-13)
|
||||
- Fix a serious bug in TwoPartyServer that was preventing it from working when passed a string address.
|
||||
- Fix bugs that were exposed by defining KJDEBUG (thanks @davidcarne for finding this)
|
||||
|
||||
|
||||
## v0.5.5 (2015-03-06)
|
||||
- Update bundled C++ libcapnp to v0.5.1.2 security release
|
||||
|
||||
|
||||
## v0.5.4 (2015-03-02)
|
||||
- Update bundled C++ libcapnp to v0.5.1.1 security release
|
||||
- Add bootstrap RPC methods
|
||||
- Fix possible segfault when importing multiple schemas
|
||||
|
||||
|
||||
## v0.5.3 (2015-02-23)
|
||||
- Fix possible crash due to bad destructor ordering in MessageReader (by @JohnEmhoff)
|
||||
- Default to no longer using cython
|
||||
|
||||
|
||||
## v0.5.2 (2015-02-20)
|
||||
- Add read\_multiple\_bytes/read\_multiple\_bytes\_packed methods
|
||||
- Added Python 3.4 to the travis build matrix
|
||||
- Bump version for bundled C++ libcapnp to v0.5.1
|
||||
|
||||
|
||||
## v0.5.1 (2014-12-27)
|
||||
- Remove installation dependency on cython. We now have no dependencies since libcapnp will automatically build as well.
|
||||
|
||||
|
||||
## v0.5.0 (2014-12-15)
|
||||
- Timer class `capnp.getTimer()`
|
||||
- pycapnp is now thread-safe and allows an event loop to be run in each thread
|
||||
- You must destroy and re-create the event loop to get this functionality (see `test_threads.py`)
|
||||
- Inheritance now works correctly for interfaces (previously inherited methods were inaccessible from pycapnp)
|
||||
- Add ability to import modules with dashes or spaces. Use underscores in place of them
|
||||
- `from_bytes` with builder=True is no longer zero copy. It never worked correctly, and is much safer now
|
||||
- Add `num_first_segment_words` argument wherever message creation can occur
|
||||
- Allow restoring a null objectId by passing None to restore
|
||||
- Support ordered dictionary in `to_dict`
|
||||
- Add ListSchema class and schemas for native types under `capnp.types` which completes all the Schemas needed to be wrapped. See `test_schema.py` for examples using it
|
||||
- Add automatic build of C++ libcapnp if it's not detected on the system. Also add flags --force-bundled-libcapnp and --force-system-libcapnp respectively
|
||||
|
||||
|
||||
## v0.4.6 (2014-9-10)
|
||||
- Fix build for new 0.21 release of Cython. 0.21 is now the minimum supported version of Cython.
|
||||
|
||||
|
||||
## v0.4.5 (2014-6-26)
|
||||
- Fix `to_dict` not converting enums to strings
|
||||
|
||||
|
||||
## v0.4.4 (2014-04-25)
|
||||
- Fix compilation problem with gcc 4.8
|
||||
|
||||
|
||||
## v0.4.3 (2014-02-18)
|
||||
- Fix problem with uninitialized unions in \_from\_dict
|
||||
- Add accesible version numbers for C++ libcapnp
|
||||
|
||||
|
||||
## v0.4.2 (2014-02-13)
|
||||
- Remove onDrained since it was removed upstream
|
||||
- Replace usage of strings as enum type with custom `_DynamicEnum` class.
|
||||
- Also change `Struct.which()` method to be a property `Struct.which` and return an enum type (`_DynamicEnumField`, which behaves much like `_DynamicEnum`).
|
||||
- TwoPartyServer.run_forever() now will handle more than 1 simulataneous connection.
|
||||
- Change exception wrapper to detect and raise AttributeError for field lookup exceptions (Fixes problem in Python3.x `__dir__`)
|
||||
- Allow setting of fields with python dicts.
|
||||
|
||||
|
||||
## 0.4.1 (2013-12-18)
|
||||
- Remove python 3.2 from travis tests. Python 3.2 still should work fine, but it's more trouble than it's worth to write unicode tests that work in both it and Python2.
|
||||
- Fix problems with null characters in Text/Data fields. Fixes #19
|
||||
|
||||
|
||||
## 0.4.0 (2013-12-12)
|
||||
- Initial working version of RPC
|
||||
- Add get_root_as_any to _MessageReader
|
||||
- Add capnp.pxd for public declarations of cython classes
|
||||
- Fix problems compiling with gcc4.7
|
||||
|
||||
|
||||
## v0.3.18 (2013-11-05)
|
||||
- Change naming of ReaderOption parameters to be pep8 compliant
|
||||
|
||||
|
||||
## v0.3.17 (2013-11-05)
|
||||
- Add ReaderOptions to read/read_packed/from_bytes
|
||||
|
||||
|
||||
## v0.3.16 (2013-10-28)
|
||||
- Add defaults flag to capnp-json. Also remove 'which' field
|
||||
- Add capnp-json serializer script. Also fix bugs in from_dict
|
||||
- Fix build for clang/python3. Also remove -fpermissive
|
||||
- Add `as_builder` method to Struct Reader
|
||||
- Add warning when writing the same message more than once
|
||||
- First working version of capability interfaces
|
||||
- Wrap InterfaceSchema
|
||||
- Fix setting string fields to support all types of strings
|
||||
- Fix changed API for DynamicObject/ObjectPointer
|
||||
|
||||
|
||||
## v0.3.15 (2013-09-19)
|
||||
- Add not having installed the C++ libcapnp library to 'Common Problems'
|
||||
- Add _short_str function for use in capnp_test_pycapnp.py
|
||||
- Add test script for testing with https://github.com/kaos/capnp_test
|
||||
- Add handling of DynamicObject
|
||||
- Fix lists of lists or dicts for from_dict
|
||||
|
||||
|
||||
## v0.3.14 (2013-09-04)
|
||||
- Fix problem with to_dict
|
||||
|
||||
|
||||
## v0.3.13 (2013-09-04)
|
||||
- Add _DynamicStructBuilder.to_bytes() and <struct module>.from_bytes()
|
||||
- Change == on StructSchema to return cbool
|
||||
- Add Builder and Reader ABCs for each struct type
|
||||
|
||||
## v0.3.12 (2013-09-03)
|
||||
- Fix handling of empty path '' in load_module
|
||||
- Add from_dict
|
||||
- Fix bug in exception handling for which(). Also standardize exceptions.
|
||||
- Change import hook to require modules to end in '_capnp'
|
||||
- Add import monkey patch function.
|
||||
- Change naming for functions to conform to PEP 8. Also deprecate old read/write API
|
||||
- Update preferred method for reading/writing messages from files
|
||||
|
||||
## v0.3.11 (2013-09-01)
|
||||
- Forgot to change project name in setup.py
|
||||
|
||||
## v0.3.10 (2013-09-01)
|
||||
- Change all references to old project name (change from capnpc-python-cpp to pycapnp)
|
||||
- Change DynamicValue.Reader lists to be returned as _DynamicListReader
|
||||
- Unify setters for DynamicList and DynamicStruct
|
||||
- Add shortcuts for reading from / writing to files. In Python, it doesn't make much sense to force people to muck around with MessageReaders and MessageBuilders since everything is landing on the heap anyway. Instead, let's make it easy: MyType.read[Packed]From(file) reads a file and returns a MyType reader. MyType.newMessage() returns a MyType builder representing the root of a new message. You can call this builder's write[Packed]To(file) method to write it to a file.
|
||||
- Store Builders by value rather than allocate them separately on the heap (matches treatment of Readers). v0.3 fixes the bug that made this not work.
|
||||
- Wrap MessageBuilder::setRoot().
|
||||
- Add tests based on TestAllTypes from the C++ test.capnp. Fix problems uncovered in capnp.pyx.
|
||||
- Implement __str__ and __repr__ for struct and list builders. __str__ uses prettyPrint while __repr__ shows the type name and the low-whitespace stringification. Also implement __repr__ for StructSchema, just because why not?
|
||||
|
||||
|
||||
## v0.3.9 (2013-08-30)
|
||||
- Change load to use a global SchemaParser. Make structs settable as field
|
||||
- Add docstrings for new functions and _DynamicResizableListBuilder
|
||||
|
||||
|
||||
## v0.3.8 (2013-08-29)
|
||||
- Add initial tests
|
||||
- Add _capnp for original Cython module. Meant for testing.
|
||||
- Lowercase schema so it conforms to member naming conventions
|
||||
- Expose _StructSchema's raw node
|
||||
- Add some useful _StructSchema, reader, and builder methods
|
||||
- Add full orphan functionality. Also, allow special orphan lists
|
||||
- Finish up adding docstrings to all public classes/methods
|
||||
|
||||
|
||||
## v0.3.7 (2013-08-26)
|
||||
- Add a ton of docstrings and add to official docs
|
||||
- Add DynamicOrphan
|
||||
|
||||
|
||||
## v0.3.6 (2013-08-26)
|
||||
- Add intersphinx for linking to python docs
|
||||
- Add C++ library version check
|
||||
|
||||
|
||||
## v0.3.5 (2013-08-25)
|
||||
- Add handling of constants in schemas
|
||||
- Fix new error with DynamicValue.Builder no longer being copyable
|
||||
|
||||
|
||||
## v0.3.4 (2013-08-22)
|
||||
- Fix Void namespace change
|
||||
- Updated capnp schema to conform with new union rules
|
||||
|
||||
|
||||
## v0.3.3 (2013-08-22)
|
||||
- Fix for the removal of DynamicUnion from the C++ API
|
||||
|
||||
|
||||
## v0.3.2 (2013-08-21)
|
||||
- Add MANIFEST.in to include README
|
||||
|
||||
|
||||
## v0.3.1 (2013-08-21)
|
||||
- Update docs with lines about upgrading setuptools
|
||||
|
||||
|
||||
## 0.3.0 (2013-08-21)
|
||||
- Initial commit of docs
|
||||
- Add querying unnamed enums to structs
|
||||
|
||||
|
||||
## 0.2.1 (2013-08-13)
|
||||
- Fix enum interface change for benchmark
|
||||
- Random formatting cleanup
|
||||
- Allow import paths in the schema loader
|
||||
- Add travis CI
|
||||
|
||||
|
||||
## 0.2.0 (2013-08-12)
|
||||
- Initial working version
|
||||
24
MANIFEST.in
24
MANIFEST.in
@@ -1,18 +1,6 @@
|
||||
include README.md
|
||||
include LICENSE.md
|
||||
include CHANGELOG.md
|
||||
include requirements.txt
|
||||
include buildutils/*
|
||||
include _custom_build/*
|
||||
include capnp/lib/capnp_api.h
|
||||
include Pipfile
|
||||
include tox.ini
|
||||
recursive-include examples *.capnp
|
||||
recursive-include examples *.cert
|
||||
recursive-include examples *.key
|
||||
recursive-include examples *.py
|
||||
recursive-include test *.binary
|
||||
recursive-include test *.capnp
|
||||
recursive-include test *.packed
|
||||
recursive-include test *.txt
|
||||
recursive-include test *.py
|
||||
include README.md LICENSE.md
|
||||
include buildutils/*.py
|
||||
include _custom_build/*.py
|
||||
recursive-include capnp *.py *.pyx *.pxd *.h *.cpp
|
||||
recursive-include test *.py *.capnp *.binary *.txt
|
||||
exclude capnp/lib/capnp.cpp capnp/lib/capnp.h capnp/lib/capnp_api.h
|
||||
|
||||
299
README.md
299
README.md
@@ -1,265 +1,62 @@
|
||||
# pycapnp
|
||||
# pycapnp for openpilot
|
||||
|
||||
[](https://github.com/capnproto/pycapnp/actions)
|
||||
[](https://github.com/capnproto/pycapnp/actions)
|
||||
[](https://badge.fury.io/py/pycapnp)
|
||||
A serialization-only fork of [pycapnp](https://github.com/capnproto/pycapnp).
|
||||
The `minimal` branch starts at upstream commit
|
||||
`a0cb5cdf0673481f2f9850541f4d42b89698c476`, including the `from_dict` reference-cycle
|
||||
fix in upstream PR #407.
|
||||
|
||||
[Cap'n'proto Mailing List](https://github.com/capnproto/capnproto/discussions) [Documentation](https://capnproto.github.io/pycapnp)
|
||||
The supported surface is based on openpilot at
|
||||
`7f6f13997c3c9b8e27e1581583f61e3fcabc5151`, including its opendbc checkout:
|
||||
|
||||
- Explicit schema loading with `capnp.load()` and schema imports.
|
||||
- Dynamic structs, lists, enums, unions, nested groups, and constants.
|
||||
- Message construction, field access (including cached `_get_by_field` and
|
||||
`_set_by_field`), dictionaries, reader/builder copies, and pickling.
|
||||
- Unpacked `to_bytes()`, `from_bytes()`, and `read_multiple_bytes()`, including
|
||||
traversal/nesting limits and readers that retain their underlying message.
|
||||
- Schema reflection for cereal, CAN conversion, WebRTC, fuzzing, and replay tools.
|
||||
|
||||
## Requirements
|
||||
Removed: RPC/capabilities, promises, KJ event loops, asyncio/network streams,
|
||||
packed serialization, file-descriptor I/O, segment APIs, borrowed Data views,
|
||||
custom allocators, orphans/resizable lists, AnyPointer wrappers, type registration,
|
||||
the Python schema import hook, and the Cython code generator. Their examples,
|
||||
tests, docs, dependencies, and unsupported-platform CI were removed too.
|
||||
`remove_import_hook()` remains a no-op for cereal/opendbc compatibility.
|
||||
|
||||
* C++14 supported compiler
|
||||
- gcc 6.1+ (5+ may work)
|
||||
- clang 6 (3.4+ may work)
|
||||
- Visual Studio 2017+
|
||||
* cmake (needed for bundled capnproto)
|
||||
- ninja (macOS + Linux)
|
||||
- Visual Studio 2017+
|
||||
* capnproto-1.0 (>=0.8.0 will also work if linking to system libraries)
|
||||
- Not necessary if using bundled capnproto
|
||||
* Python development headers (i.e. Python.h)
|
||||
- Distributables from python.org include these, however they are usually in a separate package on Linux distributions
|
||||
This is intentionally not a full upstream API replacement. The import and
|
||||
package names remain `capnp` and `pycapnp`. It must replace the installed pycapnp,
|
||||
not be installed alongside another distribution providing `capnp`.
|
||||
|
||||
32-bit Linux requires that capnproto be compiled with `-fPIC`. This is usually set correctly unless you are compiling canproto yourself. This is also called `-DCMAKE_POSITION_INDEPENDENT_CODE=1` for cmake.
|
||||
## Build and test
|
||||
|
||||
pycapnp has additional development dependencies, including cython and pytest. See requirements.txt for them all.
|
||||
Targets: CPython 3.12, Linux x86_64/aarch64, and macOS arm64. A C++14 compiler and
|
||||
CMake are required for a bundled build.
|
||||
|
||||
|
||||
## Building and installation
|
||||
|
||||
Install with `pip install pycapnp`. You can set the CC environment variable to control which compiler is used, ie `CC=gcc-8.2 pip install pycapnp`.
|
||||
|
||||
Or you can clone the repo like so:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/capnproto/pycapnp.git
|
||||
cd pycapnp
|
||||
pip install .
|
||||
```sh
|
||||
uv venv --python 3.12
|
||||
uv pip install cython setuptools wheel pkgconfig pytest build
|
||||
.venv/bin/python setup.py build_ext --inplace --force-bundled-libcapnp
|
||||
.venv/bin/python -m pytest
|
||||
.venv/bin/python -m build -Cforce-bundled-libcapnp=true
|
||||
```
|
||||
|
||||
By default, the setup script will automatically use the locally installed Cap'n Proto.
|
||||
If Cap'n Proto is not installed, it will bundle and build the matching Cap'n Proto library.
|
||||
The existing build fallback downloads Cap'n Proto 1.4.0. The extension links only
|
||||
`capnpc`, `capnp`, and `kj`; it does not link `capnp-rpc` or `kj-async`. `capnpc` is
|
||||
needed for runtime schema parsing. Owning/vendoring the C++ source itself is a
|
||||
separate step. To use a system installation, pass `--force-system-libcapnp` to
|
||||
`build_ext` (or `-Cforce-system-libcapnp=true` to the wheel build).
|
||||
|
||||
To enforce bundling, the Cap'n Proto library:
|
||||
The retained upstream tests cover message construction, schema loading,
|
||||
reflection, binary fixtures, serialization, and exceptions. Added lifetime tests
|
||||
check kwargs construction with GC disabled and readers surviving their input or
|
||||
iterator. Optional integration tests use real openpilot schemas and exercise
|
||||
messaging, CAN conversion, WebRTC reflection, LogReader, pickling, and replay:
|
||||
|
||||
```bash
|
||||
pip install . -C force-bundled-libcapnp=True
|
||||
```sh
|
||||
# Run in an environment with openpilot's dependencies and this fork installed.
|
||||
OPENPILOT_PATH=/path/to/openpilot python -m pytest test/test_openpilot.py
|
||||
python -m pytest /path/to/openpilot/openpilot/cereal/messaging/tests \
|
||||
/path/to/openpilot/openpilot/tools/lib/tests/test_logreader.py
|
||||
```
|
||||
|
||||
If you wish to install using the latest upstream C++ Cap'n Proto:
|
||||
|
||||
```bash
|
||||
pip install . \
|
||||
-C force-bundled-libcapnp=True \
|
||||
-C libcapnp-url="https://github.com/capnproto/capnproto/archive/master.tar.gz"
|
||||
```
|
||||
|
||||
To enforce using the installed Cap'n Proto from the system:
|
||||
|
||||
```bash
|
||||
pip install . -C force-system-libcapnp=True
|
||||
```
|
||||
|
||||
The bundling system isn't that smart so it might be necessary to clean up the bundled build when changing versions:
|
||||
|
||||
```bash
|
||||
python setup.py clean
|
||||
```
|
||||
|
||||
|
||||
## Stub-file generation
|
||||
|
||||
While not directly supported by pycapnp, a tool has been created to help generate pycapnp stubfile to assist with development (this is very helpful if you're new to pypcapnp!). See [#289](https://github.com/capnproto/pycapnp/pull/289#event-9078216721) for more details.
|
||||
|
||||
[Python Capnp Stub Generator](https://gitlab.com/mic_public/tools/python-helpers/capnp-stub-generator)
|
||||
|
||||
|
||||
## Python Versions
|
||||
|
||||
Python 3.9+ is supported.
|
||||
|
||||
|
||||
## Development
|
||||
|
||||
Git flow has been abandoned, use master.
|
||||
|
||||
To test, use a pipenv (or install requirements.txt and run pytest manually).
|
||||
```bash
|
||||
pip install pipenv
|
||||
pipenv install
|
||||
pipenv run pytest
|
||||
```
|
||||
|
||||
|
||||
### Binary Packages
|
||||
|
||||
Building a Python wheel distributiion
|
||||
|
||||
```bash
|
||||
pip wheel .
|
||||
```
|
||||
|
||||
|
||||
### Releasing to PyPI
|
||||
|
||||
Wheels and the sdist are built by the `Build` GitHub Actions workflow
|
||||
(`.github/workflows/wheels.yml`) for every push, including tag pushes. The
|
||||
`scripts/release-pypi.sh` helper downloads those artifacts for a given tag (or
|
||||
explicit run ID) and uploads them to PyPI via `twine`.
|
||||
|
||||
Typical release flow:
|
||||
|
||||
```bash
|
||||
git tag v2.2.1
|
||||
git push origin v2.2.1
|
||||
# wait for the "Build" workflow run to finish successfully on GitHub
|
||||
|
||||
# Download artifacts and upload to PyPI (creates dist_221/ by default).
|
||||
scripts/release-pypi.sh v2.2.1
|
||||
|
||||
# Or, target a specific Actions run id:
|
||||
scripts/release-pypi.sh 1234567890
|
||||
|
||||
# Dry run: upload to TestPyPI (https://test.pypi.org) instead of real PyPI.
|
||||
# Useful for validating the release flow end-to-end before pushing to
|
||||
# production. Requires a TestPyPI account + API token configured in
|
||||
# ~/.pypirc under a [testpypi] section. See
|
||||
# https://packaging.python.org/en/latest/guides/using-testpypi/ .
|
||||
scripts/release-pypi.sh v2.2.1 --test
|
||||
```
|
||||
|
||||
Requirements on the release machine:
|
||||
|
||||
- `gh` CLI, authenticated (`gh auth login`)
|
||||
- `python3` (the script creates `.venv-release/` and installs `twine` into it)
|
||||
- PyPI credentials available to `twine`, e.g. `TWINE_USERNAME=__token__` and
|
||||
`TWINE_PASSWORD=<api-token>`, or a configured `~/.pypirc`
|
||||
|
||||
The script:
|
||||
|
||||
1. Resolves the latest successful `wheels.yml` run for the tag (or uses the
|
||||
given run ID).
|
||||
2. Downloads `cibw-*` artifacts and flattens all `*.whl` / `*.tar.gz` files
|
||||
into the output directory (default `dist_<digits>` for tags,
|
||||
`dist_run_<id>` for run IDs; pass a second arg to override, and `--force`
|
||||
to reuse a non-empty directory).
|
||||
3. Runs `twine check`, prints the file list, and prompts before running
|
||||
`twine upload`.
|
||||
|
||||
## Documentation/Example
|
||||
|
||||
There is some basic documentation [here](http://capnproto.github.io/pycapnp/).
|
||||
|
||||
Make sure to look at the [examples](examples). The examples are generally kept up to date with the recommended usage of the library.
|
||||
|
||||
The examples directory has one example that shows off pycapnp quite nicely. Here it is, reproduced:
|
||||
|
||||
```python
|
||||
import os
|
||||
import capnp
|
||||
|
||||
import addressbook_capnp
|
||||
|
||||
def writeAddressBook(file):
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
people = addresses.init('people', 2)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = 123
|
||||
alice.name = 'Alice'
|
||||
alice.email = 'alice@example.com'
|
||||
alicePhones = alice.init('phones', 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = 'mobile'
|
||||
alice.employment.school = "MIT"
|
||||
|
||||
bob = people[1]
|
||||
bob.id = 456
|
||||
bob.name = 'Bob'
|
||||
bob.email = 'bob@example.com'
|
||||
bobPhones = bob.init('phones', 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = 'home'
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = 'work'
|
||||
bob.employment.unemployed = None
|
||||
|
||||
addresses.write(file)
|
||||
|
||||
|
||||
def printAddressBook(file):
|
||||
addresses = addressbook_capnp.AddressBook.read(file)
|
||||
|
||||
for person in addresses.people:
|
||||
print(person.name, ':', person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ':', phone.number)
|
||||
|
||||
which = person.employment.which()
|
||||
print(which)
|
||||
|
||||
if which == 'unemployed':
|
||||
print('unemployed')
|
||||
elif which == 'employer':
|
||||
print('employer:', person.employment.employer)
|
||||
elif which == 'school':
|
||||
print('student at:', person.employment.school)
|
||||
elif which == 'selfEmployed':
|
||||
print('self employed')
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
f = open('example', 'w')
|
||||
writeAddressBook(f)
|
||||
|
||||
f = open('example', 'r')
|
||||
printAddressBook(f)
|
||||
```
|
||||
|
||||
Also, pycapnp has gained RPC features that include pipelining and a promise style API. Refer to the calculator example in the examples directory for a much better demonstration:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import capnp
|
||||
import socket
|
||||
|
||||
import test_capability_capnp
|
||||
|
||||
|
||||
class Server(test_capability_capnp.TestInterface.Server):
|
||||
|
||||
def __init__(self, val=1):
|
||||
self.val = val
|
||||
|
||||
async def foo(self, i, j, **kwargs):
|
||||
return str(i * 5 + self.val)
|
||||
|
||||
|
||||
async def client(read_end):
|
||||
client = capnp.TwoPartyClient(read_end)
|
||||
|
||||
cap = client.bootstrap()
|
||||
cap = cap.cast_as(test_capability_capnp.TestInterface)
|
||||
|
||||
remote = cap.foo(i=5)
|
||||
response = await remote
|
||||
|
||||
assert response.x == '125'
|
||||
|
||||
async def main():
|
||||
client_end, server_end = socket.socketpair(socket.AF_UNIX)
|
||||
# This is a toy example using socketpair.
|
||||
# In real situations, you can use any socket.
|
||||
|
||||
client_end = await capnp.AsyncIoStream.create_connection(sock=client_end)
|
||||
server_end = await capnp.AsyncIoStream.create_connection(sock=server_end)
|
||||
|
||||
_ = capnp.TwoPartyServer(server_end, bootstrap=Server(100))
|
||||
await client(client_end)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(capnp.run(main()))
|
||||
```
|
||||
See [LICENSE.md](LICENSE.md) for the upstream BSD license and attribution.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
@0x934efea7f017fff0;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import os
|
||||
import capnp
|
||||
|
||||
this_dir = os.path.dirname(__file__)
|
||||
addressbook = capnp.load(os.path.join(this_dir, "addressbook.capnp"))
|
||||
|
||||
print = lambda *x: x
|
||||
|
||||
|
||||
def writeAddressBook():
|
||||
addressBook = addressbook.AddressBook.new_message()
|
||||
people = addressBook.init_resizable_list("people")
|
||||
|
||||
alice = people.add()
|
||||
alice.id = 123
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = alice.init("phones", 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = "mobile"
|
||||
|
||||
bob = people.add()
|
||||
bob.id = 456
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = bob.init("phones", 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = "home"
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = "work"
|
||||
|
||||
people.finish()
|
||||
msg_bytes = addressBook.to_bytes()
|
||||
return msg_bytes
|
||||
|
||||
|
||||
def printAddressBook(msg_bytes):
|
||||
with addressbook.AddressBook.from_bytes(msg_bytes) as addressBook:
|
||||
for person in addressBook.people:
|
||||
print(person.name, ":", person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ":", phone.number)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for i in range(10000):
|
||||
msg_bytes = writeAddressBook()
|
||||
|
||||
printAddressBook(msg_bytes)
|
||||
@@ -1,95 +0,0 @@
|
||||
import os
|
||||
import capnp
|
||||
|
||||
try:
|
||||
profile
|
||||
except:
|
||||
profile = lambda func: func
|
||||
this_dir = os.path.dirname(__file__)
|
||||
addressbook = capnp.load(os.path.join(this_dir, "addressbook.capnp"))
|
||||
|
||||
print = lambda *x: x
|
||||
|
||||
|
||||
@profile
|
||||
def writeAddressBook():
|
||||
addressBook = addressbook.AddressBook.new_message()
|
||||
people = addressBook.init("people", 2)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = 123
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = alice.init("phones", 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = "mobile"
|
||||
|
||||
bob = people[1]
|
||||
bob.id = 456
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = bob.init("phones", 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = "home"
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = "work"
|
||||
|
||||
msg_bytes = addressBook.to_bytes()
|
||||
return msg_bytes
|
||||
|
||||
|
||||
@profile
|
||||
def printAddressBook(msg_bytes):
|
||||
with addressbook.AddressBook.from_bytes(msg_bytes) as addressBook:
|
||||
for person in addressBook.people:
|
||||
person.name, person.email
|
||||
for phone in person.phones:
|
||||
phone.type, phone.number
|
||||
|
||||
|
||||
@profile
|
||||
def writeAddressBookDict():
|
||||
addressBook = addressbook.AddressBook.new_message()
|
||||
people = addressBook.init("people", 2)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = 123
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = alice.init("phones", 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = "mobile"
|
||||
|
||||
bob = people[1]
|
||||
bob.id = 456
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = bob.init("phones", 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = "home"
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = "work"
|
||||
|
||||
msg = addressBook.to_dict()
|
||||
return msg
|
||||
|
||||
|
||||
@profile
|
||||
def printAddressBookDict(msg):
|
||||
addressBook = addressbook.AddressBook.new_message(**msg)
|
||||
|
||||
for person in addressBook.people:
|
||||
person.name, person.email
|
||||
for phone in person.phones:
|
||||
phone.type, phone.number
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# for i in range(10000):
|
||||
# msg_bytes = writeAddressBook()
|
||||
|
||||
# printAddressBook(msg_bytes)
|
||||
for i in range(10000):
|
||||
msg = writeAddressBookDict()
|
||||
|
||||
printAddressBookDict(msg)
|
||||
@@ -1,26 +0,0 @@
|
||||
syntax = "proto2";
|
||||
|
||||
package tutorial;
|
||||
|
||||
message Person {
|
||||
required string name = 1;
|
||||
required int32 id = 2;
|
||||
required string email = 3;
|
||||
|
||||
enum PhoneType {
|
||||
MOBILE = 0;
|
||||
HOME = 1;
|
||||
WORK = 2;
|
||||
}
|
||||
|
||||
message PhoneNumber {
|
||||
required string number = 1;
|
||||
optional PhoneType type = 2 [default = HOME];
|
||||
}
|
||||
|
||||
repeated PhoneNumber phone = 4;
|
||||
}
|
||||
|
||||
message AddressBook {
|
||||
repeated Person person = 1;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import addressbook_pb2 as addressbook
|
||||
import os
|
||||
|
||||
print = lambda *x: x
|
||||
|
||||
|
||||
def writeAddressBook():
|
||||
addressBook = addressbook.AddressBook()
|
||||
|
||||
alice = addressBook.person.add()
|
||||
alice.id = 123
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = [alice.phone.add()]
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = addressbook.Person.MOBILE
|
||||
|
||||
bob = addressBook.person.add()
|
||||
bob.id = 456
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = [bob.phone.add(), bob.phone.add()]
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = addressbook.Person.HOME
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = addressbook.Person.WORK
|
||||
|
||||
message_string = addressBook.SerializeToString()
|
||||
return message_string
|
||||
|
||||
|
||||
def printAddressBook(message_string):
|
||||
addressBook = addressbook.AddressBook()
|
||||
addressBook.ParseFromString(message_string)
|
||||
|
||||
for person in addressBook.person:
|
||||
print(person.name, ":", person.email)
|
||||
for phone in person.phone:
|
||||
print(phone.type, ":", phone.number)
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for i in range(10000):
|
||||
message_string = writeAddressBook()
|
||||
|
||||
printAddressBook(message_string)
|
||||
@@ -1,278 +0,0 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: addressbook.proto
|
||||
|
||||
import sys
|
||||
|
||||
_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1"))
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name="addressbook.proto",
|
||||
package="tutorial",
|
||||
syntax="proto2",
|
||||
serialized_pb=_b(
|
||||
'\n\x11\x61\x64\x64ressbook.proto\x12\x08tutorial"\xda\x01\n\x06Person\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\n\n\x02id\x18\x02 \x02(\x05\x12\r\n\x05\x65mail\x18\x03 \x02(\t\x12+\n\x05phone\x18\x04 \x03(\x0b\x32\x1c.tutorial.Person.PhoneNumber\x1aM\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x02(\t\x12.\n\x04type\x18\x02 \x01(\x0e\x32\x1a.tutorial.Person.PhoneType:\x04HOME"+\n\tPhoneType\x12\n\n\x06MOBILE\x10\x00\x12\x08\n\x04HOME\x10\x01\x12\x08\n\x04WORK\x10\x02"/\n\x0b\x41\x64\x64ressBook\x12 \n\x06person\x18\x01 \x03(\x0b\x32\x10.tutorial.Person'
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
_PERSON_PHONETYPE = _descriptor.EnumDescriptor(
|
||||
name="PhoneType",
|
||||
full_name="tutorial.Person.PhoneType",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="MOBILE", index=0, number=0, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="HOME", index=1, number=1, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="WORK", index=2, number=2, options=None, type=None
|
||||
),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=207,
|
||||
serialized_end=250,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_PERSON_PHONETYPE)
|
||||
|
||||
|
||||
_PERSON_PHONENUMBER = _descriptor.Descriptor(
|
||||
name="PhoneNumber",
|
||||
full_name="tutorial.Person.PhoneNumber",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="number",
|
||||
full_name="tutorial.Person.PhoneNumber.number",
|
||||
index=0,
|
||||
number=1,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="type",
|
||||
full_name="tutorial.Person.PhoneNumber.type",
|
||||
index=1,
|
||||
number=2,
|
||||
type=14,
|
||||
cpp_type=8,
|
||||
label=1,
|
||||
has_default_value=True,
|
||||
default_value=1,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=128,
|
||||
serialized_end=205,
|
||||
)
|
||||
|
||||
_PERSON = _descriptor.Descriptor(
|
||||
name="Person",
|
||||
full_name="tutorial.Person",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="name",
|
||||
full_name="tutorial.Person.name",
|
||||
index=0,
|
||||
number=1,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="id",
|
||||
full_name="tutorial.Person.id",
|
||||
index=1,
|
||||
number=2,
|
||||
type=5,
|
||||
cpp_type=1,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="email",
|
||||
full_name="tutorial.Person.email",
|
||||
index=2,
|
||||
number=3,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="phone",
|
||||
full_name="tutorial.Person.phone",
|
||||
index=3,
|
||||
number=4,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=3,
|
||||
has_default_value=False,
|
||||
default_value=[],
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[
|
||||
_PERSON_PHONENUMBER,
|
||||
],
|
||||
enum_types=[
|
||||
_PERSON_PHONETYPE,
|
||||
],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=32,
|
||||
serialized_end=250,
|
||||
)
|
||||
|
||||
|
||||
_ADDRESSBOOK = _descriptor.Descriptor(
|
||||
name="AddressBook",
|
||||
full_name="tutorial.AddressBook",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="person",
|
||||
full_name="tutorial.AddressBook.person",
|
||||
index=0,
|
||||
number=1,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=3,
|
||||
has_default_value=False,
|
||||
default_value=[],
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=252,
|
||||
serialized_end=299,
|
||||
)
|
||||
|
||||
_PERSON_PHONENUMBER.fields_by_name["type"].enum_type = _PERSON_PHONETYPE
|
||||
_PERSON_PHONENUMBER.containing_type = _PERSON
|
||||
_PERSON.fields_by_name["phone"].message_type = _PERSON_PHONENUMBER
|
||||
_PERSON_PHONETYPE.containing_type = _PERSON
|
||||
_ADDRESSBOOK.fields_by_name["person"].message_type = _PERSON
|
||||
DESCRIPTOR.message_types_by_name["Person"] = _PERSON
|
||||
DESCRIPTOR.message_types_by_name["AddressBook"] = _ADDRESSBOOK
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
Person = _reflection.GeneratedProtocolMessageType(
|
||||
"Person",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
PhoneNumber=_reflection.GeneratedProtocolMessageType(
|
||||
"PhoneNumber",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_PERSON_PHONENUMBER,
|
||||
__module__="addressbook_pb2",
|
||||
# @@protoc_insertion_point(class_scope:tutorial.Person.PhoneNumber)
|
||||
),
|
||||
),
|
||||
DESCRIPTOR=_PERSON,
|
||||
__module__="addressbook_pb2",
|
||||
# @@protoc_insertion_point(class_scope:tutorial.Person)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(Person)
|
||||
_sym_db.RegisterMessage(Person.PhoneNumber)
|
||||
|
||||
AddressBook = _reflection.GeneratedProtocolMessageType(
|
||||
"AddressBook",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_ADDRESSBOOK,
|
||||
__module__="addressbook_pb2",
|
||||
# @@protoc_insertion_point(class_scope:tutorial.AddressBook)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(AddressBook)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,13 +0,0 @@
|
||||
# Benchmarks
|
||||
|
||||
You'll need to install the protobuf dependencies if you want to profile against protobufs.
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
To run all the benchmarks:
|
||||
|
||||
```bash
|
||||
./run_all -l pyproto -l pyproto_cpp
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='carsales', suffix='pycapnp', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='catrank', suffix='pycapnp', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='eval', suffix='pycapnp', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='carsales', suffix='proto', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='catrank', suffix='proto', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from runner import parse_args_simple, run_test
|
||||
|
||||
def main():
|
||||
args = parse_args_simple()
|
||||
run_test(name='eval', suffix='proto', **vars(args))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
|
||||
pyproto-carsales $@
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
|
||||
pyproto-catrank $@
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
|
||||
pyproto-eval $@
|
||||
@@ -1 +0,0 @@
|
||||
protobuf
|
||||
@@ -1,129 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import argparse
|
||||
import time
|
||||
|
||||
_this_dir = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--langs",
|
||||
help="Add languages to test, ie: -l pyproto -l pyproto_cpp",
|
||||
action="append",
|
||||
default=["pycapnp"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--reuse",
|
||||
help="If this flag is passed, re-use tests will be run",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--compression",
|
||||
help="If this flag is passed, compression tests will be run",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--scale_iters",
|
||||
help="Scaling factor to multiply the default iters by",
|
||||
type=float,
|
||||
default=1.0,
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def run_one(prefix, name, mode, iters, faster, compression):
|
||||
res_type = prefix
|
||||
reuse = "no-reuse"
|
||||
|
||||
if faster:
|
||||
reuse = "reuse"
|
||||
res_type += "_reuse"
|
||||
if compression != "none":
|
||||
res_type += "_" + compression
|
||||
|
||||
command = [
|
||||
os.path.join(_this_dir, prefix + "-" + name),
|
||||
mode,
|
||||
reuse,
|
||||
compression,
|
||||
str(iters),
|
||||
]
|
||||
start = time.time()
|
||||
print("running: " + " ".join(command), file=sys.stderr)
|
||||
p = Popen(command, stdout=PIPE, stderr=PIPE)
|
||||
res = p.wait()
|
||||
end = time.time()
|
||||
|
||||
data = {}
|
||||
|
||||
if p.returncode != 0:
|
||||
sys.stderr.write(
|
||||
" ".join(command)
|
||||
+ " failed to run with errors: \n"
|
||||
+ p.stderr.read().decode(sys.stdout.encoding)
|
||||
+ "\n"
|
||||
)
|
||||
sys.stderr.flush()
|
||||
|
||||
data["type"] = res_type
|
||||
data["mode"] = mode
|
||||
data["name"] = name
|
||||
data["iters"] = iters
|
||||
data["time"] = end - start
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def run_each(name, langs, reuse, compression, iters):
|
||||
ret = []
|
||||
|
||||
for lang_name in langs:
|
||||
ret.append(run_one(lang_name, name, "object", iters, False, "none"))
|
||||
ret.append(run_one(lang_name, name, "bytes", iters, False, "none"))
|
||||
if reuse:
|
||||
ret.append(run_one(lang_name, name, "object", iters, True, "none"))
|
||||
ret.append(run_one(lang_name, name, "bytes", iters, True, "none"))
|
||||
if compression:
|
||||
ret.append(run_one(lang_name, name, "bytes", iters, True, "packed"))
|
||||
if compression:
|
||||
ret.append(run_one(lang_name, name, "bytes", iters, False, "packed"))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
os.environ["PATH"] += ":."
|
||||
|
||||
data = []
|
||||
data += run_each(
|
||||
"carsales",
|
||||
args.langs,
|
||||
args.reuse,
|
||||
args.compression,
|
||||
int(2000 * args.scale_iters),
|
||||
)
|
||||
data += run_each(
|
||||
"catrank", args.langs, args.reuse, args.compression, int(100 * args.scale_iters)
|
||||
)
|
||||
data += run_each(
|
||||
"eval", args.langs, args.reuse, args.compression, int(10000 * args.scale_iters)
|
||||
)
|
||||
json.dump(data, sys.stdout, sort_keys=True, indent=4, separators=(",", ": "))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
from importlib import import_module
|
||||
from timeit import default_timer
|
||||
import random
|
||||
|
||||
_this_dir = os.path.dirname(__file__)
|
||||
sys.path.append(os.path.join(_this_dir, ".."))
|
||||
from common import do_benchmark
|
||||
|
||||
|
||||
def parse_args_simple():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"mode", help="Mode to use for serialization, ie. object or bytes"
|
||||
)
|
||||
parser.add_argument("reuse", help="Currently ignored")
|
||||
parser.add_argument("compression", help="Valid values are none or packed")
|
||||
parser.add_argument("iters", help="Number of iterations to run for", type=int)
|
||||
parser.add_argument(
|
||||
"-I",
|
||||
"--includes",
|
||||
help="Directories to add to PYTHONPATH",
|
||||
default="/usr/local/include",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"name",
|
||||
help="Name of the benchmark to run, eg. carsales",
|
||||
nargs="?",
|
||||
default="carsales",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--compression", help="Specify the compression type", default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--suffix", help="Choose the protocol type.", default="pycapnp"
|
||||
)
|
||||
parser.add_argument("-m", "--mode", help="Specify the mode", default="object")
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--iters",
|
||||
help="Specify the number of iterations manually. By default, it will be looked up in preset table",
|
||||
default=10,
|
||||
type=int,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--reuse",
|
||||
help="If this flag is passed, objects will be re-used",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-I",
|
||||
"--includes",
|
||||
help="Directories to add to PYTHONPATH",
|
||||
default="/usr/local/include",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def run_test(name, mode, reuse, compression, iters, suffix, includes):
|
||||
tic = default_timer()
|
||||
|
||||
name = name
|
||||
sys.path.append(includes)
|
||||
module = import_module(name + "_" + suffix)
|
||||
benchmark = module.Benchmark(compression=compression)
|
||||
|
||||
do_benchmark(mode=mode, benchmark=benchmark, iters=iters, reuse=reuse)
|
||||
toc = default_timer()
|
||||
return toc - tic
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
run_test(**vars(args))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,82 +0,0 @@
|
||||
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
|
||||
@0xff75ddc6a36723c9;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
struct ParkingLot {
|
||||
cars@0: List(Car);
|
||||
}
|
||||
|
||||
struct TotalValue {
|
||||
amount@0: UInt64;
|
||||
}
|
||||
|
||||
struct Car {
|
||||
make@0: Text;
|
||||
model@1: Text;
|
||||
color@2: Color;
|
||||
seats@3: UInt8;
|
||||
doors@4: UInt8;
|
||||
wheels@5: List(Wheel);
|
||||
length@6: UInt16;
|
||||
width@7: UInt16;
|
||||
height@8: UInt16;
|
||||
weight@9: UInt32;
|
||||
engine@10: Engine;
|
||||
fuelCapacity@11: Float32;
|
||||
fuelLevel@12: Float32;
|
||||
hasPowerWindows@13: Bool;
|
||||
hasPowerSteering@14: Bool;
|
||||
hasCruiseControl@15: Bool;
|
||||
cupHolders@16: UInt8;
|
||||
hasNavSystem@17: Bool;
|
||||
}
|
||||
|
||||
enum Color {
|
||||
black @0;
|
||||
white @1;
|
||||
red @2;
|
||||
green @3;
|
||||
blue @4;
|
||||
cyan @5;
|
||||
magenta @6;
|
||||
yellow @7;
|
||||
silver @8;
|
||||
}
|
||||
|
||||
struct Wheel {
|
||||
diameter@0: UInt16;
|
||||
airPressure@1: Float32;
|
||||
snowTires@2: Bool;
|
||||
}
|
||||
|
||||
struct Engine {
|
||||
horsepower@0: UInt16;
|
||||
cylinders@1: UInt8;
|
||||
cc@2: UInt32;
|
||||
usesGas@3: Bool;
|
||||
usesElectric@4: Bool;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
message ParkingLot {
|
||||
repeated Car car = 1;
|
||||
}
|
||||
|
||||
message TotalValue {
|
||||
required uint64 amount = 1;
|
||||
}
|
||||
|
||||
message Car {
|
||||
optional string make = 1;
|
||||
optional string model = 2;
|
||||
optional Color color = 3;
|
||||
optional uint32 seats = 4;
|
||||
optional uint32 doors = 5;
|
||||
repeated Wheel wheel = 6;
|
||||
optional uint32 length = 7;
|
||||
optional uint32 width = 8;
|
||||
optional uint32 height = 9;
|
||||
optional uint32 weight = 10;
|
||||
optional Engine engine = 11;
|
||||
optional float fuel_capacity = 12;
|
||||
optional float fuel_level = 13;
|
||||
optional bool has_power_windows = 14;
|
||||
optional bool has_power_steering = 15;
|
||||
optional bool has_cruise_control = 16;
|
||||
optional uint32 cup_holders = 17;
|
||||
optional bool has_nav_system = 18;
|
||||
}
|
||||
|
||||
enum Color {
|
||||
BLACK = 0;
|
||||
WHITE = 1;
|
||||
RED = 2;
|
||||
GREEN = 3;
|
||||
BLUE = 4;
|
||||
CYAN = 5;
|
||||
MAGENTA = 6;
|
||||
YELLOW = 7;
|
||||
SILVER = 8;
|
||||
}
|
||||
|
||||
message Wheel {
|
||||
optional uint32 diameter = 1;
|
||||
optional float air_pressure = 2;
|
||||
optional bool snow_tires = 3;
|
||||
}
|
||||
|
||||
message Engine {
|
||||
optional uint32 horsepower = 1;
|
||||
optional uint32 cylinders = 2;
|
||||
optional uint32 cc = 3;
|
||||
optional bool uses_gas = 4;
|
||||
optional bool uses_electric = 5;
|
||||
}
|
||||
@@ -1,729 +0,0 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: carsales.proto
|
||||
|
||||
import sys
|
||||
|
||||
_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1"))
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name="carsales.proto",
|
||||
package="capnp.benchmark.protobuf",
|
||||
syntax="proto2",
|
||||
serialized_pb=_b(
|
||||
'\n\x0e\x63\x61rsales.proto\x12\x18\x63\x61pnp.benchmark.protobuf"8\n\nParkingLot\x12*\n\x03\x63\x61r\x18\x01 \x03(\x0b\x32\x1d.capnp.benchmark.protobuf.Car"\x1c\n\nTotalValue\x12\x0e\n\x06\x61mount\x18\x01 \x02(\x04"\xbc\x03\n\x03\x43\x61r\x12\x0c\n\x04make\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12.\n\x05\x63olor\x18\x03 \x01(\x0e\x32\x1f.capnp.benchmark.protobuf.Color\x12\r\n\x05seats\x18\x04 \x01(\r\x12\r\n\x05\x64oors\x18\x05 \x01(\r\x12.\n\x05wheel\x18\x06 \x03(\x0b\x32\x1f.capnp.benchmark.protobuf.Wheel\x12\x0e\n\x06length\x18\x07 \x01(\r\x12\r\n\x05width\x18\x08 \x01(\r\x12\x0e\n\x06height\x18\t \x01(\r\x12\x0e\n\x06weight\x18\n \x01(\r\x12\x30\n\x06\x65ngine\x18\x0b \x01(\x0b\x32 .capnp.benchmark.protobuf.Engine\x12\x15\n\rfuel_capacity\x18\x0c \x01(\x02\x12\x12\n\nfuel_level\x18\r \x01(\x02\x12\x19\n\x11has_power_windows\x18\x0e \x01(\x08\x12\x1a\n\x12has_power_steering\x18\x0f \x01(\x08\x12\x1a\n\x12has_cruise_control\x18\x10 \x01(\x08\x12\x13\n\x0b\x63up_holders\x18\x11 \x01(\r\x12\x16\n\x0ehas_nav_system\x18\x12 \x01(\x08"C\n\x05Wheel\x12\x10\n\x08\x64iameter\x18\x01 \x01(\r\x12\x14\n\x0c\x61ir_pressure\x18\x02 \x01(\x02\x12\x12\n\nsnow_tires\x18\x03 \x01(\x08"d\n\x06\x45ngine\x12\x12\n\nhorsepower\x18\x01 \x01(\r\x12\x11\n\tcylinders\x18\x02 \x01(\r\x12\n\n\x02\x63\x63\x18\x03 \x01(\r\x12\x10\n\x08uses_gas\x18\x04 \x01(\x08\x12\x15\n\ruses_electric\x18\x05 \x01(\x08*j\n\x05\x43olor\x12\t\n\x05\x42LACK\x10\x00\x12\t\n\x05WHITE\x10\x01\x12\x07\n\x03RED\x10\x02\x12\t\n\x05GREEN\x10\x03\x12\x08\n\x04\x42LUE\x10\x04\x12\x08\n\x04\x43YAN\x10\x05\x12\x0b\n\x07MAGENTA\x10\x06\x12\n\n\x06YELLOW\x10\x07\x12\n\n\x06SILVER\x10\x08'
|
||||
),
|
||||
)
|
||||
|
||||
_COLOR = _descriptor.EnumDescriptor(
|
||||
name="Color",
|
||||
full_name="capnp.benchmark.protobuf.Color",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="BLACK", index=0, number=0, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="WHITE", index=1, number=1, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="RED", index=2, number=2, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="GREEN", index=3, number=3, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="BLUE", index=4, number=4, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="CYAN", index=5, number=5, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="MAGENTA", index=6, number=6, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="YELLOW", index=7, number=7, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="SILVER", index=8, number=8, options=None, type=None
|
||||
),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=750,
|
||||
serialized_end=856,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_COLOR)
|
||||
|
||||
Color = enum_type_wrapper.EnumTypeWrapper(_COLOR)
|
||||
BLACK = 0
|
||||
WHITE = 1
|
||||
RED = 2
|
||||
GREEN = 3
|
||||
BLUE = 4
|
||||
CYAN = 5
|
||||
MAGENTA = 6
|
||||
YELLOW = 7
|
||||
SILVER = 8
|
||||
|
||||
|
||||
_PARKINGLOT = _descriptor.Descriptor(
|
||||
name="ParkingLot",
|
||||
full_name="capnp.benchmark.protobuf.ParkingLot",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="car",
|
||||
full_name="capnp.benchmark.protobuf.ParkingLot.car",
|
||||
index=0,
|
||||
number=1,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=3,
|
||||
has_default_value=False,
|
||||
default_value=[],
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=44,
|
||||
serialized_end=100,
|
||||
)
|
||||
|
||||
|
||||
_TOTALVALUE = _descriptor.Descriptor(
|
||||
name="TotalValue",
|
||||
full_name="capnp.benchmark.protobuf.TotalValue",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="amount",
|
||||
full_name="capnp.benchmark.protobuf.TotalValue.amount",
|
||||
index=0,
|
||||
number=1,
|
||||
type=4,
|
||||
cpp_type=4,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=102,
|
||||
serialized_end=130,
|
||||
)
|
||||
|
||||
|
||||
_CAR = _descriptor.Descriptor(
|
||||
name="Car",
|
||||
full_name="capnp.benchmark.protobuf.Car",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="make",
|
||||
full_name="capnp.benchmark.protobuf.Car.make",
|
||||
index=0,
|
||||
number=1,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="model",
|
||||
full_name="capnp.benchmark.protobuf.Car.model",
|
||||
index=1,
|
||||
number=2,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="color",
|
||||
full_name="capnp.benchmark.protobuf.Car.color",
|
||||
index=2,
|
||||
number=3,
|
||||
type=14,
|
||||
cpp_type=8,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="seats",
|
||||
full_name="capnp.benchmark.protobuf.Car.seats",
|
||||
index=3,
|
||||
number=4,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="doors",
|
||||
full_name="capnp.benchmark.protobuf.Car.doors",
|
||||
index=4,
|
||||
number=5,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="wheel",
|
||||
full_name="capnp.benchmark.protobuf.Car.wheel",
|
||||
index=5,
|
||||
number=6,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=3,
|
||||
has_default_value=False,
|
||||
default_value=[],
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="length",
|
||||
full_name="capnp.benchmark.protobuf.Car.length",
|
||||
index=6,
|
||||
number=7,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="width",
|
||||
full_name="capnp.benchmark.protobuf.Car.width",
|
||||
index=7,
|
||||
number=8,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="height",
|
||||
full_name="capnp.benchmark.protobuf.Car.height",
|
||||
index=8,
|
||||
number=9,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="weight",
|
||||
full_name="capnp.benchmark.protobuf.Car.weight",
|
||||
index=9,
|
||||
number=10,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="engine",
|
||||
full_name="capnp.benchmark.protobuf.Car.engine",
|
||||
index=10,
|
||||
number=11,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=None,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="fuel_capacity",
|
||||
full_name="capnp.benchmark.protobuf.Car.fuel_capacity",
|
||||
index=11,
|
||||
number=12,
|
||||
type=2,
|
||||
cpp_type=6,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=float(0),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="fuel_level",
|
||||
full_name="capnp.benchmark.protobuf.Car.fuel_level",
|
||||
index=12,
|
||||
number=13,
|
||||
type=2,
|
||||
cpp_type=6,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=float(0),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="has_power_windows",
|
||||
full_name="capnp.benchmark.protobuf.Car.has_power_windows",
|
||||
index=13,
|
||||
number=14,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="has_power_steering",
|
||||
full_name="capnp.benchmark.protobuf.Car.has_power_steering",
|
||||
index=14,
|
||||
number=15,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="has_cruise_control",
|
||||
full_name="capnp.benchmark.protobuf.Car.has_cruise_control",
|
||||
index=15,
|
||||
number=16,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="cup_holders",
|
||||
full_name="capnp.benchmark.protobuf.Car.cup_holders",
|
||||
index=16,
|
||||
number=17,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="has_nav_system",
|
||||
full_name="capnp.benchmark.protobuf.Car.has_nav_system",
|
||||
index=17,
|
||||
number=18,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=133,
|
||||
serialized_end=577,
|
||||
)
|
||||
|
||||
|
||||
_WHEEL = _descriptor.Descriptor(
|
||||
name="Wheel",
|
||||
full_name="capnp.benchmark.protobuf.Wheel",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="diameter",
|
||||
full_name="capnp.benchmark.protobuf.Wheel.diameter",
|
||||
index=0,
|
||||
number=1,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="air_pressure",
|
||||
full_name="capnp.benchmark.protobuf.Wheel.air_pressure",
|
||||
index=1,
|
||||
number=2,
|
||||
type=2,
|
||||
cpp_type=6,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=float(0),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="snow_tires",
|
||||
full_name="capnp.benchmark.protobuf.Wheel.snow_tires",
|
||||
index=2,
|
||||
number=3,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=579,
|
||||
serialized_end=646,
|
||||
)
|
||||
|
||||
|
||||
_ENGINE = _descriptor.Descriptor(
|
||||
name="Engine",
|
||||
full_name="capnp.benchmark.protobuf.Engine",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="horsepower",
|
||||
full_name="capnp.benchmark.protobuf.Engine.horsepower",
|
||||
index=0,
|
||||
number=1,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="cylinders",
|
||||
full_name="capnp.benchmark.protobuf.Engine.cylinders",
|
||||
index=1,
|
||||
number=2,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="cc",
|
||||
full_name="capnp.benchmark.protobuf.Engine.cc",
|
||||
index=2,
|
||||
number=3,
|
||||
type=13,
|
||||
cpp_type=3,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="uses_gas",
|
||||
full_name="capnp.benchmark.protobuf.Engine.uses_gas",
|
||||
index=3,
|
||||
number=4,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="uses_electric",
|
||||
full_name="capnp.benchmark.protobuf.Engine.uses_electric",
|
||||
index=4,
|
||||
number=5,
|
||||
type=8,
|
||||
cpp_type=7,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=False,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=648,
|
||||
serialized_end=748,
|
||||
)
|
||||
|
||||
_PARKINGLOT.fields_by_name["car"].message_type = _CAR
|
||||
_CAR.fields_by_name["color"].enum_type = _COLOR
|
||||
_CAR.fields_by_name["wheel"].message_type = _WHEEL
|
||||
_CAR.fields_by_name["engine"].message_type = _ENGINE
|
||||
DESCRIPTOR.message_types_by_name["ParkingLot"] = _PARKINGLOT
|
||||
DESCRIPTOR.message_types_by_name["TotalValue"] = _TOTALVALUE
|
||||
DESCRIPTOR.message_types_by_name["Car"] = _CAR
|
||||
DESCRIPTOR.message_types_by_name["Wheel"] = _WHEEL
|
||||
DESCRIPTOR.message_types_by_name["Engine"] = _ENGINE
|
||||
DESCRIPTOR.enum_types_by_name["Color"] = _COLOR
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
ParkingLot = _reflection.GeneratedProtocolMessageType(
|
||||
"ParkingLot",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_PARKINGLOT,
|
||||
__module__="carsales_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.ParkingLot)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(ParkingLot)
|
||||
|
||||
TotalValue = _reflection.GeneratedProtocolMessageType(
|
||||
"TotalValue",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_TOTALVALUE,
|
||||
__module__="carsales_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.TotalValue)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(TotalValue)
|
||||
|
||||
Car = _reflection.GeneratedProtocolMessageType(
|
||||
"Car",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_CAR,
|
||||
__module__="carsales_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.Car)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(Car)
|
||||
|
||||
Wheel = _reflection.GeneratedProtocolMessageType(
|
||||
"Wheel",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_WHEEL,
|
||||
__module__="carsales_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.Wheel)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(Wheel)
|
||||
|
||||
Engine = _reflection.GeneratedProtocolMessageType(
|
||||
"Engine",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_ENGINE,
|
||||
__module__="carsales_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.Engine)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(Engine)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import carsales_pb2
|
||||
from common import rand_int, rand_double, rand_bool, from_bytes_helper
|
||||
from random import choice
|
||||
|
||||
MAKES = ["Toyota", "GM", "Ford", "Honda", "Tesla"]
|
||||
MODELS = ["Camry", "Prius", "Volt", "Accord", "Leaf", "Model S"]
|
||||
COLORS = [
|
||||
"black",
|
||||
"white",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"cyan",
|
||||
"magenta",
|
||||
"yellow",
|
||||
"silver",
|
||||
]
|
||||
|
||||
|
||||
def random_car(car):
|
||||
car.make = choice(MAKES)
|
||||
car.model = choice(MODELS)
|
||||
car.color = rand_int(len(COLORS))
|
||||
|
||||
car.seats = 2 + rand_int(6)
|
||||
car.doors = 2 + rand_int(3)
|
||||
|
||||
for _ in range(4):
|
||||
wheel = car.wheel.add()
|
||||
wheel.diameter = 25 + rand_int(15)
|
||||
wheel.air_pressure = 30 + rand_double(20)
|
||||
wheel.snow_tires = rand_int(16) == 0
|
||||
|
||||
car.length = 170 + rand_int(150)
|
||||
car.width = 48 + rand_int(36)
|
||||
car.height = 54 + rand_int(48)
|
||||
car.weight = car.length * car.width * car.height // 200
|
||||
|
||||
engine = car.engine
|
||||
engine.horsepower = 100 * rand_int(400)
|
||||
engine.cylinders = 4 + 2 * rand_int(3)
|
||||
engine.cc = 800 + rand_int(10000)
|
||||
engine.uses_gas = True
|
||||
engine.uses_electric = rand_bool()
|
||||
|
||||
car.fuel_capacity = 10.0 + rand_double(30.0)
|
||||
car.fuel_level = rand_double(car.fuel_capacity)
|
||||
car.has_power_windows = rand_bool()
|
||||
car.has_power_steering = rand_bool()
|
||||
car.has_cruise_control = rand_bool()
|
||||
car.cup_holders = rand_int(12)
|
||||
car.has_nav_system = rand_bool()
|
||||
|
||||
|
||||
def calc_value(car):
|
||||
result = 0
|
||||
|
||||
result += car.seats * 200
|
||||
result += car.doors * 350
|
||||
for wheel in car.wheel:
|
||||
result += wheel.diameter * wheel.diameter
|
||||
result += 100 if wheel.snow_tires else 0
|
||||
|
||||
result += car.length * car.width * car.height // 50
|
||||
|
||||
engine = car.engine
|
||||
result += engine.horsepower * 40
|
||||
if engine.uses_electric:
|
||||
if engine.uses_gas:
|
||||
result += 5000
|
||||
else:
|
||||
result += 3000
|
||||
|
||||
result += 100 if car.has_power_windows else 0
|
||||
result += 200 if car.has_power_steering else 0
|
||||
result += 400 if car.has_cruise_control else 0
|
||||
result += 2000 if car.has_nav_system else 0
|
||||
|
||||
result += car.cup_holders * 25
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = carsales_pb2.ParkingLot
|
||||
self.Response = carsales_pb2.TotalValue
|
||||
self.from_bytes_request = from_bytes_helper(carsales_pb2.ParkingLot)
|
||||
self.from_bytes_response = from_bytes_helper(carsales_pb2.TotalValue)
|
||||
self.to_bytes = lambda x: x.SerializeToString()
|
||||
|
||||
def setup(self, request):
|
||||
result = 0
|
||||
for _ in range(rand_int(200)):
|
||||
car = request.car.add()
|
||||
random_car(car)
|
||||
result += calc_value(car)
|
||||
return result
|
||||
|
||||
def handle(self, request, response):
|
||||
result = 0
|
||||
for car in request.car:
|
||||
result += calc_value(car)
|
||||
|
||||
response.amount = result
|
||||
|
||||
def check(self, response, expected):
|
||||
return response.amount == expected
|
||||
@@ -1,114 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import capnp
|
||||
import carsales_capnp
|
||||
from common import rand_int, rand_double, rand_bool
|
||||
from random import choice
|
||||
|
||||
MAKES = ["Toyota", "GM", "Ford", "Honda", "Tesla"]
|
||||
MODELS = ["Camry", "Prius", "Volt", "Accord", "Leaf", "Model S"]
|
||||
COLORS = [
|
||||
"black",
|
||||
"white",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"cyan",
|
||||
"magenta",
|
||||
"yellow",
|
||||
"silver",
|
||||
]
|
||||
|
||||
|
||||
def random_car(car):
|
||||
car.make = choice(MAKES)
|
||||
car.model = choice(MODELS)
|
||||
car.color = choice(COLORS)
|
||||
|
||||
car.seats = 2 + rand_int(6)
|
||||
car.doors = 2 + rand_int(3)
|
||||
|
||||
for wheel in car.init("wheels", 4):
|
||||
wheel.diameter = 25 + rand_int(15)
|
||||
wheel.airPressure = 30 + rand_double(20)
|
||||
wheel.snowTires = rand_int(16) == 0
|
||||
|
||||
car.length = 170 + rand_int(150)
|
||||
car.width = 48 + rand_int(36)
|
||||
car.height = 54 + rand_int(48)
|
||||
car.weight = car.length * car.width * car.height // 200
|
||||
|
||||
engine = car.init("engine")
|
||||
engine.horsepower = 100 * rand_int(400)
|
||||
engine.cylinders = 4 + 2 * rand_int(3)
|
||||
engine.cc = 800 + rand_int(10000)
|
||||
engine.usesGas = True
|
||||
engine.usesElectric = rand_bool()
|
||||
|
||||
car.fuelCapacity = 10.0 + rand_double(30.0)
|
||||
car.fuelLevel = rand_double(car.fuelCapacity)
|
||||
car.hasPowerWindows = rand_bool()
|
||||
car.hasPowerSteering = rand_bool()
|
||||
car.hasCruiseControl = rand_bool()
|
||||
car.cupHolders = rand_int(12)
|
||||
car.hasNavSystem = rand_bool()
|
||||
|
||||
|
||||
def calc_value(car):
|
||||
result = 0
|
||||
|
||||
result += car.seats * 200
|
||||
result += car.doors * 350
|
||||
for wheel in car.wheels:
|
||||
result += wheel.diameter * wheel.diameter
|
||||
result += 100 if wheel.snowTires else 0
|
||||
|
||||
result += car.length * car.width * car.height // 50
|
||||
|
||||
engine = car.engine
|
||||
result += engine.horsepower * 40
|
||||
if engine.usesElectric:
|
||||
if engine.usesGas:
|
||||
result += 5000
|
||||
else:
|
||||
result += 3000
|
||||
|
||||
result += 100 if car.hasPowerWindows else 0
|
||||
result += 200 if car.hasPowerSteering else 0
|
||||
result += 400 if car.hasCruiseControl else 0
|
||||
result += 2000 if car.hasNavSystem else 0
|
||||
|
||||
result += car.cupHolders * 25
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = carsales_capnp.ParkingLot.new_message
|
||||
self.Response = carsales_capnp.TotalValue.new_message
|
||||
if compression == "packed":
|
||||
self.from_bytes_request = carsales_capnp.ParkingLot.from_bytes_packed
|
||||
self.from_bytes_response = carsales_capnp.TotalValue.from_bytes_packed
|
||||
self.to_bytes = lambda x: x.to_bytes_packed()
|
||||
else:
|
||||
self.from_bytes_request = carsales_capnp.ParkingLot.from_bytes
|
||||
self.from_bytes_response = carsales_capnp.TotalValue.from_bytes
|
||||
self.to_bytes = lambda x: x.to_bytes()
|
||||
|
||||
def setup(self, request):
|
||||
result = 0
|
||||
for car in request.init("cars", rand_int(200)):
|
||||
random_car(car)
|
||||
result += calc_value(car)
|
||||
return result
|
||||
|
||||
def handle(self, request, response):
|
||||
result = 0
|
||||
for car in request.cars:
|
||||
result += calc_value(car)
|
||||
|
||||
response.amount = result
|
||||
|
||||
def check(self, response, expected):
|
||||
return response.amount == expected
|
||||
@@ -1,37 +0,0 @@
|
||||
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
|
||||
@0x82beb8e37ff79aba;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
struct SearchResultList {
|
||||
results@0: List(SearchResult);
|
||||
}
|
||||
|
||||
struct SearchResult {
|
||||
url@0: Text;
|
||||
score@1: Float64;
|
||||
snippet@2: Text;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
message SearchResultList {
|
||||
repeated SearchResult result = 1;
|
||||
}
|
||||
|
||||
message SearchResult {
|
||||
optional string url = 1;
|
||||
optional double score = 2;
|
||||
optional string snippet = 3;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: catrank.proto
|
||||
|
||||
import sys
|
||||
|
||||
_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1"))
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name="catrank.proto",
|
||||
package="capnp.benchmark.protobuf",
|
||||
syntax="proto2",
|
||||
serialized_pb=_b(
|
||||
'\n\rcatrank.proto\x12\x18\x63\x61pnp.benchmark.protobuf"J\n\x10SearchResultList\x12\x36\n\x06result\x18\x01 \x03(\x0b\x32&.capnp.benchmark.protobuf.SearchResult";\n\x0cSearchResult\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\r\n\x05score\x18\x02 \x01(\x01\x12\x0f\n\x07snippet\x18\x03 \x01(\t'
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
_SEARCHRESULTLIST = _descriptor.Descriptor(
|
||||
name="SearchResultList",
|
||||
full_name="capnp.benchmark.protobuf.SearchResultList",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="result",
|
||||
full_name="capnp.benchmark.protobuf.SearchResultList.result",
|
||||
index=0,
|
||||
number=1,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=3,
|
||||
has_default_value=False,
|
||||
default_value=[],
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=43,
|
||||
serialized_end=117,
|
||||
)
|
||||
|
||||
|
||||
_SEARCHRESULT = _descriptor.Descriptor(
|
||||
name="SearchResult",
|
||||
full_name="capnp.benchmark.protobuf.SearchResult",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="url",
|
||||
full_name="capnp.benchmark.protobuf.SearchResult.url",
|
||||
index=0,
|
||||
number=1,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="score",
|
||||
full_name="capnp.benchmark.protobuf.SearchResult.score",
|
||||
index=1,
|
||||
number=2,
|
||||
type=1,
|
||||
cpp_type=5,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=float(0),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="snippet",
|
||||
full_name="capnp.benchmark.protobuf.SearchResult.snippet",
|
||||
index=2,
|
||||
number=3,
|
||||
type=9,
|
||||
cpp_type=9,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=_b("").decode("utf-8"),
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=119,
|
||||
serialized_end=178,
|
||||
)
|
||||
|
||||
_SEARCHRESULTLIST.fields_by_name["result"].message_type = _SEARCHRESULT
|
||||
DESCRIPTOR.message_types_by_name["SearchResultList"] = _SEARCHRESULTLIST
|
||||
DESCRIPTOR.message_types_by_name["SearchResult"] = _SEARCHRESULT
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
SearchResultList = _reflection.GeneratedProtocolMessageType(
|
||||
"SearchResultList",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_SEARCHRESULTLIST,
|
||||
__module__="catrank_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.SearchResultList)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(SearchResultList)
|
||||
|
||||
SearchResult = _reflection.GeneratedProtocolMessageType(
|
||||
"SearchResult",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_SEARCHRESULT,
|
||||
__module__="catrank_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.SearchResult)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(SearchResult)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from common import rand_int, rand_double, rand_bool, WORDS, from_bytes_helper
|
||||
from random import choice
|
||||
from string import ascii_letters
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
from itertools import izip
|
||||
except ImportError:
|
||||
izip = zip
|
||||
import catrank_pb2
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = catrank_pb2.SearchResultList
|
||||
self.Response = catrank_pb2.SearchResultList
|
||||
self.from_bytes_request = from_bytes_helper(catrank_pb2.SearchResultList)
|
||||
self.from_bytes_response = from_bytes_helper(catrank_pb2.SearchResultList)
|
||||
self.to_bytes = lambda x: x.SerializeToString()
|
||||
|
||||
def setup(self, request):
|
||||
goodCount = 0
|
||||
count = rand_int(1000)
|
||||
|
||||
for i in range(count):
|
||||
result = request.result.add()
|
||||
result.score = 1000 - i
|
||||
url_size = rand_int(100)
|
||||
result.url = "http://example.com/" + "".join(
|
||||
[choice(ascii_letters) for _ in range(url_size)]
|
||||
)
|
||||
|
||||
isCat = rand_bool()
|
||||
isDog = rand_bool()
|
||||
if isCat and not isDog:
|
||||
goodCount += 1
|
||||
|
||||
snippet = [choice(WORDS) for i in range(rand_int(20))]
|
||||
|
||||
if isCat:
|
||||
snippet.append(" cat ")
|
||||
if isDog:
|
||||
snippet.append(" dog ")
|
||||
|
||||
snippet += [choice(WORDS) for i in range(rand_int(20))]
|
||||
|
||||
result.snippet = "".join(snippet)
|
||||
|
||||
return goodCount
|
||||
|
||||
def handle(self, request, response):
|
||||
for req in request.result:
|
||||
resp = response.result.add()
|
||||
score = req.score
|
||||
|
||||
if " cat " in req.snippet:
|
||||
score *= 10000
|
||||
if " dog " in req.snippet:
|
||||
score /= 10000
|
||||
|
||||
resp.score = score
|
||||
resp.url = req.url
|
||||
resp.snippet = req.snippet
|
||||
|
||||
def check(self, response, expected):
|
||||
goodCount = 0
|
||||
|
||||
for result in response.result:
|
||||
if result.score > 1001:
|
||||
goodCount += 1
|
||||
|
||||
return goodCount == expected
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import capnp
|
||||
import catrank_capnp
|
||||
from common import rand_int, rand_double, rand_bool, WORDS
|
||||
from random import choice
|
||||
from string import ascii_letters
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
from itertools import izip
|
||||
except ImportError:
|
||||
izip = zip
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = catrank_capnp.SearchResultList.new_message
|
||||
self.Response = catrank_capnp.SearchResultList.new_message
|
||||
if compression == "packed":
|
||||
self.from_bytes_request = catrank_capnp.SearchResultList.from_bytes_packed
|
||||
self.from_bytes_response = catrank_capnp.SearchResultList.from_bytes_packed
|
||||
self.to_bytes = lambda x: x.to_bytes_packed()
|
||||
else:
|
||||
self.from_bytes_request = catrank_capnp.SearchResultList.from_bytes
|
||||
self.from_bytes_response = catrank_capnp.SearchResultList.from_bytes
|
||||
self.to_bytes = lambda x: x.to_bytes()
|
||||
|
||||
def setup(self, request):
|
||||
goodCount = 0
|
||||
count = rand_int(1000)
|
||||
|
||||
results = request.init("results", count)
|
||||
|
||||
for i, result in enumerate(results):
|
||||
result.score = 1000 - i
|
||||
url_size = rand_int(100)
|
||||
result.url = "http://example.com/" + "".join(
|
||||
[choice(ascii_letters) for _ in range(url_size)]
|
||||
)
|
||||
|
||||
isCat = rand_bool()
|
||||
isDog = rand_bool()
|
||||
if isCat and not isDog:
|
||||
goodCount += 1
|
||||
|
||||
snippet = [choice(WORDS) for i in range(rand_int(20))]
|
||||
|
||||
if isCat:
|
||||
snippet.append(" cat ")
|
||||
if isDog:
|
||||
snippet.append(" dog ")
|
||||
|
||||
snippet += [choice(WORDS) for i in range(rand_int(20))]
|
||||
|
||||
result.snippet = "".join(snippet)
|
||||
|
||||
return goodCount
|
||||
|
||||
def handle(self, request, response):
|
||||
results = response.init("results", len(request.results))
|
||||
|
||||
for req, resp in izip(request.results, results):
|
||||
score = req.score
|
||||
|
||||
if " cat " in req.snippet:
|
||||
score *= 10000
|
||||
if " dog " in req.snippet:
|
||||
score /= 10000
|
||||
|
||||
resp.score = score
|
||||
resp.url = req.url
|
||||
resp.snippet = req.snippet
|
||||
|
||||
def check(self, response, expected):
|
||||
goodCount = 0
|
||||
|
||||
for result in response.results:
|
||||
if result.score > 1001:
|
||||
goodCount += 1
|
||||
|
||||
return goodCount == expected
|
||||
@@ -1,94 +0,0 @@
|
||||
from random import random
|
||||
import pyximport
|
||||
|
||||
importers = pyximport.install()
|
||||
from common_fast import rand_int, rand_double, rand_bool
|
||||
|
||||
pyximport.uninstall(*importers)
|
||||
|
||||
WORDS = [
|
||||
"foo ",
|
||||
"bar ",
|
||||
"baz ",
|
||||
"qux ",
|
||||
"quux ",
|
||||
"corge ",
|
||||
"grault ",
|
||||
"garply ",
|
||||
"waldo ",
|
||||
"fred ",
|
||||
"plugh ",
|
||||
"xyzzy ",
|
||||
"thud ",
|
||||
]
|
||||
|
||||
|
||||
def from_bytes_helper(klass):
|
||||
def helper(text):
|
||||
obj = klass()
|
||||
obj.ParseFromString(text)
|
||||
return obj
|
||||
|
||||
return helper
|
||||
|
||||
|
||||
def pass_by_object(reuse, iters, benchmark):
|
||||
for _ in range(iters):
|
||||
request = benchmark.Request()
|
||||
expected = benchmark.setup(request)
|
||||
|
||||
response = benchmark.Response()
|
||||
benchmark.handle(request, response)
|
||||
|
||||
if not benchmark.check(response, expected):
|
||||
raise ValueError("Expected {}".format(expected))
|
||||
|
||||
|
||||
def pass_by_bytes(reuse, iters, benchmark):
|
||||
for _ in range(iters):
|
||||
request = benchmark.Request()
|
||||
expected = benchmark.setup(request)
|
||||
req_bytes = benchmark.to_bytes(request)
|
||||
|
||||
request2 = benchmark.from_bytes_request(req_bytes)
|
||||
response = benchmark.Response()
|
||||
benchmark.handle(request2, response)
|
||||
resp_bytes = benchmark.to_bytes(response)
|
||||
|
||||
response2 = benchmark.from_bytes_response(resp_bytes)
|
||||
if not benchmark.check(response2, expected):
|
||||
raise ValueError("Expected {}".format(expected))
|
||||
|
||||
|
||||
def do_benchmark(mode, *args, **kwargs):
|
||||
if mode == "client":
|
||||
pass
|
||||
elif mode == "object":
|
||||
return pass_by_object(*args, **kwargs)
|
||||
elif mode == "bytes":
|
||||
return pass_by_bytes(*args, **kwargs)
|
||||
else:
|
||||
raise ValueError("Unknown mode: " + str(mode))
|
||||
|
||||
|
||||
# typedef typename BenchmarkTypes::template BenchmarkMethods<TestCase, Reuse, Compression>
|
||||
# BenchmarkMethods;
|
||||
# if (mode == "client") {
|
||||
# return BenchmarkMethods::syncClient(STDIN_FILENO, STDOUT_FILENO, iters);
|
||||
# } else if (mode == "server") {
|
||||
# return BenchmarkMethods::server(STDIN_FILENO, STDOUT_FILENO, iters);
|
||||
# } else if (mode == "object") {
|
||||
# return BenchmarkMethods::passByObject(iters, false);
|
||||
# } else if (mode == "object-size") {
|
||||
# return BenchmarkMethods::passByObject(iters, true);
|
||||
# } else if (mode == "bytes") {
|
||||
# return BenchmarkMethods::passByBytes(iters);
|
||||
# } else if (mode == "pipe") {
|
||||
# return passByPipe<BenchmarkMethods>(BenchmarkMethods::syncClient, iters);
|
||||
# } else if (mode == "pipe-async") {
|
||||
# return passByPipe<BenchmarkMethods>(BenchmarkMethods::asyncClient, iters);
|
||||
# } else {
|
||||
# fprintf(stderr, "Unknown mode: %s\n", mode.c_str());
|
||||
# exit(1);
|
||||
# }
|
||||
# }
|
||||
@@ -1,20 +0,0 @@
|
||||
from libc.stdint cimport *
|
||||
|
||||
cdef uint32_t A = 1664525
|
||||
cdef uint32_t C = 1013904223
|
||||
cdef uint32_t state = C
|
||||
cdef int32_t MAX_INT = 2**31 - 1
|
||||
|
||||
cpdef uint32_t nextFastRand():
|
||||
global state
|
||||
state = A * state + C
|
||||
return state
|
||||
|
||||
cpdef uint32_t rand_int(uint32_t range):
|
||||
return nextFastRand() % range
|
||||
|
||||
cpdef double rand_double(double range):
|
||||
return nextFastRand() * range / MAX_INT
|
||||
|
||||
cpdef bint rand_bool():
|
||||
return nextFastRand() % 2
|
||||
@@ -1,53 +0,0 @@
|
||||
# Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
|
||||
@0xe12dc4c3e70e9eda;
|
||||
$Cxx.namespace("capnp::benchmark::capnp");
|
||||
|
||||
enum Operation {
|
||||
add @0;
|
||||
subtract @1;
|
||||
multiply @2;
|
||||
divide @3;
|
||||
modulus @4;
|
||||
}
|
||||
|
||||
struct Expression {
|
||||
op@0: Operation;
|
||||
|
||||
left :union {
|
||||
value@1: Int32;
|
||||
expression@2: Expression;
|
||||
}
|
||||
|
||||
right :union {
|
||||
value@3: Int32;
|
||||
expression@4: Expression;
|
||||
}
|
||||
}
|
||||
|
||||
struct EvaluationResult {
|
||||
value@0: Int32;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package capnp.benchmark.protobuf;
|
||||
|
||||
enum Operation {
|
||||
ADD = 0;
|
||||
SUBTRACT = 1;
|
||||
MULTIPLY = 2;
|
||||
DIVIDE = 3;
|
||||
MODULUS = 4;
|
||||
}
|
||||
|
||||
message Expression {
|
||||
required Operation op = 1;
|
||||
|
||||
optional int32 left_value = 2;
|
||||
optional Expression left_expression = 3;
|
||||
|
||||
optional int32 right_value = 4;
|
||||
optional Expression right_expression = 5;
|
||||
}
|
||||
|
||||
message EvaluationResult {
|
||||
required sint32 value = 1;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: eval.proto
|
||||
|
||||
import sys
|
||||
|
||||
_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1"))
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf import descriptor_pb2
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name="eval.proto",
|
||||
package="capnp.benchmark.protobuf",
|
||||
syntax="proto2",
|
||||
serialized_pb=_b(
|
||||
'\n\neval.proto\x12\x18\x63\x61pnp.benchmark.protobuf"\xe5\x01\n\nExpression\x12/\n\x02op\x18\x01 \x02(\x0e\x32#.capnp.benchmark.protobuf.Operation\x12\x12\n\nleft_value\x18\x02 \x01(\x05\x12=\n\x0fleft_expression\x18\x03 \x01(\x0b\x32$.capnp.benchmark.protobuf.Expression\x12\x13\n\x0bright_value\x18\x04 \x01(\x05\x12>\n\x10right_expression\x18\x05 \x01(\x0b\x32$.capnp.benchmark.protobuf.Expression"!\n\x10\x45valuationResult\x12\r\n\x05value\x18\x01 \x02(\x11*I\n\tOperation\x12\x07\n\x03\x41\x44\x44\x10\x00\x12\x0c\n\x08SUBTRACT\x10\x01\x12\x0c\n\x08MULTIPLY\x10\x02\x12\n\n\x06\x44IVIDE\x10\x03\x12\x0b\n\x07MODULUS\x10\x04'
|
||||
),
|
||||
)
|
||||
|
||||
_OPERATION = _descriptor.EnumDescriptor(
|
||||
name="Operation",
|
||||
full_name="capnp.benchmark.protobuf.Operation",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="ADD", index=0, number=0, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="SUBTRACT", index=1, number=1, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="MULTIPLY", index=2, number=2, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="DIVIDE", index=3, number=3, options=None, type=None
|
||||
),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name="MODULUS", index=4, number=4, options=None, type=None
|
||||
),
|
||||
],
|
||||
containing_type=None,
|
||||
options=None,
|
||||
serialized_start=307,
|
||||
serialized_end=380,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_OPERATION)
|
||||
|
||||
Operation = enum_type_wrapper.EnumTypeWrapper(_OPERATION)
|
||||
ADD = 0
|
||||
SUBTRACT = 1
|
||||
MULTIPLY = 2
|
||||
DIVIDE = 3
|
||||
MODULUS = 4
|
||||
|
||||
|
||||
_EXPRESSION = _descriptor.Descriptor(
|
||||
name="Expression",
|
||||
full_name="capnp.benchmark.protobuf.Expression",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="op",
|
||||
full_name="capnp.benchmark.protobuf.Expression.op",
|
||||
index=0,
|
||||
number=1,
|
||||
type=14,
|
||||
cpp_type=8,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="left_value",
|
||||
full_name="capnp.benchmark.protobuf.Expression.left_value",
|
||||
index=1,
|
||||
number=2,
|
||||
type=5,
|
||||
cpp_type=1,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="left_expression",
|
||||
full_name="capnp.benchmark.protobuf.Expression.left_expression",
|
||||
index=2,
|
||||
number=3,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=None,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="right_value",
|
||||
full_name="capnp.benchmark.protobuf.Expression.right_value",
|
||||
index=3,
|
||||
number=4,
|
||||
type=5,
|
||||
cpp_type=1,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
_descriptor.FieldDescriptor(
|
||||
name="right_expression",
|
||||
full_name="capnp.benchmark.protobuf.Expression.right_expression",
|
||||
index=4,
|
||||
number=5,
|
||||
type=11,
|
||||
cpp_type=10,
|
||||
label=1,
|
||||
has_default_value=False,
|
||||
default_value=None,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=41,
|
||||
serialized_end=270,
|
||||
)
|
||||
|
||||
|
||||
_EVALUATIONRESULT = _descriptor.Descriptor(
|
||||
name="EvaluationResult",
|
||||
full_name="capnp.benchmark.protobuf.EvaluationResult",
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name="value",
|
||||
full_name="capnp.benchmark.protobuf.EvaluationResult.value",
|
||||
index=0,
|
||||
number=1,
|
||||
type=17,
|
||||
cpp_type=1,
|
||||
label=2,
|
||||
has_default_value=False,
|
||||
default_value=0,
|
||||
message_type=None,
|
||||
enum_type=None,
|
||||
containing_type=None,
|
||||
is_extension=False,
|
||||
extension_scope=None,
|
||||
options=None,
|
||||
),
|
||||
],
|
||||
extensions=[],
|
||||
nested_types=[],
|
||||
enum_types=[],
|
||||
options=None,
|
||||
is_extendable=False,
|
||||
syntax="proto2",
|
||||
extension_ranges=[],
|
||||
oneofs=[],
|
||||
serialized_start=272,
|
||||
serialized_end=305,
|
||||
)
|
||||
|
||||
_EXPRESSION.fields_by_name["op"].enum_type = _OPERATION
|
||||
_EXPRESSION.fields_by_name["left_expression"].message_type = _EXPRESSION
|
||||
_EXPRESSION.fields_by_name["right_expression"].message_type = _EXPRESSION
|
||||
DESCRIPTOR.message_types_by_name["Expression"] = _EXPRESSION
|
||||
DESCRIPTOR.message_types_by_name["EvaluationResult"] = _EVALUATIONRESULT
|
||||
DESCRIPTOR.enum_types_by_name["Operation"] = _OPERATION
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
Expression = _reflection.GeneratedProtocolMessageType(
|
||||
"Expression",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_EXPRESSION,
|
||||
__module__="eval_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.Expression)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(Expression)
|
||||
|
||||
EvaluationResult = _reflection.GeneratedProtocolMessageType(
|
||||
"EvaluationResult",
|
||||
(_message.Message,),
|
||||
dict(
|
||||
DESCRIPTOR=_EVALUATIONRESULT,
|
||||
__module__="eval_pb2",
|
||||
# @@protoc_insertion_point(class_scope:capnp.benchmark.protobuf.EvaluationResult)
|
||||
),
|
||||
)
|
||||
_sym_db.RegisterMessage(EvaluationResult)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from common import rand_int, rand_double, rand_bool, from_bytes_helper
|
||||
from random import choice
|
||||
import eval_pb2
|
||||
|
||||
MAX_INT = 2**31 - 1
|
||||
MIN_INT = -(2**31)
|
||||
|
||||
OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"]
|
||||
|
||||
|
||||
def clamp(res):
|
||||
if res > MAX_INT:
|
||||
return MAX_INT
|
||||
elif res < MIN_INT:
|
||||
return MIN_INT
|
||||
else:
|
||||
return res
|
||||
|
||||
|
||||
def div(a, b):
|
||||
if b == 0:
|
||||
return MAX_INT
|
||||
if a == MIN_INT and b == -1:
|
||||
return MAX_INT
|
||||
|
||||
return a // b
|
||||
|
||||
|
||||
def mod(a, b):
|
||||
if b == 0:
|
||||
return MAX_INT
|
||||
if a == MIN_INT and b == -1:
|
||||
return MAX_INT
|
||||
|
||||
return a % b
|
||||
|
||||
|
||||
def make_expression(exp, depth):
|
||||
exp.op = rand_int(len(OPERATIONS))
|
||||
|
||||
if rand_int(8) < depth:
|
||||
left = rand_int(128) + 1
|
||||
exp.left_value = left
|
||||
else:
|
||||
left = make_expression(exp.left_expression, depth + 1)
|
||||
|
||||
if rand_int(8) < depth:
|
||||
right = rand_int(128) + 1
|
||||
exp.right_value = right
|
||||
else:
|
||||
right = make_expression(exp.right_expression, depth + 1)
|
||||
|
||||
op = exp.op
|
||||
if op == 0:
|
||||
return clamp(left + right)
|
||||
elif op == 1:
|
||||
return clamp(left - right)
|
||||
elif op == 2:
|
||||
return clamp(left * right)
|
||||
elif op == 3:
|
||||
return div(left, right)
|
||||
elif op == 4:
|
||||
return mod(left, right)
|
||||
raise RuntimeError("op wasn't a valid value: " + str(op))
|
||||
|
||||
|
||||
def evaluate_expression(exp):
|
||||
left = 0
|
||||
right = 0
|
||||
|
||||
if exp.HasField("left_value"):
|
||||
left = exp.left_value
|
||||
else:
|
||||
left = evaluate_expression(exp.left_expression)
|
||||
|
||||
if exp.HasField("right_value"):
|
||||
right = exp.right_value
|
||||
else:
|
||||
right = evaluate_expression(exp.right_expression)
|
||||
|
||||
op = exp.op
|
||||
if op == 0:
|
||||
return clamp(left + right)
|
||||
elif op == 1:
|
||||
return clamp(left - right)
|
||||
elif op == 2:
|
||||
return clamp(left * right)
|
||||
elif op == 3:
|
||||
return div(left, right)
|
||||
elif op == 4:
|
||||
return mod(left, right)
|
||||
raise RuntimeError("op wasn't a valid value: " + str(op))
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = eval_pb2.Expression
|
||||
self.Response = eval_pb2.EvaluationResult
|
||||
self.from_bytes_request = from_bytes_helper(eval_pb2.Expression)
|
||||
self.from_bytes_response = from_bytes_helper(eval_pb2.EvaluationResult)
|
||||
self.to_bytes = lambda x: x.SerializeToString()
|
||||
|
||||
def setup(self, request):
|
||||
return make_expression(request, 0)
|
||||
|
||||
def handle(self, request, response):
|
||||
response.value = evaluate_expression(request)
|
||||
|
||||
def check(self, response, expected):
|
||||
return response.value == expected
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import capnp
|
||||
import eval_capnp
|
||||
from common import rand_int, rand_double, rand_bool
|
||||
from random import choice
|
||||
|
||||
MAX_INT = 2**31 - 1
|
||||
MIN_INT = -(2**31)
|
||||
|
||||
OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"]
|
||||
|
||||
|
||||
def clamp(res):
|
||||
if res > MAX_INT:
|
||||
return MAX_INT
|
||||
elif res < MIN_INT:
|
||||
return MIN_INT
|
||||
else:
|
||||
return res
|
||||
|
||||
|
||||
def div(a, b):
|
||||
if b == 0:
|
||||
return MAX_INT
|
||||
if a == MIN_INT and b == -1:
|
||||
return MAX_INT
|
||||
|
||||
return a // b
|
||||
|
||||
|
||||
def mod(a, b):
|
||||
if b == 0:
|
||||
return MAX_INT
|
||||
if a == MIN_INT and b == -1:
|
||||
return MAX_INT
|
||||
|
||||
return a % b
|
||||
|
||||
|
||||
def make_expression(exp, depth):
|
||||
exp.op = choice(OPERATIONS)
|
||||
|
||||
if rand_int(8) < depth:
|
||||
left = rand_int(128) + 1
|
||||
exp.left.value = left
|
||||
else:
|
||||
left = make_expression(exp.left.init("expression"), depth + 1)
|
||||
|
||||
if rand_int(8) < depth:
|
||||
right = rand_int(128) + 1
|
||||
exp.right.value = right
|
||||
else:
|
||||
right = make_expression(exp.right.init("expression"), depth + 1)
|
||||
|
||||
op = exp.op
|
||||
if op == "add":
|
||||
return clamp(left + right)
|
||||
elif op == "subtract":
|
||||
return clamp(left - right)
|
||||
elif op == "multiply":
|
||||
return clamp(left * right)
|
||||
elif op == "divide":
|
||||
return div(left, right)
|
||||
elif op == "modulus":
|
||||
return mod(left, right)
|
||||
raise RuntimeError("op wasn't a valid value: " + str(op))
|
||||
|
||||
|
||||
def evaluate_expression(exp):
|
||||
left = 0
|
||||
right = 0
|
||||
|
||||
which = exp.left.which()
|
||||
if which == "value":
|
||||
left = exp.left.value
|
||||
elif which == "expression":
|
||||
left = evaluate_expression(exp.left.expression)
|
||||
|
||||
which = exp.right.which()
|
||||
if which == "value":
|
||||
right = exp.right.value
|
||||
elif which == "expression":
|
||||
right = evaluate_expression(exp.right.expression)
|
||||
|
||||
op = exp.op
|
||||
if op == "add":
|
||||
return clamp(left + right)
|
||||
elif op == "subtract":
|
||||
return clamp(left - right)
|
||||
elif op == "multiply":
|
||||
return clamp(left * right)
|
||||
elif op == "divide":
|
||||
return div(left, right)
|
||||
elif op == "modulus":
|
||||
return mod(left, right)
|
||||
raise RuntimeError("op wasn't a valid value: " + str(op))
|
||||
|
||||
|
||||
class Benchmark:
|
||||
def __init__(self, compression):
|
||||
self.Request = eval_capnp.Expression.new_message
|
||||
self.Response = eval_capnp.EvaluationResult.new_message
|
||||
if compression == "packed":
|
||||
self.from_bytes_request = eval_capnp.Expression.from_bytes_packed
|
||||
self.from_bytes_response = eval_capnp.EvaluationResult.from_bytes_packed
|
||||
self.to_bytes = lambda x: x.to_bytes_packed()
|
||||
else:
|
||||
self.from_bytes_request = eval_capnp.Expression.from_bytes
|
||||
self.from_bytes_response = eval_capnp.EvaluationResult.from_bytes
|
||||
self.to_bytes = lambda x: x.to_bytes()
|
||||
|
||||
def setup(self, request):
|
||||
return make_expression(request, 0)
|
||||
|
||||
def handle(self, request, response):
|
||||
response.value = evaluate_expression(request)
|
||||
|
||||
def check(self, response, expected):
|
||||
return response.value == expected
|
||||
@@ -3,7 +3,6 @@
|
||||
import subprocess
|
||||
import os
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
@@ -14,7 +13,7 @@ def build_libcapnp(bundle_dir, build_dir): # noqa: C901
|
||||
bundle_dir = os.path.abspath(bundle_dir)
|
||||
capnp_dir = os.path.join(bundle_dir, "capnproto-c++")
|
||||
build_dir = os.path.abspath(build_dir)
|
||||
tmp_dir = os.path.join(capnp_dir, "build{}".format(8 * struct.calcsize("P")))
|
||||
tmp_dir = os.path.join(capnp_dir, "build")
|
||||
|
||||
# Clean the tmp build directory every time
|
||||
if os.path.exists(tmp_dir):
|
||||
@@ -28,24 +27,9 @@ def build_libcapnp(bundle_dir, build_dir): # noqa: C901
|
||||
|
||||
# Enable ninja for compilation if available
|
||||
build_type = []
|
||||
if shutil.which("ninja") and os.name != "nt":
|
||||
if shutil.which("ninja"):
|
||||
build_type = ["-G", "Ninja"]
|
||||
|
||||
# Determine python shell architecture for Windows
|
||||
python_arch = 8 * struct.calcsize("P")
|
||||
build_arch = []
|
||||
build_flags = []
|
||||
if os.name == "nt":
|
||||
if python_arch == 64:
|
||||
build_arch_flag = "x64"
|
||||
elif python_arch == 32:
|
||||
build_arch_flag = "Win32"
|
||||
else:
|
||||
raise RuntimeError("Unknown windows build arch")
|
||||
build_arch = ["-A", build_arch_flag]
|
||||
build_flags = ["--config", "Release"]
|
||||
print("Building module for {}".format(python_arch))
|
||||
|
||||
if not shutil.which("cmake"):
|
||||
raise RuntimeError("Could not find cmake in your path!")
|
||||
|
||||
@@ -54,11 +38,11 @@ def build_libcapnp(bundle_dir, build_dir): # noqa: C901
|
||||
"-DCMAKE_POSITION_INDEPENDENT_CODE=1",
|
||||
"-DBUILD_TESTING=OFF",
|
||||
"-DBUILD_SHARED_LIBS=OFF",
|
||||
"-DWITH_OPENSSL=OFF",
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH={}".format(build_dir),
|
||||
capnp_dir,
|
||||
]
|
||||
args.extend(build_type)
|
||||
args.extend(build_arch)
|
||||
conf = subprocess.Popen(args, cwd=tmp_dir, stdout=sys.stdout)
|
||||
returncode = conf.wait()
|
||||
if returncode != 0:
|
||||
@@ -72,7 +56,6 @@ def build_libcapnp(bundle_dir, build_dir): # noqa: C901
|
||||
"--target",
|
||||
"install",
|
||||
]
|
||||
args.extend(build_flags)
|
||||
build = subprocess.Popen(args, cwd=tmp_dir, stdout=sys.stdout)
|
||||
returncode = build.wait()
|
||||
if cxxflags is None:
|
||||
|
||||
@@ -1,61 +1,17 @@
|
||||
"""A python library wrapping the Cap'n Proto C++ library
|
||||
|
||||
Example Usage::
|
||||
|
||||
import capnp
|
||||
|
||||
addressbook = capnp.load('addressbook.capnp')
|
||||
|
||||
# Building
|
||||
addresses = addressbook.AddressBook.newMessage()
|
||||
people = addresses.init('people', 1)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = 123
|
||||
alice.name = 'Alice'
|
||||
alice.email = 'alice@example.com'
|
||||
alicePhone = alice.init('phones', 1)[0]
|
||||
alicePhone.type = 'mobile'
|
||||
|
||||
f = open('example.bin', 'w')
|
||||
addresses.write(f)
|
||||
f.close()
|
||||
|
||||
# Reading
|
||||
f = open('example.bin')
|
||||
|
||||
addresses = addressbook.AddressBook.read(f)
|
||||
|
||||
for person in addresses.people:
|
||||
print(person.name, ':', person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ':', phone.number)
|
||||
"""
|
||||
"""Dynamic Cap'n Proto serialization for openpilot."""
|
||||
|
||||
from .version import version as __version__
|
||||
from .lib.capnp import *
|
||||
from .lib.capnp import (
|
||||
_CapabilityClient,
|
||||
_DynamicCapabilityClient,
|
||||
_DynamicEnum,
|
||||
_DynamicListBuilder,
|
||||
_DynamicListReader,
|
||||
_DynamicOrphan,
|
||||
_DynamicResizableListBuilder,
|
||||
_DynamicStructBuilder,
|
||||
_DynamicStructReader,
|
||||
_EventLoop,
|
||||
_InterfaceModule,
|
||||
_ListSchema,
|
||||
_MallocMessageBuilder,
|
||||
_PyCustomMessageBuilder,
|
||||
_PackedFdMessageReader,
|
||||
_StreamFdMessageReader,
|
||||
_StructModule,
|
||||
_write_message_to_fd,
|
||||
_write_packed_message_to_fd,
|
||||
_AsyncIoStream as AsyncIoStream,
|
||||
_init_capnp_api,
|
||||
)
|
||||
|
||||
_init_capnp_api()
|
||||
add_import_hook() # enable import hook by default
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from jinja2 import Environment, PackageLoader
|
||||
|
||||
import capnp
|
||||
import schema_capnp
|
||||
|
||||
|
||||
def find_type(code, id):
|
||||
for node in code["nodes"]:
|
||||
if node["id"] == id:
|
||||
return node
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
env = Environment(loader=PackageLoader("capnp", "templates"))
|
||||
env.filters["format_name"] = lambda name: name[name.find(":") + 1 :]
|
||||
|
||||
code = schema_capnp.CodeGeneratorRequest.read(sys.stdin)
|
||||
code = code.to_dict()
|
||||
code["nodes"] = [node for node in code["nodes"] if "struct" in node and node["scopeId"] != 0]
|
||||
for node in code["nodes"]:
|
||||
displayName = node["displayName"]
|
||||
parent, path = displayName.split(":")
|
||||
node["module_path"] = parent.replace(".", "_") + "." + ".".join([x[0].upper() + x[1:] for x in path.split(".")])
|
||||
node["module_name"] = path.replace(".", "_")
|
||||
node["c_module_path"] = "::".join([x[0].upper() + x[1:] for x in path.split(".")])
|
||||
node["schema"] = "_{}_Schema".format(node["module_name"])
|
||||
is_union = False
|
||||
for field in node["struct"]["fields"]:
|
||||
if field["discriminantValue"] != 65535:
|
||||
is_union = True
|
||||
field["c_name"] = field["name"][0].upper() + field["name"][1:]
|
||||
if "slot" in field:
|
||||
field["type"] = list(field["slot"]["type"].keys())[0]
|
||||
if not isinstance(field["slot"]["type"][field["type"]], dict):
|
||||
continue
|
||||
sub_type = field["slot"]["type"][field["type"]].get("typeId", None)
|
||||
if sub_type:
|
||||
field["sub_type"] = find_type(code, sub_type)
|
||||
sub_type = field["slot"]["type"][field["type"]].get("elementType", None)
|
||||
if sub_type:
|
||||
field["sub_type"] = sub_type
|
||||
else:
|
||||
field["type"] = find_type(code, field["group"]["typeId"])
|
||||
node["is_union"] = is_union
|
||||
|
||||
include_dir = os.path.abspath(os.path.join(os.path.dirname(capnp.__file__), ".."))
|
||||
module = env.get_template("module.pyx")
|
||||
|
||||
for f in code["requestedFiles"]:
|
||||
filename = f["filename"].replace(".", "_") + "_cython.pyx"
|
||||
|
||||
file_code = dict(code)
|
||||
file_code["nodes"] = [node for node in file_code["nodes"] if node["displayName"].startswith(f["filename"])]
|
||||
with open(filename, "w") as out:
|
||||
out.write(module.render(code=file_code, file=f, include_dir=include_dir))
|
||||
|
||||
setup = env.get_template("setup.py.tmpl")
|
||||
with open("setup_capnp.py", "w") as out:
|
||||
out.write(setup.render(code=code))
|
||||
print("You now need to build the cython module by running `python setup_capnp.py build_ext --inplace`.")
|
||||
print()
|
||||
@@ -1,199 +0,0 @@
|
||||
#include "capnp/helpers/capabilityHelper.h"
|
||||
#include "capnp/lib/capnp_api.h"
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> convert_to_pypromise(capnp::RemotePromise<capnp::DynamicStruct> promise) {
|
||||
return promise.then([](capnp::Response<capnp::DynamicStruct>&& response) {
|
||||
return stealPyRef(wrap_dynamic_struct_reader(response)); } );
|
||||
}
|
||||
|
||||
void c_reraise_kj_exception() {
|
||||
GILAcquire gil;
|
||||
try {
|
||||
if (PyErr_Occurred())
|
||||
; // let the latest Python exn pass through and ignore the current one
|
||||
else
|
||||
throw;
|
||||
}
|
||||
catch (kj::Exception& exn) {
|
||||
auto obj = wrap_kj_exception_for_reraise(exn);
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
PyErr_SetObject((PyObject*)obj->ob_type, obj);
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
catch (const std::exception& exn) {
|
||||
PyErr_SetString(PyExc_RuntimeError, exn.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
void check_py_error() {
|
||||
GILAcquire gil;
|
||||
PyObject * err = PyErr_Occurred();
|
||||
if(err) {
|
||||
PyObject * ptype, *pvalue, *ptraceback;
|
||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||
if(ptype == NULL || pvalue == NULL || ptraceback == NULL)
|
||||
throw kj::Exception(kj::Exception::Type::FAILED, kj::heapString("capabilityHelper.h"), 44, kj::heapString("Unknown error occurred"));
|
||||
|
||||
PyObject * info = get_exception_info(ptype, pvalue, ptraceback);
|
||||
|
||||
PyObject * py_filename = PyTuple_GetItem(info, 0);
|
||||
kj::String filename(kj::heapString(PyBytes_AsString(py_filename)));
|
||||
|
||||
PyObject * py_line = PyTuple_GetItem(info, 1);
|
||||
int line = PyLong_AsLong(py_line);
|
||||
|
||||
PyObject * py_description = PyTuple_GetItem(info, 2);
|
||||
kj::String description(kj::heapString(PyBytes_AsString(py_description)));
|
||||
|
||||
Py_DECREF(ptype);
|
||||
Py_DECREF(pvalue);
|
||||
Py_DECREF(ptraceback);
|
||||
Py_DECREF(info);
|
||||
PyErr_Clear();
|
||||
|
||||
throw kj::Exception(kj::Exception::Type::FAILED, kj::mv(filename), line, kj::mv(description));
|
||||
}
|
||||
}
|
||||
|
||||
kj::Promise<kj::Own<PyRefCounter>> wrapPyFunc(kj::Own<PyRefCounter> func, kj::Own<PyRefCounter> arg) {
|
||||
GILAcquire gil;
|
||||
PyObject * result = PyObject_CallFunctionObjArgs(func->obj, arg->obj, NULL);
|
||||
check_py_error();
|
||||
return stealPyRef(result);
|
||||
}
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> then(kj::Promise<kj::Own<PyRefCounter>> promise,
|
||||
kj::Own<PyRefCounter> func, kj::Own<PyRefCounter> error_func) {
|
||||
if(error_func->obj == Py_None)
|
||||
return promise.then([func=kj::mv(func)](kj::Own<PyRefCounter> arg) mutable {
|
||||
return wrapPyFunc(kj::mv(func), kj::mv(arg)); } );
|
||||
else
|
||||
return promise.then
|
||||
([func=kj::mv(func)](kj::Own<PyRefCounter> arg) mutable {
|
||||
return wrapPyFunc(kj::mv(func), kj::mv(arg)); },
|
||||
[error_func=kj::mv(error_func)](kj::Exception arg) mutable {
|
||||
return wrapPyFunc(kj::mv(error_func), stealPyRef(wrap_kj_exception(arg))); } );
|
||||
}
|
||||
|
||||
kj::Promise<void> PythonInterfaceDynamicImpl::call(capnp::InterfaceSchema::Method method,
|
||||
capnp::CallContext< capnp::DynamicStruct,
|
||||
capnp::DynamicStruct> context) {
|
||||
auto methodName = method.getProto().getName();
|
||||
|
||||
kj::Promise<void> * promise = call_server_method(this->py_server->obj,
|
||||
const_cast<char *>(methodName.cStr()),
|
||||
context,
|
||||
this->kj_loop->obj);
|
||||
|
||||
check_py_error();
|
||||
|
||||
if(promise == nullptr)
|
||||
return kj::READY_NOW;
|
||||
|
||||
kj::Promise<void> ret(kj::mv(*promise));
|
||||
delete promise;
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
class ReadPromiseAdapter {
|
||||
public:
|
||||
ReadPromiseAdapter(kj::PromiseFulfiller<size_t>& fulfiller, PyObject* protocol,
|
||||
void* buffer, size_t minBytes, size_t maxBytes)
|
||||
: protocol(protocol) {
|
||||
_asyncio_stream_read_start(protocol, buffer, minBytes, maxBytes, fulfiller);
|
||||
}
|
||||
|
||||
~ReadPromiseAdapter() {
|
||||
_asyncio_stream_read_stop(protocol);
|
||||
}
|
||||
|
||||
private:
|
||||
PyObject* protocol;
|
||||
};
|
||||
|
||||
|
||||
class WritePromiseAdapter {
|
||||
public:
|
||||
WritePromiseAdapter(kj::PromiseFulfiller<void>& fulfiller, PyObject* protocol,
|
||||
kj::ArrayPtr<const kj::ArrayPtr<const kj::byte>> pieces)
|
||||
: protocol(protocol) {
|
||||
_asyncio_stream_write_start(protocol, pieces, fulfiller);
|
||||
}
|
||||
|
||||
~WritePromiseAdapter() {
|
||||
_asyncio_stream_write_stop(protocol);
|
||||
}
|
||||
|
||||
private:
|
||||
PyObject* protocol;
|
||||
|
||||
};
|
||||
|
||||
PyAsyncIoStream::~PyAsyncIoStream() {
|
||||
_asyncio_stream_close(protocol->obj);
|
||||
}
|
||||
|
||||
kj::Promise<size_t> PyAsyncIoStream::tryRead(void* buffer, size_t minBytes, size_t maxBytes) {
|
||||
return kj::newAdaptedPromise<size_t, ReadPromiseAdapter>(protocol->obj, buffer, minBytes, maxBytes);
|
||||
}
|
||||
|
||||
kj::Promise<void> PyAsyncIoStream::write(const void* buffer, size_t size) {
|
||||
KJ_UNIMPLEMENTED("No use-case AsyncIoStream::write was found yet.");
|
||||
}
|
||||
|
||||
kj::Promise<void> PyAsyncIoStream::write(kj::ArrayPtr<const kj::ArrayPtr<const kj::byte>> pieces) {
|
||||
return kj::newAdaptedPromise<void, WritePromiseAdapter>(protocol->obj, pieces);
|
||||
}
|
||||
|
||||
kj::Promise<void> PyAsyncIoStream::whenWriteDisconnected() {
|
||||
// TODO: Possibly connect this to protocol.connection_lost?
|
||||
return kj::NEVER_DONE;
|
||||
}
|
||||
|
||||
void PyAsyncIoStream::shutdownWrite() {
|
||||
_asyncio_stream_shutdown_write(protocol->obj);
|
||||
}
|
||||
|
||||
class TaskToPromiseAdapter {
|
||||
public:
|
||||
TaskToPromiseAdapter(kj::PromiseFulfiller<void>& fulfiller,
|
||||
kj::Own<PyRefCounter> task, PyObject* callback)
|
||||
: task(kj::mv(task)) {
|
||||
promise_task_add_done_callback(this->task->obj, callback, fulfiller);
|
||||
}
|
||||
|
||||
~TaskToPromiseAdapter() {
|
||||
promise_task_cancel(this->task->obj);
|
||||
}
|
||||
|
||||
private:
|
||||
kj::Own<PyRefCounter> task;
|
||||
};
|
||||
|
||||
kj::Promise<void> taskToPromise(kj::Own<PyRefCounter> task, PyObject* callback) {
|
||||
return kj::newAdaptedPromise<void, TaskToPromiseAdapter>(kj::mv(task), callback);
|
||||
}
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> tryReadMessage(kj::AsyncIoStream& stream, capnp::ReaderOptions opts) {
|
||||
return capnp::tryReadMessage(stream, opts)
|
||||
.then([](kj::Maybe<kj::Own<capnp::MessageReader>> maybeReader) -> kj::Promise<kj::Own<PyRefCounter>> {
|
||||
KJ_IF_MAYBE(reader, maybeReader) {
|
||||
PyObject* pyreader = make_async_message_reader(kj::mv(*reader));
|
||||
check_py_error();
|
||||
return kj::heap<PyRefCounter>(pyreader);
|
||||
} else {
|
||||
return kj::heap<PyRefCounter>(Py_None);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void init_capnp_api() {
|
||||
import_capnp__lib__capnp();
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "capnp/dynamic.h"
|
||||
#include <kj/async-io.h>
|
||||
#include <capnp/serialize-async.h>
|
||||
#include <stdexcept>
|
||||
#include "Python.h"
|
||||
|
||||
class GILAcquire {
|
||||
public:
|
||||
GILAcquire() : gstate(PyGILState_Ensure()) {}
|
||||
~GILAcquire() {
|
||||
PyGILState_Release(gstate);
|
||||
}
|
||||
|
||||
PyGILState_STATE gstate;
|
||||
};
|
||||
|
||||
class GILRelease {
|
||||
public:
|
||||
GILRelease() {
|
||||
Py_UNBLOCK_THREADS
|
||||
}
|
||||
~GILRelease() {
|
||||
Py_BLOCK_THREADS
|
||||
}
|
||||
|
||||
PyThreadState *_save; // The macros above read/write from this variable
|
||||
};
|
||||
|
||||
class PyRefCounter {
|
||||
public:
|
||||
PyObject * obj;
|
||||
|
||||
PyRefCounter(PyObject * o) : obj(o) {
|
||||
GILAcquire gil;
|
||||
Py_INCREF(obj);
|
||||
}
|
||||
|
||||
PyRefCounter(const PyRefCounter & ref) : obj(ref.obj) {
|
||||
GILAcquire gil;
|
||||
Py_INCREF(obj);
|
||||
}
|
||||
|
||||
~PyRefCounter() {
|
||||
GILAcquire gil;
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
};
|
||||
|
||||
inline kj::Own<PyRefCounter> stealPyRef(PyObject* o) {
|
||||
auto ret = kj::heap<PyRefCounter>(o);
|
||||
Py_DECREF(o);
|
||||
return ret;
|
||||
}
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> convert_to_pypromise(capnp::RemotePromise<capnp::DynamicStruct> promise);
|
||||
|
||||
inline ::kj::Promise<kj::Own<PyRefCounter>> convert_to_pypromise(kj::Promise<void> promise) {
|
||||
return promise.then([]() {
|
||||
GILAcquire gil;
|
||||
return kj::heap<PyRefCounter>(Py_None);
|
||||
});
|
||||
}
|
||||
|
||||
void c_reraise_kj_exception();
|
||||
|
||||
void check_py_error();
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> then(kj::Promise<kj::Own<PyRefCounter>> promise,
|
||||
kj::Own<PyRefCounter> func, kj::Own<PyRefCounter> error_func);
|
||||
|
||||
class PythonInterfaceDynamicImpl final: public capnp::DynamicCapability::Server {
|
||||
public:
|
||||
kj::Own<PyRefCounter> py_server;
|
||||
kj::Own<PyRefCounter> kj_loop;
|
||||
|
||||
#if (CAPNP_VERSION_MAJOR < 1)
|
||||
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema,
|
||||
kj::Own<PyRefCounter> _py_server,
|
||||
kj::Own<PyRefCounter> kj_loop)
|
||||
: capnp::DynamicCapability::Server(schema),
|
||||
py_server(kj::mv(_py_server)), kj_loop(kj::mv(kj_loop)) { }
|
||||
#else
|
||||
PythonInterfaceDynamicImpl(capnp::InterfaceSchema & schema,
|
||||
kj::Own<PyRefCounter> _py_server,
|
||||
kj::Own<PyRefCounter> kj_loop)
|
||||
: capnp::DynamicCapability::Server(schema, { true }),
|
||||
py_server(kj::mv(_py_server)), kj_loop(kj::mv(kj_loop)) { }
|
||||
#endif
|
||||
|
||||
~PythonInterfaceDynamicImpl() {
|
||||
}
|
||||
|
||||
kj::Promise<void> call(capnp::InterfaceSchema::Method method,
|
||||
capnp::CallContext< capnp::DynamicStruct, capnp::DynamicStruct> context);
|
||||
};
|
||||
|
||||
inline void allowCancellation(capnp::CallContext<capnp::DynamicStruct, capnp::DynamicStruct> context) {
|
||||
#if (CAPNP_VERSION_MAJOR < 1)
|
||||
context.allowCancellation();
|
||||
#endif
|
||||
}
|
||||
|
||||
class PyAsyncIoStream: public kj::AsyncIoStream {
|
||||
public:
|
||||
kj::Own<PyRefCounter> protocol;
|
||||
|
||||
PyAsyncIoStream(kj::Own<PyRefCounter> protocol) : protocol(kj::mv(protocol)) {}
|
||||
~PyAsyncIoStream();
|
||||
|
||||
kj::Promise<size_t> tryRead(void* buffer, size_t minBytes, size_t maxBytes);
|
||||
|
||||
kj::Promise<void> write(const void* buffer, size_t size);
|
||||
|
||||
kj::Promise<void> write(kj::ArrayPtr<const kj::ArrayPtr<const kj::byte>> pieces);
|
||||
|
||||
kj::Promise<void> whenWriteDisconnected();
|
||||
|
||||
void shutdownWrite();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void rejectDisconnected(kj::PromiseFulfiller<T>& fulfiller, kj::StringPtr message) {
|
||||
fulfiller.reject(KJ_EXCEPTION(DISCONNECTED, message));
|
||||
}
|
||||
inline void rejectVoidDisconnected(kj::PromiseFulfiller<void>& fulfiller, kj::StringPtr message) {
|
||||
fulfiller.reject(KJ_EXCEPTION(DISCONNECTED, message));
|
||||
}
|
||||
|
||||
inline kj::Exception makeException(kj::StringPtr message) {
|
||||
return KJ_EXCEPTION(FAILED, message);
|
||||
}
|
||||
|
||||
kj::Promise<void> taskToPromise(kj::Own<PyRefCounter> coroutine, PyObject* callback);
|
||||
|
||||
::kj::Promise<kj::Own<PyRefCounter>> tryReadMessage(kj::AsyncIoStream& stream, capnp::ReaderOptions opts);
|
||||
|
||||
void init_capnp_api();
|
||||
@@ -1,8 +1,3 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#endif
|
||||
|
||||
#include "capnp/dynamic.h"
|
||||
|
||||
static_assert(CAPNP_VERSION >= 8000, "Version of Cap'n Proto C++ Library is too old. Please upgrade to a version >= 0.8 and then re-install this python library");
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "capnp/dynamic.h"
|
||||
#include "capnp/schema.capnp.h"
|
||||
|
||||
/// @brief Convert the dynamic struct to a Node::Reader
|
||||
::capnp::schema::Node::Reader toReader(capnp::DynamicStruct::Reader reader)
|
||||
{
|
||||
// requires an intermediate step to AnyStruct before going directly to Node::Reader,
|
||||
// since there exists no direct conversion from DynamicStruct::Reader to Node::Reader.
|
||||
return reader.as<capnp::AnyStruct>().as<capnp::schema::Node>();
|
||||
}
|
||||
31
capnp/helpers/exception.cpp
Normal file
31
capnp/helpers/exception.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "capnp/helpers/exception.h"
|
||||
#include "capnp/lib/capnp_api.h"
|
||||
|
||||
void c_reraise_kj_exception() {
|
||||
GILAcquire gil;
|
||||
try {
|
||||
if (PyErr_Occurred())
|
||||
; // let the latest Python exn pass through and ignore the current one
|
||||
else
|
||||
throw;
|
||||
}
|
||||
catch (kj::Exception& exn) {
|
||||
auto obj = wrap_kj_exception_for_reraise(exn);
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
PyErr_SetObject((PyObject*)obj->ob_type, obj);
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
catch (const std::exception& exn) {
|
||||
PyErr_SetString(PyExc_RuntimeError, exn.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
void init_capnp_api() {
|
||||
import_capnp__lib__capnp();
|
||||
}
|
||||
18
capnp/helpers/exception.h
Normal file
18
capnp/helpers/exception.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
#include <kj/exception.h>
|
||||
#include <stdexcept>
|
||||
|
||||
class GILAcquire {
|
||||
public:
|
||||
GILAcquire() : gstate(PyGILState_Ensure()) {}
|
||||
~GILAcquire() {
|
||||
PyGILState_Release(gstate);
|
||||
}
|
||||
|
||||
PyGILState_STATE gstate;
|
||||
};
|
||||
|
||||
void c_reraise_kj_exception();
|
||||
void init_capnp_api();
|
||||
@@ -1,34 +1,9 @@
|
||||
from capnp.includes.capnp_cpp cimport (
|
||||
Maybe, PyPromise, VoidPromise, RemotePromise,
|
||||
DynamicCapability, InterfaceSchema, EnumSchema, StructSchema, DynamicValue, Capability,
|
||||
RpcSystem, MessageBuilder, Own, PyRefCounter, Node, DynamicStruct, CallContext
|
||||
)
|
||||
|
||||
from capnp.includes.schema_cpp cimport ByteArray
|
||||
|
||||
from capnp.includes.capnp_cpp cimport Maybe, EnumSchema, StructSchema
|
||||
from non_circular cimport c_reraise_kj_exception as reraise_kj_exception
|
||||
|
||||
from cpython.ref cimport PyObject
|
||||
|
||||
cdef extern from "capnp/helpers/fixMaybe.h":
|
||||
EnumSchema.Enumerant fixMaybe(Maybe[EnumSchema.Enumerant]) except +reraise_kj_exception
|
||||
StructSchema.Field fixMaybe(Maybe[StructSchema.Field]) except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/helpers/capabilityHelper.h":
|
||||
PyPromise then(PyPromise promise, Own[PyRefCounter] func, Own[PyRefCounter] error_func)
|
||||
PyPromise convert_to_pypromise(RemotePromise)
|
||||
PyPromise convert_to_pypromise(VoidPromise)
|
||||
VoidPromise taskToPromise(Own[PyRefCounter] coroutine, PyObject* callback)
|
||||
void allowCancellation(CallContext context) except +reraise_kj_exception nogil
|
||||
cdef extern from "capnp/helpers/exception.h":
|
||||
void init_capnp_api()
|
||||
|
||||
cdef extern from "capnp/helpers/rpcHelper.h":
|
||||
Own[Capability.Client] bootstrapHelper(RpcSystem&) except +reraise_kj_exception
|
||||
Own[Capability.Client] bootstrapHelperServer(RpcSystem&) except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/helpers/serialize.h":
|
||||
ByteArray messageToPackedBytes(MessageBuilder &, size_t wordCount) except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/helpers/deserialize.h":
|
||||
Node.Reader toReader(DynamicStruct.Reader reader) except +reraise_kj_exception
|
||||
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
from cpython.ref cimport PyObject
|
||||
from libcpp cimport bool
|
||||
|
||||
cdef extern from "capnp/helpers/capabilityHelper.h":
|
||||
cdef extern from "capnp/helpers/exception.h":
|
||||
void c_reraise_kj_exception()
|
||||
cdef cppclass PyRefCounter:
|
||||
PyRefCounter(PyObject *)
|
||||
PyObject * obj
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "capnp/dynamic.h"
|
||||
#include <capnp/rpc.capnp.h>
|
||||
#include "capnp/rpc-twoparty.h"
|
||||
#include "Python.h"
|
||||
#include "capabilityHelper.h"
|
||||
|
||||
kj::Own<capnp::Capability::Client> bootstrapHelper(capnp::RpcSystem<capnp::rpc::twoparty::SturdyRefHostId>& client) {
|
||||
capnp::MallocMessageBuilder hostIdMessage(8);
|
||||
auto hostId = hostIdMessage.initRoot<capnp::rpc::twoparty::SturdyRefHostId>();
|
||||
hostId.setSide(capnp::rpc::twoparty::Side::SERVER);
|
||||
return kj::heap<capnp::Capability::Client>(client.bootstrap(hostId));
|
||||
}
|
||||
|
||||
kj::Own<capnp::Capability::Client> bootstrapHelperServer(capnp::RpcSystem<capnp::rpc::twoparty::SturdyRefHostId>& client) {
|
||||
capnp::MallocMessageBuilder hostIdMessage(8);
|
||||
auto hostId = hostIdMessage.initRoot<capnp::rpc::twoparty::SturdyRefHostId>();
|
||||
hostId.setSide(capnp::rpc::twoparty::Side::CLIENT);
|
||||
return kj::heap<capnp::Capability::Client>(client.bootstrap(hostId));
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "kj/io.h"
|
||||
#include "capnp/dynamic.h"
|
||||
#include "capnp/serialize-packed.h"
|
||||
|
||||
kj::Array< ::capnp::byte> messageToPackedBytes(capnp::MessageBuilder & message, size_t wordCount)
|
||||
{
|
||||
|
||||
kj::Array<capnp::byte> result = kj::heapArray<capnp::byte>(wordCount * 8);
|
||||
kj::ArrayOutputStream out(result.asPtr());
|
||||
capnp::writePackedMessage(out, message);
|
||||
return heapArray(out.getArray()); // TODO: make this non-copying somehow
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "PyCustomMessageBuilder.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
PyCustomMessageBuilder::PyCustomMessageBuilder(
|
||||
PyObject* allocateSegmentCallable, uint firstSegmentWords)
|
||||
: allocateSegmentCallable(allocateSegmentCallable), firstSize(firstSegmentWords)
|
||||
{
|
||||
KJ_REQUIRE(PyCallable_Check(allocateSegmentCallable),
|
||||
"allocateSegmentCallable must be callable");
|
||||
Py_INCREF(allocateSegmentCallable);
|
||||
}
|
||||
|
||||
PyCustomMessageBuilder::~PyCustomMessageBuilder() noexcept(false) {
|
||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||
|
||||
for (auto* obj : allocatedBuffers) {
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
allocatedBuffers.clear();
|
||||
|
||||
Py_DECREF(allocateSegmentCallable);
|
||||
PyGILState_Release(gstate);
|
||||
}
|
||||
|
||||
kj::ArrayPtr<capnp::word> PyCustomMessageBuilder::allocateSegment(capnp::uint minimumSize) {
|
||||
PyGILState_STATE gstate = PyGILState_Ensure();
|
||||
KJ_DEFER({ PyGILState_Release(gstate); });
|
||||
if (curSize == 0) {
|
||||
minimumSize = kj::max(minimumSize, firstSize);
|
||||
}
|
||||
PyObject* pyBufObj = PyObject_CallFunction(allocateSegmentCallable, "I", minimumSize);
|
||||
KJ_REQUIRE(pyBufObj, "PyCustomMessageBuilder: allocateSegment failed");
|
||||
allocatedBuffers.push_back(pyBufObj);
|
||||
|
||||
|
||||
Py_buffer view;
|
||||
int bufRes = PyObject_GetBuffer(pyBufObj, &view, PyBUF_SIMPLE);
|
||||
KJ_REQUIRE(bufRes == 0, "PyCustomMessageBuilder: object does not support buffer protocol");
|
||||
KJ_DEFER({ PyBuffer_Release(&view); });
|
||||
|
||||
size_t byteCount = view.len;
|
||||
size_t wordCount = byteCount / sizeof(capnp::word);
|
||||
KJ_REQUIRE(wordCount >= minimumSize, "PyCustomMessageBuilder: buffer too small for minimumSize");
|
||||
curSize += wordCount;
|
||||
return kj::arrayPtr(reinterpret_cast<capnp::word*>(view.buf), wordCount);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Python.h"
|
||||
#include <capnp/message.h>
|
||||
#include <capnp/serialize.h>
|
||||
#include <vector>
|
||||
|
||||
namespace capnp {
|
||||
|
||||
class PyCustomMessageBuilder : public capnp::MessageBuilder {
|
||||
public:
|
||||
explicit PyCustomMessageBuilder(PyObject* allocateSegmentCallable,
|
||||
uint firstSegmentWords = capnp::SUGGESTED_FIRST_SEGMENT_WORDS);
|
||||
|
||||
~PyCustomMessageBuilder() noexcept(false) override;
|
||||
|
||||
kj::ArrayPtr<capnp::word> allocateSegment(capnp::uint minimumSize) override;
|
||||
|
||||
private:
|
||||
PyObject* allocateSegmentCallable;
|
||||
|
||||
uint firstSize;
|
||||
uint curSize = 0;
|
||||
|
||||
std::vector<PyObject*> allocatedBuffers;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -3,12 +3,11 @@
|
||||
cdef extern from "capnp/helpers/checkCompiler.h":
|
||||
pass
|
||||
|
||||
from libcpp cimport bool
|
||||
from capnp.helpers.non_circular cimport (
|
||||
c_reraise_kj_exception as reraise_kj_exception, PyRefCounter,
|
||||
c_reraise_kj_exception as reraise_kj_exception,
|
||||
)
|
||||
from capnp.includes.schema_cpp cimport (
|
||||
Node, Data, StructNode, EnumNode, InterfaceNode, MessageBuilder, MessageReader, ReaderOptions,
|
||||
Node, Data, Field as SchemaField, Enumerant as SchemaEnumerant, MessageBuilder, MessageReader, ReaderOptions,
|
||||
)
|
||||
from capnp.includes.types cimport *
|
||||
|
||||
@@ -46,26 +45,6 @@ cdef extern from "kj/exception.h" namespace " ::kj":
|
||||
int getType()
|
||||
StringPtr getDescription()
|
||||
|
||||
cdef extern from "kj/memory.h" namespace " ::kj":
|
||||
cdef cppclass Own[T] nogil:
|
||||
Own()
|
||||
T& operator*()
|
||||
T* get()
|
||||
Own[T] heap[T](...)
|
||||
|
||||
cdef extern from "kj/async.h" namespace " ::kj":
|
||||
cdef cppclass Promise[T] nogil:
|
||||
Promise(Promise)
|
||||
Promise(T)
|
||||
String trace()
|
||||
Promise[T] attach(Own[PyRefCounter] &)
|
||||
Promise[T] attach(Own[PyRefCounter] &, Own[PyRefCounter] &)
|
||||
Promise[T] attach(Own[PyRefCounter] &, Own[PyRefCounter] &, Own[PyRefCounter] &)
|
||||
Promise[T] attach(Own[PyRefCounter] &, Own[PyRefCounter] &, Own[PyRefCounter] &, Own[PyRefCounter] &)
|
||||
|
||||
ctypedef Promise[Own[PyRefCounter]] PyPromise
|
||||
ctypedef Promise[void] VoidPromise
|
||||
|
||||
cdef extern from "kj/string-tree.h" namespace " ::kj":
|
||||
cdef cppclass StringTree nogil:
|
||||
String flatten()
|
||||
@@ -80,59 +59,15 @@ cdef extern from "kj/common.h" namespace " ::kj":
|
||||
size_t size()
|
||||
T& operator[](size_t index)
|
||||
|
||||
cdef extern from "kj/array.h" namespace " ::kj":
|
||||
cdef cppclass Array[T] nogil:
|
||||
T* begin()
|
||||
size_t size()
|
||||
T& operator[](size_t index)
|
||||
cdef cppclass ArrayBuilder[T] nogil:
|
||||
T* begin()
|
||||
size_t size()
|
||||
T& operator[](size_t index)
|
||||
T& add(T&)
|
||||
Array[T] finish()
|
||||
|
||||
|
||||
cdef extern from "kj/async-io.h" namespace " ::kj":
|
||||
cdef cppclass AsyncIoStream nogil:
|
||||
Promise[size_t] read(void*, size_t, size_t) except +reraise_kj_exception
|
||||
Promise[void] write(const void*, size_t) except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/schema.capnp.h" namespace " ::capnp":
|
||||
enum TypeWhich" ::capnp::schema::Type::Which":
|
||||
TypeWhichVOID " ::capnp::schema::Type::Which::VOID"
|
||||
TypeWhichBOOL " ::capnp::schema::Type::Which::BOOL"
|
||||
TypeWhichINT8 " ::capnp::schema::Type::Which::INT8"
|
||||
TypeWhichINT16 " ::capnp::schema::Type::Which::INT16"
|
||||
TypeWhichINT32 " ::capnp::schema::Type::Which::INT32"
|
||||
TypeWhichINT64 " ::capnp::schema::Type::Which::INT64"
|
||||
TypeWhichUINT8 " ::capnp::schema::Type::Which::UINT8"
|
||||
TypeWhichUINT16 " ::capnp::schema::Type::Which::UINT16"
|
||||
TypeWhichUINT32 " ::capnp::schema::Type::Which::UINT32"
|
||||
TypeWhichUINT64 " ::capnp::schema::Type::Which::UINT64"
|
||||
TypeWhichFLOAT32 " ::capnp::schema::Type::Which::FLOAT32"
|
||||
TypeWhichFLOAT64 " ::capnp::schema::Type::Which::FLOAT64"
|
||||
TypeWhichTEXT " ::capnp::schema::Type::Which::TEXT"
|
||||
TypeWhichDATA " ::capnp::schema::Type::Which::DATA"
|
||||
TypeWhichLIST " ::capnp::schema::Type::Which::LIST"
|
||||
TypeWhichENUM " ::capnp::schema::Type::Which::ENUM"
|
||||
TypeWhichSTRUCT " ::capnp::schema::Type::Which::STRUCT"
|
||||
TypeWhichINTERFACE " ::capnp::schema::Type::Which::INTERFACE"
|
||||
TypeWhichANY_POINTER " ::capnp::schema::Type::Which::ANY_POINTER"
|
||||
|
||||
cdef extern from "capnp/schema.h" namespace " ::capnp":
|
||||
cdef cppclass SchemaType" ::capnp::Type" nogil:
|
||||
SchemaType()
|
||||
SchemaType(TypeWhich)
|
||||
cbool isList()
|
||||
cbool isEnum()
|
||||
cbool isStruct()
|
||||
cbool isInterface()
|
||||
cbool isData()
|
||||
|
||||
StructSchema asStruct() except +reraise_kj_exception
|
||||
EnumSchema asEnum() except +reraise_kj_exception
|
||||
InterfaceSchema asInterface() except +reraise_kj_exception
|
||||
ListSchema asList() except +reraise_kj_exception
|
||||
|
||||
cdef cppclass Schema nogil:
|
||||
@@ -141,35 +76,10 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
|
||||
EnumSchema asEnum() except +reraise_kj_exception
|
||||
ConstSchema asConst() except +reraise_kj_exception
|
||||
Schema getDependency(uint64_t id) except +reraise_kj_exception
|
||||
InterfaceSchema asInterface() except +reraise_kj_exception
|
||||
|
||||
cdef cppclass InterfaceSchema(Schema) nogil:
|
||||
cppclass SuperclassList nogil:
|
||||
uint size()
|
||||
InterfaceSchema operator[](uint index)
|
||||
|
||||
cppclass Method nogil:
|
||||
InterfaceNode.Method.Reader getProto()
|
||||
InterfaceSchema getContainingInterface()
|
||||
uint16_t getOrdinal()
|
||||
uint getIndex()
|
||||
StructSchema getParamType()
|
||||
StructSchema getResultType()
|
||||
|
||||
cppclass MethodList nogil:
|
||||
uint size()
|
||||
Method operator[](uint index)
|
||||
|
||||
MethodList getMethods()
|
||||
Maybe[Method] findMethodByName(StringPtr name)
|
||||
Method getMethodByName(StringPtr name)
|
||||
bint extends(InterfaceSchema other)
|
||||
SuperclassList getSuperclasses()
|
||||
# kj::Maybe<InterfaceSchema> findSuperclass(uint64_t typeId) const;
|
||||
|
||||
cdef cppclass StructSchema(Schema) nogil:
|
||||
cppclass Field nogil:
|
||||
StructNode.Member.Reader getProto()
|
||||
SchemaField.Reader getProto()
|
||||
StructSchema getContainingStruct()
|
||||
uint getIndex()
|
||||
SchemaType getType()
|
||||
@@ -192,7 +102,7 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
|
||||
|
||||
cdef cppclass EnumSchema nogil:
|
||||
cppclass Enumerant nogil:
|
||||
EnumNode.Enumerant.Reader getProto()
|
||||
SchemaEnumerant.Reader getProto()
|
||||
EnumSchema getContainingEnum()
|
||||
uint16_t getOrdinal()
|
||||
|
||||
@@ -207,11 +117,6 @@ cdef extern from "capnp/schema.h" namespace " ::capnp":
|
||||
cdef cppclass ListSchema nogil:
|
||||
SchemaType getElementType()
|
||||
|
||||
ListSchema listSchemaOfStruct" ::capnp::ListSchema::of"(StructSchema) nogil
|
||||
ListSchema listSchemaOfEnum" ::capnp::ListSchema::of"(EnumSchema) nogil
|
||||
ListSchema listSchemaOfInterface" ::capnp::ListSchema::of"(InterfaceSchema) nogil
|
||||
ListSchema listSchemaOfList" ::capnp::ListSchema::of"(ListSchema) nogil
|
||||
ListSchema listSchemaOfType" ::capnp::ListSchema::of"(SchemaType) nogil
|
||||
|
||||
cdef cppclass ConstSchema:
|
||||
pass
|
||||
@@ -222,8 +127,6 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
pass
|
||||
cppclass Builder nogil:
|
||||
pass
|
||||
cppclass Pipeline nogil:
|
||||
pass
|
||||
|
||||
enum Type:
|
||||
TYPE_UNKNOWN " ::capnp::DynamicValue::UNKNOWN"
|
||||
@@ -237,8 +140,6 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
TYPE_LIST " ::capnp::DynamicValue::LIST"
|
||||
TYPE_ENUM " ::capnp::DynamicValue::ENUM"
|
||||
TYPE_STRUCT " ::capnp::DynamicValue::STRUCT"
|
||||
TYPE_CAPABILITY " ::capnp::DynamicValue::CAPABILITY"
|
||||
TYPE_ANY_POINTER " ::capnp::DynamicValue::ANY_POINTER"
|
||||
|
||||
cdef cppclass DynamicStruct nogil:
|
||||
cppclass Reader nogil:
|
||||
@@ -250,11 +151,6 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
uint64_t getId"getSchema().getProto().getId"()
|
||||
Maybe[StructSchema.Field] which()
|
||||
MessageSize totalSize()
|
||||
cppclass Pipeline nogil:
|
||||
Pipeline()
|
||||
Pipeline(Pipeline &)
|
||||
DynamicValueForward.Pipeline get(char *)
|
||||
StructSchema getSchema()
|
||||
|
||||
cdef cppclass DynamicStruct_Builder" ::capnp::DynamicStruct::Builder" nogil:
|
||||
# Need to flatten this class out, since nested C++ classes cause havoc with cython fused types
|
||||
@@ -273,63 +169,9 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
StructSchema getSchema()
|
||||
uint64_t getId"getSchema().getProto().getId"()
|
||||
Maybe[StructSchema.Field] which()
|
||||
void adopt(char *, DynamicOrphan) except +reraise_kj_exception
|
||||
DynamicOrphan disown(char *)
|
||||
DynamicStruct.Reader asReader()
|
||||
MessageSize totalSize()
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicCapability nogil:
|
||||
cppclass Client nogil:
|
||||
Client()
|
||||
Client(Client&)
|
||||
Client(Own[PythonInterfaceDynamicImpl])
|
||||
Client upcast(InterfaceSchema requestedSchema) except +reraise_kj_exception
|
||||
DynamicCapability.Client castAs"castAs< ::capnp::DynamicCapability>"(InterfaceSchema)
|
||||
InterfaceSchema getSchema()
|
||||
Request newRequest(char * methodName)
|
||||
# Request newRequest(char * methodName, MessageSize)
|
||||
|
||||
cdef extern from "capnp/capability.h" namespace " ::capnp":
|
||||
cdef cppclass Response" ::capnp::Response< ::capnp::DynamicStruct>"(DynamicStruct.Reader) nogil:
|
||||
Response(Response)
|
||||
cdef cppclass RemotePromise" ::capnp::RemotePromise< ::capnp::DynamicStruct>"(
|
||||
Promise[Response], DynamicStruct.Pipeline) nogil:
|
||||
RemotePromise(RemotePromise)
|
||||
cdef cppclass Capability nogil:
|
||||
cppclass Client nogil:
|
||||
Client(Client&)
|
||||
DynamicCapability.Client castAs"castAs< ::capnp::DynamicCapability>"(InterfaceSchema)
|
||||
|
||||
cdef extern from "capnp/rpc-twoparty.h" namespace " ::capnp":
|
||||
cdef cppclass RpcSystem" ::capnp::RpcSystem<capnp::rpc::twoparty::SturdyRefHostId>" nogil:
|
||||
RpcSystem(RpcSystem&&)
|
||||
|
||||
cdef cppclass Side" ::capnp::rpc::twoparty::Side" nogil:
|
||||
pass
|
||||
cdef Side CLIENT" ::capnp::rpc::twoparty::Side::CLIENT"
|
||||
cdef Side SERVER" ::capnp::rpc::twoparty::Side::SERVER"
|
||||
|
||||
cdef cppclass TwoPartyVatNetwork nogil:
|
||||
TwoPartyVatNetwork(EventLoop &, AsyncIoStream& stream, Side, ReaderOptions)
|
||||
VoidPromise onDisconnect()
|
||||
VoidPromise onDrained()
|
||||
RpcSystem makeRpcServer(TwoPartyVatNetwork&, Capability.Client) nogil
|
||||
RpcSystem makeRpcClient(TwoPartyVatNetwork&) nogil
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass Request" ::capnp::Request< ::capnp::DynamicStruct, ::capnp::DynamicStruct>" nogil:
|
||||
Request()
|
||||
Request(Request &)
|
||||
DynamicValueForward.Builder get(char *) except +reraise_kj_exception
|
||||
bint has(char *) except +reraise_kj_exception
|
||||
void set(char *, DynamicValueForward.Reader) except +reraise_kj_exception
|
||||
DynamicValueForward.Builder init(char *, uint size) except +reraise_kj_exception
|
||||
DynamicValueForward.Builder init(char *) except +reraise_kj_exception
|
||||
StructSchema getSchema()
|
||||
Maybe[StructSchema.Field] which()
|
||||
RemotePromise send()
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicEnum nogil:
|
||||
uint16_t getRaw()
|
||||
@@ -346,35 +188,9 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
uint size()
|
||||
void set(uint index, DynamicValueForward.Reader value) except +reraise_kj_exception
|
||||
DynamicValueForward.Builder init(uint index, uint size) except +reraise_kj_exception
|
||||
void adopt(uint, DynamicOrphan) except +reraise_kj_exception
|
||||
DynamicOrphan disown(uint)
|
||||
StructSchema getStructElementType'getSchema().getStructElementType'()
|
||||
DynamicList.Reader asReader() except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/any.h" namespace " ::capnp":
|
||||
cdef cppclass AnyPointer nogil:
|
||||
cppclass Reader nogil:
|
||||
DynamicStruct.Reader getAs"getAs< ::capnp::DynamicStruct>"(StructSchema) except +reraise_kj_exception
|
||||
DynamicCapability.Client getAsCapability"getAs< ::capnp::DynamicCapability>"(
|
||||
InterfaceSchema) except +reraise_kj_exception
|
||||
DynamicList.Reader getAsList"getAs< ::capnp::DynamicList>"(ListSchema) except +reraise_kj_exception
|
||||
StringPtr getAsText"getAs< ::capnp::Text>"() except +reraise_kj_exception
|
||||
cppclass Builder nogil:
|
||||
Builder(Builder)
|
||||
DynamicStruct_Builder getAs"getAs< ::capnp::DynamicStruct>"(StructSchema) except +reraise_kj_exception
|
||||
DynamicCapability.Client getAsCapability"getAs< ::capnp::DynamicCapability>"(
|
||||
InterfaceSchema) except +reraise_kj_exception
|
||||
DynamicList.Builder getAsList"getAs< ::capnp::DynamicList>"(ListSchema) except +reraise_kj_exception
|
||||
StringPtr getAsText"getAs< ::capnp::Text>"() except +reraise_kj_exception
|
||||
void setAsStruct"setAs< ::capnp::DynamicStruct>"(DynamicStruct.Reader&) except +reraise_kj_exception
|
||||
void setAsText"setAs< ::capnp::Text>"(char*) except +reraise_kj_exception
|
||||
AnyPointer.Reader asReader() except +reraise_kj_exception
|
||||
void set(AnyPointer.Reader) except +reraise_kj_exception
|
||||
DynamicStruct_Builder initAsStruct"initAs< ::capnp::DynamicStruct>"(
|
||||
StructSchema) except +reraise_kj_exception
|
||||
DynamicList.Builder initAsList"initAs< ::capnp::DynamicList>"(ListSchema, uint) except +reraise_kj_exception
|
||||
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicValue nogil:
|
||||
cppclass Reader nogil:
|
||||
@@ -398,9 +214,6 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
Reader(DynamicList.Reader& value)
|
||||
Reader(DynamicEnum value)
|
||||
Reader(DynamicStruct.Reader& value)
|
||||
Reader(DynamicCapability.Client& value)
|
||||
Reader(Own[PythonInterfaceDynamicImpl] value)
|
||||
Reader(AnyPointer.Reader& value)
|
||||
Type getType()
|
||||
int64_t asInt"as<int64_t>"()
|
||||
uint64_t asUint"as<uint64_t>"()
|
||||
@@ -409,8 +222,6 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
StringPtr asText"as< ::capnp::Text>"()
|
||||
DynamicList.Reader asList"as< ::capnp::DynamicList>"()
|
||||
DynamicStruct.Reader asStruct"as< ::capnp::DynamicStruct>"()
|
||||
AnyPointer.Reader asObject"as< ::capnp::AnyPointer>"()
|
||||
DynamicCapability.Client asCapability"as< ::capnp::DynamicCapability>"()
|
||||
DynamicEnum asEnum"as< ::capnp::DynamicEnum>"()
|
||||
Data.Reader asData"as< ::capnp::Data>"()
|
||||
|
||||
@@ -423,22 +234,9 @@ cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
StringPtr asText"as< ::capnp::Text>"()
|
||||
DynamicList.Builder asList"as< ::capnp::DynamicList>"()
|
||||
DynamicStruct_Builder asStruct"as< ::capnp::DynamicStruct>"()
|
||||
AnyPointer.Builder asObject"as< ::capnp::AnyPointer>"()
|
||||
DynamicCapability.Client asCapability"as< ::capnp::DynamicCapability>"()
|
||||
DynamicEnum asEnum"as< ::capnp::DynamicEnum>"()
|
||||
Data.Builder asData"as< ::capnp::Data>"()
|
||||
|
||||
cppclass Pipeline nogil:
|
||||
Pipeline(Pipeline)
|
||||
DynamicCapability.Client asCapability"releaseAs< ::capnp::DynamicCapability>"()
|
||||
DynamicStruct.Pipeline asStruct"releaseAs< ::capnp::DynamicStruct>"()
|
||||
Type getType()
|
||||
|
||||
cdef extern from "capnp/schema-loader.h" namespace " ::capnp":
|
||||
cdef cppclass SchemaLoader nogil:
|
||||
SchemaLoader()
|
||||
Schema load(Node.Reader reader) except +reraise_kj_exception
|
||||
Schema get(uint64_t id_) except +reraise_kj_exception
|
||||
|
||||
cdef extern from "capnp/schema-parser.h" namespace " ::capnp":
|
||||
cdef cppclass ParsedSchema(Schema) nogil:
|
||||
@@ -446,53 +244,3 @@ cdef extern from "capnp/schema-parser.h" namespace " ::capnp":
|
||||
cdef cppclass SchemaParser nogil:
|
||||
SchemaParser()
|
||||
ParsedSchema parseDiskFile(char * displayName, char * diskPath, ArrayPtr[StringPtr] importPath)
|
||||
|
||||
cdef extern from "capnp/orphan.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicOrphan" ::capnp::Orphan< ::capnp::DynamicValue>" nogil:
|
||||
DynamicValue.Builder get()
|
||||
DynamicValue.Reader getReader()
|
||||
|
||||
cdef extern from "capnp/capability.h" namespace " ::capnp":
|
||||
cdef cppclass CallContext' ::capnp::CallContext< ::capnp::DynamicStruct, ::capnp::DynamicStruct>' nogil:
|
||||
CallContext(CallContext&)
|
||||
DynamicStruct.Reader getParams() except +reraise_kj_exception
|
||||
void releaseParams() except +reraise_kj_exception
|
||||
|
||||
DynamicStruct_Builder getResults()
|
||||
DynamicStruct_Builder initResults()
|
||||
void setResults(DynamicStruct.Reader value)
|
||||
# void adoptResults(Orphan<Results>&& value);
|
||||
# Orphanage getResultsOrphanage(uint firstSegmentWordSize = 0);
|
||||
VoidPromise tailCall(Request & tailRequest)
|
||||
|
||||
cdef extern from "kj/async.h" namespace " ::kj":
|
||||
cdef cppclass EventPort:
|
||||
bool wait() except* with gil
|
||||
bool poll() except* with gil
|
||||
void setRunnable(bool runnable) except* with gil
|
||||
cdef cppclass EventLoop nogil:
|
||||
EventLoop()
|
||||
EventLoop(EventPort &)
|
||||
void run()
|
||||
cdef cppclass WaitScope nogil:
|
||||
WaitScope(EventLoop &)
|
||||
void poll()
|
||||
cdef cppclass PromiseFulfiller[T] nogil:
|
||||
void fulfill(T&& value)
|
||||
void reject(Exception&& exception)
|
||||
cdef cppclass VoidPromiseFulfiller"::kj::PromiseFulfiller<void>" nogil:
|
||||
void fulfill()
|
||||
void reject(Exception&& exception)
|
||||
|
||||
cdef extern from "capnp/helpers/capabilityHelper.h":
|
||||
cdef cppclass PyAsyncIoStream(AsyncIoStream):
|
||||
PyAsyncIoStream(Own[PyRefCounter] thisptr)
|
||||
void rejectDisconnected[T](PromiseFulfiller[T]& fulfiller, StringPtr message)
|
||||
void rejectVoidDisconnected(VoidPromiseFulfiller& fulfiller, StringPtr message)
|
||||
Exception makeException(StringPtr message)
|
||||
PyPromise tryReadMessage(AsyncIoStream& stream, ReaderOptions opts)
|
||||
cppclass PythonInterfaceDynamicImpl:
|
||||
PythonInterfaceDynamicImpl(InterfaceSchema&, Own[PyRefCounter] server, Own[PyRefCounter] kj_loop)
|
||||
|
||||
cdef extern from "capnp/serialize-async.h" namespace " ::capnp":
|
||||
VoidPromise writeMessage(AsyncIoStream& output, MessageBuilder& builder)
|
||||
|
||||
@@ -1,648 +1,71 @@
|
||||
# schema.capnp.cpp.pyx
|
||||
# distutils: language = c++
|
||||
|
||||
from libc.stdint cimport *
|
||||
from capnp.helpers.non_circular cimport c_reraise_kj_exception as reraise_kj_exception
|
||||
|
||||
from capnp.includes.types cimport *
|
||||
|
||||
cdef extern from "capnp/dynamic.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicValue nogil:
|
||||
cppclass Reader nogil:
|
||||
pass
|
||||
cppclass Builder nogil:
|
||||
pass
|
||||
cdef cppclass DynamicStruct nogil:
|
||||
cppclass Reader nogil:
|
||||
pass
|
||||
|
||||
cdef cppclass DynamicStruct_Builder" ::capnp::DynamicStruct::Builder" nogil:
|
||||
cdef cppclass DynamicStruct_Builder " ::capnp::DynamicStruct::Builder" nogil:
|
||||
pass
|
||||
|
||||
cdef extern from "capnp/orphan.h" namespace " ::capnp":
|
||||
cdef cppclass DynamicOrphan" ::capnp::Orphan< ::capnp::DynamicValue>" nogil:
|
||||
DynamicValue.Builder get()
|
||||
DynamicValue.Reader getReader()
|
||||
|
||||
cdef extern from "capnp/schema.h" namespace " ::capnp":
|
||||
cdef cppclass Schema nogil:
|
||||
cdef cppclass StructSchema nogil:
|
||||
pass
|
||||
cdef cppclass StructSchema(Schema) nogil:
|
||||
pass
|
||||
|
||||
cdef extern from "capnp/any.h" namespace " ::capnp":
|
||||
cdef cppclass AnyPointer nogil:
|
||||
cppclass Reader nogil:
|
||||
pass
|
||||
cppclass Builder nogil:
|
||||
pass
|
||||
|
||||
cdef extern from "capnp/blob.h" namespace " ::capnp":
|
||||
cdef cppclass Data nogil:
|
||||
cppclass Reader nogil:
|
||||
char * begin()
|
||||
char* begin()
|
||||
size_t size()
|
||||
cppclass Builder nogil:
|
||||
char * begin()
|
||||
char* begin()
|
||||
size_t size()
|
||||
cdef cppclass Text nogil:
|
||||
cppclass Reader nogil:
|
||||
char * cStr()
|
||||
cppclass Builder nogil:
|
||||
char * cStr()
|
||||
cdef extern from "capnp/message.h" namespace " ::capnp":
|
||||
cdef cppclass List[T] nogil:
|
||||
cppclass Reader nogil:
|
||||
T operator[](uint)
|
||||
uint size()
|
||||
cppclass Builder nogil:
|
||||
T operator[](uint)
|
||||
uint size()
|
||||
char* cStr()
|
||||
|
||||
cdef extern from "capnp/schema.capnp.h" namespace " ::capnp::schema":
|
||||
enum:
|
||||
_ElementSize_inlineComposite " ::capnp::schema::ElementSize::INLINE_COMPOSITE"
|
||||
_ElementSize_eightBytes " ::capnp::schema::ElementSize::EIGHT_BYTES"
|
||||
_ElementSize_pointer " ::capnp::schema::ElementSize::POINTER"
|
||||
_ElementSize_bit " ::capnp::schema::ElementSize::BIT"
|
||||
_ElementSize_twoBytes " ::capnp::schema::ElementSize::TWO_BYTES"
|
||||
_ElementSize_fourBytes " ::capnp::schema::ElementSize::FOUR_BYTES"
|
||||
_ElementSize_byte " ::capnp::schema::ElementSize::BYTE"
|
||||
_ElementSize_empty " ::capnp::schema::ElementSize::EMPTY"
|
||||
enum _Value_Body_Which:
|
||||
_Value_Body_uint32Value " ::capnp::schema::Value::Body::Which::UINT32_VALUE"
|
||||
_Value_Body_float64Value " ::capnp::schema::Value::Body::Which::FLOAT64_VALUE"
|
||||
_Value_Body_voidValue " ::capnp::schema::Value::Body::Which::VOID_VALUE"
|
||||
_Value_Body_dataValue " ::capnp::schema::Value::Body::Which::DATA_VALUE"
|
||||
_Value_Body_listValue " ::capnp::schema::Value::Body::Which::LIST_VALUE"
|
||||
_Value_Body_int32Value " ::capnp::schema::Value::Body::Which::INT32_VALUE"
|
||||
_Value_Body_enumValue " ::capnp::schema::Value::Body::Which::ENUM_VALUE"
|
||||
_Value_Body_int8Value " ::capnp::schema::Value::Body::Which::INT8_VALUE"
|
||||
_Value_Body_boolValue " ::capnp::schema::Value::Body::Which::BOOL_VALUE"
|
||||
_Value_Body_int16Value " ::capnp::schema::Value::Body::Which::INT16_VALUE"
|
||||
_Value_Body_float32Value " ::capnp::schema::Value::Body::Which::FLOAT32_VALUE"
|
||||
_Value_Body_interfaceValue " ::capnp::schema::Value::Body::Which::INTERFACE_VALUE"
|
||||
_Value_Body_uint16Value " ::capnp::schema::Value::Body::Which::UINT16_VALUE"
|
||||
_Value_Body_uint8Value " ::capnp::schema::Value::Body::Which::UINT8_VALUE"
|
||||
_Value_Body_int64Value " ::capnp::schema::Value::Body::Which::INT64_VALUE"
|
||||
_Value_Body_structValue " ::capnp::schema::Value::Body::Which::STRUCT_VALUE"
|
||||
_Value_Body_textValue " ::capnp::schema::Value::Body::Which::TEXT_VALUE"
|
||||
_Value_Body_uint64Value " ::capnp::schema::Value::Body::Which::UINT64_VALUE"
|
||||
_Value_Body_objectValue " ::capnp::schema::Value::Body::Which::OBJECT_VALUE"
|
||||
enum _Type_Body_Which:
|
||||
_Type_Body_boolType " ::capnp::schema::Type::Body::Which::BOOL_TYPE"
|
||||
_Type_Body_structType " ::capnp::schema::Type::Body::Which::STRUCT_TYPE"
|
||||
_Type_Body_int32Type " ::capnp::schema::Type::Body::Which::INT32_TYPE"
|
||||
_Type_Body_voidType " ::capnp::schema::Type::Body::Which::VOID_TYPE"
|
||||
_Type_Body_uint16Type " ::capnp::schema::Type::Body::Which::UINT16_TYPE"
|
||||
_Type_Body_dataType " ::capnp::schema::Type::Body::Which::DATA_TYPE"
|
||||
_Type_Body_objectType " ::capnp::schema::Type::Body::Which::OBJECT_TYPE"
|
||||
_Type_Body_int64Type " ::capnp::schema::Type::Body::Which::INT64_TYPE"
|
||||
_Type_Body_float64Type " ::capnp::schema::Type::Body::Which::FLOAT64_TYPE"
|
||||
_Type_Body_interfaceType " ::capnp::schema::Type::Body::Which::INTERFACE_TYPE"
|
||||
_Type_Body_uint32Type " ::capnp::schema::Type::Body::Which::UINT32_TYPE"
|
||||
_Type_Body_uint8Type " ::capnp::schema::Type::Body::Which::UINT8_TYPE"
|
||||
_Type_Body_listType " ::capnp::schema::Type::Body::Which::LIST_TYPE"
|
||||
_Type_Body_int8Type " ::capnp::schema::Type::Body::Which::INT8_TYPE"
|
||||
_Type_Body_float32Type " ::capnp::schema::Type::Body::Which::FLOAT32_TYPE"
|
||||
_Type_Body_enumType " ::capnp::schema::Type::Body::Which::ENUM_TYPE"
|
||||
_Type_Body_uint64Type " ::capnp::schema::Type::Body::Which::UINT64_TYPE"
|
||||
_Type_Body_textType " ::capnp::schema::Type::Body::Which::TEXT_TYPE"
|
||||
_Type_Body_int16Type " ::capnp::schema::Type::Body::Which::INT16_TYPE"
|
||||
enum _Node_Body_Which:
|
||||
_Node_Body_annotationNode " ::capnp::schema::Node::Body::Which::ANNOTATION_NODE"
|
||||
_Node_Body_interfaceNode " ::capnp::schema::Node::Body::Which::INTERFACE_NODE"
|
||||
_Node_Body_enumNode " ::capnp::schema::Node::Body::Which::ENUM_NODE"
|
||||
_Node_Body_structNode " ::capnp::schema::Node::Body::Which::STRUCT_NODE"
|
||||
_Node_Body_constNode " ::capnp::schema::Node::Body::Which::CONST_NODE"
|
||||
_Node_Body_fileNode " ::capnp::schema::Node::Body::Which::FILE_NODE"
|
||||
enum _StructNode_Member_Body_Which:
|
||||
_StructNode_Member_Body_fieldMember " ::capnp::schema::StructNode::Member::Body::Which::FIELD_MEMBER"
|
||||
_StructNode_Member_Body_unionMember " ::capnp::schema::StructNode::Member::Body::Which::UNION_MEMBER"
|
||||
cdef cppclass CodeGeneratorRequest
|
||||
|
||||
cdef cppclass InterfaceNode
|
||||
cdef cppclass Value
|
||||
cdef cppclass ConstNode
|
||||
cdef cppclass Type
|
||||
cdef cppclass FileNode
|
||||
cdef cppclass Node
|
||||
cdef cppclass AnnotationNode
|
||||
cdef cppclass EnumNode
|
||||
cdef cppclass StructNode
|
||||
cdef cppclass Annotation
|
||||
cdef cppclass CodeGeneratorRequest nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
List[CodeGeneratorRequest.Node].Reader getNodes()
|
||||
List[UInt64].Reader getRequestedFiles()
|
||||
cppclass Builder nogil:
|
||||
|
||||
List[CodeGeneratorRequest.Node].Builder getNodes()
|
||||
List[CodeGeneratorRequest.Node].Builder initNodes(int)
|
||||
List[UInt64].Builder getRequestedFiles()
|
||||
List[UInt64].Builder initRequestedFiles(int)
|
||||
|
||||
cdef cppclass InterfaceNode nogil:
|
||||
cppclass Method
|
||||
|
||||
cppclass Method:
|
||||
cppclass Param
|
||||
|
||||
cppclass Param nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Value getDefaultValue()
|
||||
Type getType()
|
||||
Text.Reader getName()
|
||||
List[InterfaceNode.Method.Param.Annotation].Reader getAnnotations()
|
||||
cppclass Builder nogil:
|
||||
|
||||
Value getDefaultValue()
|
||||
void setDefaultValue(Value)
|
||||
Type getType()
|
||||
void setType(Type)
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
List[InterfaceNode.Method.Param.Annotation].Builder getAnnotations()
|
||||
List[InterfaceNode.Method.Param.Annotation].Builder initAnnotations(int)
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt16 getCodeOrder()
|
||||
Text.Reader getName()
|
||||
List[InterfaceNode.Method.InterfaceNode.Method.Param].Reader getParams()
|
||||
UInt16 getRequiredParamCount()
|
||||
Type getReturnType()
|
||||
List[InterfaceNode.Method.Annotation].Reader getAnnotations()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt16 getCodeOrder()
|
||||
void setCodeOrder(UInt16)
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
List[InterfaceNode.Method.InterfaceNode.Method.Param].Builder getParams()
|
||||
List[InterfaceNode.Method.InterfaceNode.Method.Param].Builder initParams(int)
|
||||
UInt16 getRequiredParamCount()
|
||||
void setRequiredParamCount(UInt16)
|
||||
Type getReturnType()
|
||||
void setReturnType(Type)
|
||||
List[InterfaceNode.Method.Annotation].Builder getAnnotations()
|
||||
List[InterfaceNode.Method.Annotation].Builder initAnnotations(int)
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
List[InterfaceNode.InterfaceNode.Method].Reader getMethods()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
List[InterfaceNode.InterfaceNode.Method].Builder getMethods()
|
||||
List[InterfaceNode.InterfaceNode.Method].Builder initMethods(int)
|
||||
|
||||
cdef cppclass Value nogil:
|
||||
cppclass Body
|
||||
|
||||
cppclass Body nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
int which()
|
||||
UInt32 getUint32Value()
|
||||
Float64 getFloat64Value()
|
||||
Void getVoidValue()
|
||||
Data.Reader getDataValue()
|
||||
Object getListValue()
|
||||
Int32 getInt32Value()
|
||||
UInt16 getEnumValue()
|
||||
Int8 getInt8Value()
|
||||
Bool getBoolValue()
|
||||
Int16 getInt16Value()
|
||||
Float32 getFloat32Value()
|
||||
Void getInterfaceValue()
|
||||
UInt16 getUint16Value()
|
||||
UInt8 getUint8Value()
|
||||
Int64 getInt64Value()
|
||||
Object getStructValue()
|
||||
Text.Reader getTextValue()
|
||||
UInt64 getUint64Value()
|
||||
Object getObjectValue()
|
||||
|
||||
cppclass Builder nogil:
|
||||
int which()
|
||||
UInt32 getUint32Value()
|
||||
void setUint32Value(UInt32)
|
||||
Float64 getFloat64Value()
|
||||
void setFloat64Value(Float64)
|
||||
Void getVoidValue()
|
||||
void setVoidValue(Void)
|
||||
Data.Builder getDataValue()
|
||||
void setDataValue(Data)
|
||||
Object getListValue()
|
||||
void setListValue(Object)
|
||||
Int32 getInt32Value()
|
||||
void setInt32Value(Int32)
|
||||
UInt16 getEnumValue()
|
||||
void setEnumValue(UInt16)
|
||||
Int8 getInt8Value()
|
||||
void setInt8Value(Int8)
|
||||
Bool getBoolValue()
|
||||
void setBoolValue(Bool)
|
||||
Int16 getInt16Value()
|
||||
void setInt16Value(Int16)
|
||||
Float32 getFloat32Value()
|
||||
void setFloat32Value(Float32)
|
||||
Void getInterfaceValue()
|
||||
void setInterfaceValue(Void)
|
||||
UInt16 getUint16Value()
|
||||
void setUint16Value(UInt16)
|
||||
UInt8 getUint8Value()
|
||||
void setUint8Value(UInt8)
|
||||
Int64 getInt64Value()
|
||||
void setInt64Value(Int64)
|
||||
Object getStructValue()
|
||||
void setStructValue(Object)
|
||||
Text.Builder getTextValue()
|
||||
void setTextValue(Text)
|
||||
UInt64 getUint64Value()
|
||||
void setUint64Value(UInt64)
|
||||
Object getObjectValue()
|
||||
void setObjectValue(Object)
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Value.Body getBody()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
Value.Body getBody()
|
||||
void setBody(Value.Body)
|
||||
|
||||
cdef cppclass ConstNode nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Type getType()
|
||||
Value getValue()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
Type getType()
|
||||
void setType(Type)
|
||||
Value getValue()
|
||||
void setValue(Value)
|
||||
|
||||
cdef cppclass Type nogil:
|
||||
cppclass Body
|
||||
|
||||
cppclass Body nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
int which()
|
||||
Void getBoolType()
|
||||
UInt64 getStructType()
|
||||
Void getInt32Type()
|
||||
Void getVoidType()
|
||||
Void getUint16Type()
|
||||
Void getDataType()
|
||||
Void getObjectType()
|
||||
Void getInt64Type()
|
||||
Void getFloat64Type()
|
||||
UInt64 getInterfaceType()
|
||||
Void getUint32Type()
|
||||
Void getUint8Type()
|
||||
Type getListType()
|
||||
Void getInt8Type()
|
||||
Void getFloat32Type()
|
||||
UInt64 getEnumType()
|
||||
Void getUint64Type()
|
||||
Void getTextType()
|
||||
Void getInt16Type()
|
||||
|
||||
cppclass Builder nogil:
|
||||
int which()
|
||||
Void getBoolType()
|
||||
void setBoolType(Void)
|
||||
UInt64 getStructType()
|
||||
void setStructType(UInt64)
|
||||
Void getInt32Type()
|
||||
void setInt32Type(Void)
|
||||
Void getVoidType()
|
||||
void setVoidType(Void)
|
||||
Void getUint16Type()
|
||||
void setUint16Type(Void)
|
||||
Void getDataType()
|
||||
void setDataType(Void)
|
||||
Void getObjectType()
|
||||
void setObjectType(Void)
|
||||
Void getInt64Type()
|
||||
void setInt64Type(Void)
|
||||
Void getFloat64Type()
|
||||
void setFloat64Type(Void)
|
||||
UInt64 getInterfaceType()
|
||||
void setInterfaceType(UInt64)
|
||||
Void getUint32Type()
|
||||
void setUint32Type(Void)
|
||||
Void getUint8Type()
|
||||
void setUint8Type(Void)
|
||||
Type getListType()
|
||||
void setListType(Type)
|
||||
Void getInt8Type()
|
||||
void setInt8Type(Void)
|
||||
Void getFloat32Type()
|
||||
void setFloat32Type(Void)
|
||||
UInt64 getEnumType()
|
||||
void setEnumType(UInt64)
|
||||
Void getUint64Type()
|
||||
void setUint64Type(Void)
|
||||
Void getTextType()
|
||||
void setTextType(Void)
|
||||
Void getInt16Type()
|
||||
void setInt16Type(Void)
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Type.Body getBody()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
Type.Body getBody()
|
||||
void setBody(Type.Body)
|
||||
|
||||
cdef cppclass FileNode nogil:
|
||||
cppclass Import
|
||||
|
||||
cppclass Import nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt64 getId()
|
||||
Text.Reader getName()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt64 getId()
|
||||
void setId(UInt64)
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
List[FileNode.FileNode.Import].Reader getImports()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
List[FileNode.FileNode.Import].Builder getImports()
|
||||
List[FileNode.FileNode.Import].Builder initImports(int)
|
||||
|
||||
cdef cppclass Node nogil:
|
||||
cppclass Body
|
||||
cppclass NestedNode
|
||||
|
||||
cppclass Body nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
int which()
|
||||
AnnotationNode getAnnotationNode()
|
||||
InterfaceNode getInterfaceNode()
|
||||
EnumNode getEnumNode()
|
||||
StructNode getStructNode()
|
||||
ConstNode getConstNode()
|
||||
FileNode getFileNode()
|
||||
cppclass Builder nogil:
|
||||
int which()
|
||||
AnnotationNode getAnnotationNode()
|
||||
void setAnnotationNode(AnnotationNode)
|
||||
InterfaceNode getInterfaceNode()
|
||||
void setInterfaceNode(InterfaceNode)
|
||||
EnumNode getEnumNode()
|
||||
void setEnumNode(EnumNode)
|
||||
StructNode getStructNode()
|
||||
void setStructNode(StructNode)
|
||||
ConstNode getConstNode()
|
||||
void setConstNode(ConstNode)
|
||||
FileNode getFileNode()
|
||||
void setFileNode(FileNode)
|
||||
|
||||
cppclass NestedNode nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Text.Reader getName()
|
||||
UInt64 getId()
|
||||
cppclass Builder nogil:
|
||||
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
UInt64 getId()
|
||||
void setId(UInt64)
|
||||
uint64_t getId()
|
||||
cppclass Reader nogil:
|
||||
|
||||
Node.Body getBody()
|
||||
Text.Reader getDisplayName()
|
||||
List[Node.Annotation].Reader getAnnotations()
|
||||
UInt64 getScopeId()
|
||||
List[Node.Node.NestedNode].Reader getNestedNodes()
|
||||
UInt64 getId()
|
||||
bint isFile()
|
||||
uint64_t getScopeId()
|
||||
uint64_t getId()
|
||||
ListNestedNodeReader getNestedNodes()
|
||||
bint isStruct()
|
||||
bint isEnum()
|
||||
bint isInterface()
|
||||
bint isConst()
|
||||
bint isAnnotation()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
Node.Body getBody()
|
||||
void setBody(Node.Body)
|
||||
Text.Builder getDisplayName()
|
||||
void setDisplayName(Text)
|
||||
List[Node.Annotation].Builder getAnnotations()
|
||||
List[Node.Annotation].Builder initAnnotations(int)
|
||||
UInt64 getScopeId()
|
||||
void setScopeId(UInt64)
|
||||
List[Node.Node.NestedNode].Builder getNestedNodes()
|
||||
List[Node.Node.NestedNode].Builder initNestedNodes(int)
|
||||
UInt64 getId()
|
||||
void setId(UInt64)
|
||||
bint isFile()
|
||||
bint isStruct()
|
||||
bint isEnum()
|
||||
bint isInterface()
|
||||
bint isConst()
|
||||
bint isAnnotation()
|
||||
|
||||
cdef cppclass AnnotationNode nogil:
|
||||
|
||||
cdef cppclass Field nogil:
|
||||
cppclass Reader nogil:
|
||||
Text.Reader getName()
|
||||
|
||||
Bool getTargetsField()
|
||||
Bool getTargetsConst()
|
||||
Bool getTargetsFile()
|
||||
Bool getTargetsStruct()
|
||||
Bool getTargetsParam()
|
||||
Bool getTargetsUnion()
|
||||
Bool getTargetsAnnotation()
|
||||
Bool getTargetsEnumerant()
|
||||
Type getType()
|
||||
Bool getTargetsEnum()
|
||||
Bool getTargetsInterface()
|
||||
Bool getTargetsMethod()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
Bool getTargetsField()
|
||||
void setTargetsField(Bool)
|
||||
Bool getTargetsConst()
|
||||
void setTargetsConst(Bool)
|
||||
Bool getTargetsFile()
|
||||
void setTargetsFile(Bool)
|
||||
Bool getTargetsStruct()
|
||||
void setTargetsStruct(Bool)
|
||||
Bool getTargetsParam()
|
||||
void setTargetsParam(Bool)
|
||||
Bool getTargetsUnion()
|
||||
void setTargetsUnion(Bool)
|
||||
Bool getTargetsAnnotation()
|
||||
void setTargetsAnnotation(Bool)
|
||||
Bool getTargetsEnumerant()
|
||||
void setTargetsEnumerant(Bool)
|
||||
Type getType()
|
||||
void setType(Type)
|
||||
Bool getTargetsEnum()
|
||||
void setTargetsEnum(Bool)
|
||||
Bool getTargetsInterface()
|
||||
void setTargetsInterface(Bool)
|
||||
Bool getTargetsMethod()
|
||||
void setTargetsMethod(Bool)
|
||||
|
||||
cdef cppclass EnumNode nogil:
|
||||
cppclass Enumerant
|
||||
|
||||
cppclass Enumerant nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt16 getCodeOrder()
|
||||
Text.Reader getName()
|
||||
List[EnumNode.Enumerant.Annotation].Reader getAnnotations()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt16 getCodeOrder()
|
||||
void setCodeOrder(UInt16)
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
List[EnumNode.Enumerant.Annotation].Builder getAnnotations()
|
||||
List[EnumNode.Enumerant.Annotation].Builder initAnnotations(int)
|
||||
|
||||
cdef cppclass Enumerant nogil:
|
||||
cppclass Reader nogil:
|
||||
Text.Reader getName()
|
||||
|
||||
List[EnumNode.EnumNode.Enumerant].Reader getEnumerants()
|
||||
|
||||
cppclass Builder nogil:
|
||||
|
||||
List[EnumNode.EnumNode.Enumerant].Builder getEnumerants()
|
||||
List[EnumNode.EnumNode.Enumerant].Builder initEnumerants(int)
|
||||
|
||||
cdef cppclass StructNode nogil:
|
||||
cppclass Union
|
||||
cppclass Member
|
||||
cppclass Field
|
||||
|
||||
cppclass Union nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt32 getDiscriminantOffset()
|
||||
List[StructNode.Union.StructNode.Member].Reader getMembers()
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt32 getDiscriminantOffset()
|
||||
void setDiscriminantOffset(UInt32)
|
||||
List[StructNode.Union.StructNode.Member].Builder getMembers()
|
||||
List[StructNode.Union.StructNode.Member].Builder initMembers(int)
|
||||
cppclass Member nogil:
|
||||
cppclass Body
|
||||
|
||||
cppclass Body nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
int which()
|
||||
Field getFieldMember()
|
||||
Union getUnionMember()
|
||||
cppclass Builder nogil:
|
||||
int which()
|
||||
Field getFieldMember()
|
||||
void setFieldMember(Field)
|
||||
Union getUnionMember()
|
||||
void setUnionMember(Union)
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt16 getOrdinal()
|
||||
StructNode.Member.Body getBody()
|
||||
UInt16 getCodeOrder()
|
||||
Text.Reader getName()
|
||||
List[StructNode.Member.Annotation].Reader getAnnotations()
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt16 getOrdinal()
|
||||
void setOrdinal(UInt16)
|
||||
StructNode.Member.Body getBody()
|
||||
void setBody(StructNode.Member.Body)
|
||||
UInt16 getCodeOrder()
|
||||
void setCodeOrder(UInt16)
|
||||
Text.Builder getName()
|
||||
void setName(Text)
|
||||
List[StructNode.Member.Annotation].Builder getAnnotations()
|
||||
List[StructNode.Member.Annotation].Builder initAnnotations(int)
|
||||
|
||||
cppclass Field nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
Value getDefaultValue()
|
||||
Type getType()
|
||||
UInt32 getOffset()
|
||||
cppclass Builder nogil:
|
||||
|
||||
Value getDefaultValue()
|
||||
void setDefaultValue(Value)
|
||||
Type getType()
|
||||
void setType(Type)
|
||||
UInt32 getOffset()
|
||||
void setOffset(UInt32)
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt16 getDataSectionWordSize()
|
||||
List[StructNode.StructNode.Member].Reader getMembers()
|
||||
UInt16 getPointerSectionSize()
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt16 getDataSectionWordSize()
|
||||
void setDataSectionWordSize(UInt16)
|
||||
List[StructNode.StructNode.Member].Builder getMembers()
|
||||
List[StructNode.StructNode.Member].Builder initMembers(int)
|
||||
UInt16 getPointerSectionSize()
|
||||
void setPointerSectionSize(UInt16)
|
||||
cdef cppclass Annotation nogil:
|
||||
|
||||
cppclass Reader nogil:
|
||||
|
||||
UInt64 getId()
|
||||
Value getValue()
|
||||
cppclass Builder nogil:
|
||||
|
||||
UInt64 getId()
|
||||
void setId(UInt64)
|
||||
Value getValue()
|
||||
void setValue(Value)
|
||||
cdef cppclass ListNestedNodeReader"capnp::List<capnp::schema::Node::NestedNode>::Reader" nogil:
|
||||
ListNestedNodeReader()
|
||||
ListNestedNodeReader(ListNestedNodeReader)
|
||||
Node.NestedNode.Reader operator[](uint)
|
||||
cdef cppclass ListNestedNodeReader "capnp::List<capnp::schema::Node::NestedNode>::Reader" nogil:
|
||||
uint size()
|
||||
Node.NestedNode.Reader operator[](uint)
|
||||
|
||||
cdef extern from "capnp/common.h" namespace " ::capnp":
|
||||
cdef cppclass word nogil:
|
||||
pass
|
||||
|
||||
cdef extern from "kj/common.h" namespace " ::kj":
|
||||
cdef cppclass WordArrayPtr " ::kj::ArrayPtr< ::capnp::word>" nogil:
|
||||
WordArrayPtr(word*, size_t)
|
||||
|
||||
cdef extern from "kj/array.h" namespace " ::kj":
|
||||
cdef cppclass WordArray " ::kj::Array< ::capnp::word>" nogil:
|
||||
word* begin()
|
||||
size_t size()
|
||||
|
||||
cdef extern from "capnp/message.h" namespace " ::capnp":
|
||||
cdef cppclass ReaderOptions nogil:
|
||||
@@ -650,177 +73,20 @@ cdef extern from "capnp/message.h" namespace " ::capnp":
|
||||
uint nestingLimit
|
||||
|
||||
cdef cppclass MessageBuilder nogil:
|
||||
CodeGeneratorRequest.Builder getRootCodeGeneratorRequest'getRoot< ::capnp::schema::CodeGeneratorRequest>'()
|
||||
CodeGeneratorRequest.Builder initRootCodeGeneratorRequest'initRoot< ::capnp::schema::CodeGeneratorRequest>'()
|
||||
InterfaceNode.Builder getRootInterfaceNode'getRoot< ::capnp::schema::InterfaceNode>'()
|
||||
InterfaceNode.Builder initRootInterfaceNode'initRoot< ::capnp::schema::InterfaceNode>'()
|
||||
Value.Builder getRootValue'getRoot< ::capnp::schema::Value>'()
|
||||
Value.Builder initRootValue'initRoot< ::capnp::schema::Value>'()
|
||||
ConstNode.Builder getRootConstNode'getRoot< ::capnp::schema::ConstNode>'()
|
||||
ConstNode.Builder initRootConstNode'initRoot< ::capnp::schema::ConstNode>'()
|
||||
Type.Builder getRootType'getRoot< ::capnp::schema::Type>'()
|
||||
Type.Builder initRootType'initRoot< ::capnp::schema::Type>'()
|
||||
FileNode.Builder getRootFileNode'getRoot< ::capnp::schema::FileNode>'()
|
||||
FileNode.Builder initRootFileNode'initRoot< ::capnp::schema::FileNode>'()
|
||||
Node.Builder getRootNode'getRoot< ::capnp::schema::Node>'()
|
||||
Node.Builder initRootNode'initRoot< ::capnp::schema::Node>'()
|
||||
AnnotationNode.Builder getRootAnnotationNode'getRoot< ::capnp::schema::AnnotationNode>'()
|
||||
AnnotationNode.Builder initRootAnnotationNode'initRoot< ::capnp::schema::AnnotationNode>'()
|
||||
EnumNode.Builder getRootEnumNode'getRoot< ::capnp::schema::EnumNode>'()
|
||||
EnumNode.Builder initRootEnumNode'initRoot< ::capnp::schema::EnumNode>'()
|
||||
StructNode.Builder getRootStructNode'getRoot< ::capnp::schema::StructNode>'()
|
||||
StructNode.Builder initRootStructNode'initRoot< ::capnp::schema::StructNode>'()
|
||||
Annotation.Builder getRootAnnotation'getRoot< ::capnp::schema::Annotation>'()
|
||||
Annotation.Builder initRootAnnotation'initRoot< ::capnp::schema::Annotation>'()
|
||||
|
||||
DynamicStruct_Builder getRootDynamicStruct'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
DynamicStruct_Builder initRootDynamicStruct'initRoot< ::capnp::DynamicStruct>'(StructSchema)
|
||||
void setRootDynamicStruct'setRoot< ::capnp::DynamicStruct::Reader>'(DynamicStruct.Reader)
|
||||
|
||||
ConstWordArrayArrayPtr getSegmentsForOutput'getSegmentsForOutput'()
|
||||
|
||||
AnyPointer.Builder getRootAnyPointer'getRoot< ::capnp::AnyPointer>'()
|
||||
|
||||
DynamicOrphan newOrphan'getOrphanage().newOrphan'(StructSchema)
|
||||
DynamicStruct_Builder getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
DynamicStruct_Builder initRootDynamicStruct 'initRoot< ::capnp::DynamicStruct>'(StructSchema)
|
||||
void setRootDynamicStruct 'setRoot< ::capnp::DynamicStruct::Reader>'(DynamicStruct.Reader)
|
||||
|
||||
cdef cppclass MessageReader nogil:
|
||||
CodeGeneratorRequest.Reader getRootCodeGeneratorRequest'getRoot< ::capnp::schema::CodeGeneratorRequest>'()
|
||||
InterfaceNode.Reader getRootInterfaceNode'getRoot< ::capnp::schema::InterfaceNode>'()
|
||||
Value.Reader getRootValue'getRoot< ::capnp::schema::Value>'()
|
||||
ConstNode.Reader getRootConstNode'getRoot< ::capnp::schema::ConstNode>'()
|
||||
Type.Reader getRootType'getRoot< ::capnp::schema::Type>'()
|
||||
FileNode.Reader getRootFileNode'getRoot< ::capnp::schema::FileNode>'()
|
||||
Node.Reader getRootNode'getRoot< ::capnp::schema::Node>'()
|
||||
AnnotationNode.Reader getRootAnnotationNode'getRoot< ::capnp::schema::AnnotationNode>'()
|
||||
EnumNode.Reader getRootEnumNode'getRoot< ::capnp::schema::EnumNode>'()
|
||||
StructNode.Reader getRootStructNode'getRoot< ::capnp::schema::StructNode>'()
|
||||
Annotation.Reader getRootAnnotation'getRoot< ::capnp::schema::Annotation>'()
|
||||
|
||||
DynamicStruct.Reader getRootDynamicStruct'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
AnyPointer.Reader getRootAnyPointer'getRoot< ::capnp::AnyPointer>'()
|
||||
DynamicStruct.Reader getRootDynamicStruct 'getRoot< ::capnp::DynamicStruct>'(StructSchema) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass MallocMessageBuilder(MessageBuilder) nogil:
|
||||
MallocMessageBuilder()
|
||||
MallocMessageBuilder(int)
|
||||
|
||||
cdef cppclass SegmentArrayMessageReader(MessageReader) nogil:
|
||||
SegmentArrayMessageReader(ConstWordArrayArrayPtr array) except +reraise_kj_exception
|
||||
SegmentArrayMessageReader(ConstWordArrayArrayPtr array, ReaderOptions) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass FlatMessageBuilder(MessageBuilder) nogil:
|
||||
FlatMessageBuilder(WordArrayPtr array)
|
||||
FlatMessageBuilder(WordArrayPtr array, ReaderOptions)
|
||||
|
||||
enum Void:
|
||||
VOID
|
||||
|
||||
cdef extern from "PyCustomMessageBuilder.h" namespace " ::capnp":
|
||||
cdef cppclass PyCustomMessageBuilder(MessageBuilder):
|
||||
PyCustomMessageBuilder(PyObject* allocateSegmentCallable)
|
||||
PyCustomMessageBuilder(PyObject* allocateSegmentCallable, int firstSegmentSize)
|
||||
|
||||
cdef extern from "capnp/common.h" namespace " ::capnp":
|
||||
cdef cppclass word nogil:
|
||||
pass
|
||||
|
||||
cdef extern from "kj/common.h" namespace " ::kj":
|
||||
# Cython can't handle ArrayPtr[word] as a function argument
|
||||
cdef cppclass WordArrayPtr " ::kj::ArrayPtr< ::capnp::word>" nogil:
|
||||
WordArrayPtr()
|
||||
WordArrayPtr(word *, size_t size)
|
||||
size_t size()
|
||||
word& operator[](size_t index)
|
||||
cdef cppclass ByteArrayPtr " ::kj::ArrayPtr< ::capnp::byte>" nogil:
|
||||
ByteArrayPtr()
|
||||
ByteArrayPtr(byte *, size_t size)
|
||||
size_t size()
|
||||
byte& operator[](size_t index)
|
||||
cdef cppclass ConstWordArrayPtr " ::kj::ArrayPtr< const ::capnp::word>" nogil:
|
||||
ConstWordArrayPtr()
|
||||
ConstWordArrayPtr(word *, size_t size)
|
||||
size_t size()
|
||||
const word* begin()
|
||||
cdef cppclass ConstWordArrayArrayPtr " ::kj::ArrayPtr< const ::kj::ArrayPtr< const ::capnp::word>>" nogil:
|
||||
ConstWordArrayArrayPtr()
|
||||
ConstWordArrayArrayPtr(ConstWordArrayPtr*, size_t size)
|
||||
size_t size()
|
||||
ConstWordArrayPtr& operator[](size_t index)
|
||||
|
||||
cdef extern from "kj/array.h" namespace " ::kj":
|
||||
# Cython can't handle Array[word] as a function argument
|
||||
cdef cppclass WordArray " ::kj::Array< ::capnp::word>" nogil:
|
||||
word* begin()
|
||||
size_t size()
|
||||
cdef cppclass ByteArray " ::kj::Array< ::capnp::byte>" nogil:
|
||||
char* begin()
|
||||
size_t size()
|
||||
|
||||
cdef extern from "kj/array.h" namespace " ::kj":
|
||||
cdef cppclass InputStream nogil:
|
||||
void read(void* buffer, size_t bytes) except +reraise_kj_exception
|
||||
size_t read(void* buffer, size_t minBytes, size_t maxBytes) except +reraise_kj_exception
|
||||
size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) except +reraise_kj_exception
|
||||
void skip(size_t bytes) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass OutputStream nogil:
|
||||
void write(const void* buffer, size_t size) except +reraise_kj_exception
|
||||
# void write(ArrayPtr<const ArrayPtr<const byte>> pieces);
|
||||
|
||||
cdef cppclass BufferedInputStream(InputStream) nogil:
|
||||
pass
|
||||
cdef cppclass BufferedOutputStream(OutputStream) nogil:
|
||||
pass
|
||||
|
||||
cdef cppclass BufferedInputStreamWrapper(BufferedInputStream) nogil:
|
||||
BufferedInputStreamWrapper(InputStream&)
|
||||
cdef cppclass BufferedOutputStreamWrapper(BufferedOutputStream) nogil:
|
||||
BufferedOutputStreamWrapper(OutputStream&)
|
||||
|
||||
cdef cppclass ArrayInputStream(BufferedInputStream) nogil:
|
||||
ArrayInputStream(ByteArrayPtr)
|
||||
ByteArrayPtr getArray()
|
||||
# ByteArrayPtr tryGetReadBuffer() except +reraise_kj_exception
|
||||
cdef cppclass ArrayOutputStream(BufferedOutputStream) nogil:
|
||||
ArrayOutputStream(ByteArrayPtr)
|
||||
ByteArrayPtr getArray()
|
||||
ByteArrayPtr getWriteBuffer()
|
||||
|
||||
cdef cppclass FdInputStream(InputStream) nogil:
|
||||
FdInputStream(int)
|
||||
cdef cppclass FdOutputStream(OutputStream) nogil:
|
||||
FdOutputStream(int)
|
||||
|
||||
cdef extern from "capnp/serialize.h" namespace " ::capnp":
|
||||
cdef cppclass InputStreamMessageReader(MessageReader) nogil:
|
||||
InputStreamMessageReader(InputStream&) except +reraise_kj_exception
|
||||
InputStreamMessageReader(InputStream&, ReaderOptions) except +reraise_kj_exception
|
||||
cdef cppclass StreamFdMessageReader(MessageReader) nogil:
|
||||
StreamFdMessageReader(int) except +reraise_kj_exception
|
||||
StreamFdMessageReader(int, ReaderOptions) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass FlatArrayMessageReader(MessageReader) nogil:
|
||||
FlatArrayMessageReader(WordArrayPtr array) except +reraise_kj_exception
|
||||
FlatArrayMessageReader(WordArrayPtr array, ReaderOptions) except +reraise_kj_exception
|
||||
FlatArrayMessageReader(WordArrayPtr, ReaderOptions) except +reraise_kj_exception
|
||||
const word* getEnd() const
|
||||
|
||||
void writeMessageToFd(int, MessageBuilder&) except +reraise_kj_exception nogil
|
||||
|
||||
WordArray messageToFlatArray(MessageBuilder &) nogil
|
||||
|
||||
cdef extern from "capnp/serialize-packed.h" namespace " ::capnp":
|
||||
cdef cppclass PackedInputStream(InputStream) nogil:
|
||||
PackedInputStream(BufferedInputStream&) except +reraise_kj_exception
|
||||
cdef cppclass PackedOutputStream(OutputStream) nogil:
|
||||
PackedOutputStream(BufferedOutputStream&) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass PackedMessageReader(MessageReader) nogil:
|
||||
PackedMessageReader(BufferedInputStream&) except +reraise_kj_exception
|
||||
PackedMessageReader(BufferedInputStream&, ReaderOptions) except +reraise_kj_exception
|
||||
|
||||
cdef cppclass PackedFdMessageReader(MessageReader) nogil:
|
||||
PackedFdMessageReader(int) except +reraise_kj_exception
|
||||
PackedFdMessageReader(int, ReaderOptions) except +reraise_kj_exception
|
||||
|
||||
void writePackedMessage(BufferedOutputStream&, MessageBuilder&) except +reraise_kj_exception nogil
|
||||
void writePackedMessage(OutputStream&, MessageBuilder&) except +reraise_kj_exception nogil
|
||||
void writePackedMessageToFd(int, MessageBuilder&) except +reraise_kj_exception nogil
|
||||
WordArray messageToFlatArray(MessageBuilder&) nogil
|
||||
|
||||
@@ -3,17 +3,13 @@
|
||||
from capnp.includes cimport capnp_cpp as capnp
|
||||
from capnp.includes cimport schema_cpp
|
||||
from capnp.includes.capnp_cpp cimport (
|
||||
Schema as C_Schema, StructSchema as C_StructSchema, InterfaceSchema as C_InterfaceSchema,
|
||||
Schema as C_Schema, StructSchema as C_StructSchema,
|
||||
EnumSchema as C_EnumSchema, ListSchema as C_ListSchema, DynamicStruct as C_DynamicStruct,
|
||||
DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList, SchemaLoader as C_SchemaLoader,
|
||||
DynamicValue as C_DynamicValue, Type as C_Type, DynamicList as C_DynamicList,
|
||||
SchemaParser as C_SchemaParser, ParsedSchema as C_ParsedSchema, VOID, ArrayPtr, StringPtr,
|
||||
String, StringTree, DynamicOrphan as C_DynamicOrphan, AnyPointer as C_DynamicObject,
|
||||
DynamicCapability as C_DynamicCapability, Request, Response, RemotePromise, Promise,
|
||||
CallContext, RpcSystem, makeRpcServer, makeRpcClient, Capability as C_Capability,
|
||||
TwoPartyVatNetwork as C_TwoPartyVatNetwork, Side, AsyncIoStream, Own,
|
||||
DynamicStruct_Builder, PyRefCounter, PyAsyncIoStream
|
||||
String, StringTree, DynamicStruct_Builder
|
||||
)
|
||||
from capnp.includes.schema_cpp cimport Node as C_Node, EnumNode as C_EnumNode
|
||||
from capnp.includes.schema_cpp cimport Node as C_Node
|
||||
from capnp.includes.types cimport *
|
||||
from capnp.helpers cimport helpers
|
||||
|
||||
@@ -30,9 +26,6 @@ cdef class _StringArrayPtr:
|
||||
cdef size_t size
|
||||
cdef ArrayPtr[StringPtr] asArrayPtr(self)
|
||||
|
||||
cdef class SchemaLoader:
|
||||
cdef C_SchemaLoader * thisptr
|
||||
|
||||
cdef class SchemaParser:
|
||||
cdef C_SchemaParser * thisptr
|
||||
cdef public dict modules_by_id
|
||||
@@ -40,16 +33,6 @@ cdef class SchemaParser:
|
||||
cdef _StringArrayPtr _last_import_array
|
||||
cpdef _parse_disk_file(self, displayName, diskPath, imports)
|
||||
|
||||
cdef class _DynamicOrphan:
|
||||
cdef C_DynamicOrphan thisptr
|
||||
cdef public object _parent
|
||||
|
||||
cdef _init(self, C_DynamicOrphan other, object parent)
|
||||
|
||||
cdef C_DynamicOrphan move(self)
|
||||
cpdef get(self)
|
||||
|
||||
|
||||
cdef class _DynamicStructReader:
|
||||
cdef C_DynamicStruct.Reader thisptr
|
||||
cdef public object _parent
|
||||
@@ -57,7 +40,7 @@ cdef class _DynamicStructReader:
|
||||
cdef object _obj_to_pin
|
||||
cdef object _schema
|
||||
|
||||
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?, bint tryRegistry=?)
|
||||
cdef _init(self, C_DynamicStruct.Reader other, object parent, bint isRoot=?)
|
||||
|
||||
cpdef _get(self, field)
|
||||
cpdef _has(self, field)
|
||||
@@ -65,9 +48,7 @@ cdef class _DynamicStructReader:
|
||||
cpdef _which_str(self)
|
||||
cpdef _get_by_field(self, _StructSchemaField field)
|
||||
cpdef _has_by_field(self, _StructSchemaField field)
|
||||
cpdef get_data_as_view(self, field)
|
||||
|
||||
cpdef as_builder(self, num_first_segment_words=?, allocate_seg_callable=?)
|
||||
cpdef as_builder(self, num_first_segment_words=?)
|
||||
|
||||
|
||||
cdef class _DynamicStructBuilder:
|
||||
@@ -77,15 +58,10 @@ cdef class _DynamicStructBuilder:
|
||||
cdef public bint _is_written
|
||||
cdef object _schema
|
||||
|
||||
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?, bint tryRegistry=?)
|
||||
cdef _init(self, DynamicStruct_Builder other, object parent, bint isRoot=?)
|
||||
|
||||
cdef _check_write(self)
|
||||
cpdef to_bytes(_DynamicStructBuilder self)
|
||||
cpdef to_segments(_DynamicStructBuilder self)
|
||||
cpdef to_segment_views(_DynamicStructBuilder self)
|
||||
cpdef _to_bytes_packed_helper(_DynamicStructBuilder self, word_count)
|
||||
cpdef to_bytes_packed(_DynamicStructBuilder self)
|
||||
|
||||
cpdef _get(self, field)
|
||||
cpdef _set(self, field, value)
|
||||
cpdef _has(self, field)
|
||||
@@ -94,15 +70,10 @@ cdef class _DynamicStructBuilder:
|
||||
cpdef _set_by_field(self, _StructSchemaField field, value)
|
||||
cpdef _has_by_field(self, _StructSchemaField field)
|
||||
cpdef _init_by_field(self, _StructSchemaField field, size=?)
|
||||
cpdef init_resizable_list(self, field)
|
||||
cpdef _DynamicEnumField _which(self)
|
||||
cpdef _which_str(self)
|
||||
cpdef adopt(self, field, _DynamicOrphan orphan)
|
||||
cpdef disown(self, field)
|
||||
cpdef get_data_as_view(self, field)
|
||||
|
||||
cpdef as_reader(self)
|
||||
cpdef copy(self, num_first_segment_words=?, allocate_seg_callable=?)
|
||||
cpdef copy(self, num_first_segment_words=?)
|
||||
|
||||
cdef class _DynamicEnumField:
|
||||
cdef object thisptr
|
||||
@@ -117,15 +88,9 @@ cdef class _Schema:
|
||||
|
||||
cpdef as_const_value(self)
|
||||
cpdef as_struct(self)
|
||||
cpdef as_interface(self)
|
||||
cpdef as_enum(self)
|
||||
cpdef get_proto(self)
|
||||
|
||||
cdef class _InterfaceSchema:
|
||||
cdef C_InterfaceSchema thisptr
|
||||
cdef object __method_names, __method_names_inherited, __methods, __methods_inherited
|
||||
cdef _init(self, C_InterfaceSchema other)
|
||||
|
||||
cdef class _DynamicEnum:
|
||||
cdef capnp.DynamicEnum thisptr
|
||||
cdef public object _parent
|
||||
@@ -141,31 +106,19 @@ cdef class _DynamicListBuilder:
|
||||
cpdef _get(self, int64_t index)
|
||||
cpdef _set(self, index, value)
|
||||
|
||||
cpdef adopt(self, index, _DynamicOrphan orphan)
|
||||
cpdef disown(self, index)
|
||||
|
||||
cpdef init(self, index, size)
|
||||
|
||||
cdef class _MessageBuilder:
|
||||
cdef schema_cpp.MessageBuilder * thisptr
|
||||
cpdef init_root(self, schema)
|
||||
cpdef get_root(self, schema)
|
||||
cpdef get_root_as_any(self)
|
||||
cpdef set_root(self, value)
|
||||
cpdef get_segments_for_output(self)
|
||||
cpdef new_orphan(self, schema)
|
||||
|
||||
cdef to_python_reader(C_DynamicValue.Reader self, object parent)
|
||||
cdef to_python_builder(C_DynamicValue.Builder self, object parent)
|
||||
cdef _to_dict(msg, bint verbose, bint ordered)
|
||||
cdef _to_dict(msg, bint verbose, bint ordered, bint encode_bytes_as_base64=?)
|
||||
cdef _from_list(_DynamicListBuilder msg, list d)
|
||||
cdef _from_tuple(_DynamicListBuilder msg, tuple d)
|
||||
cdef _setDynamicFieldWithField(DynamicStruct_Builder thisptr, _StructSchemaField field, value, parent)
|
||||
cdef _setDynamicFieldStatic(DynamicStruct_Builder thisptr, field, value, parent)
|
||||
|
||||
cdef api object wrap_dynamic_struct_reader(Response & r) with gil
|
||||
cdef api Promise[void] * call_server_method(
|
||||
object server, char * _method_name, CallContext & _context, object kj_loop) except * with gil
|
||||
cdef api object wrap_kj_exception(capnp.Exception & exception) with gil
|
||||
cdef api object wrap_kj_exception_for_reraise(capnp.Exception & exception) with gil
|
||||
cdef api object get_exception_info(object exc_type, object exc_obj, object exc_tb) with gil
|
||||
|
||||
2929
capnp/lib/capnp.pyx
2929
capnp/lib/capnp.pyx
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
import capnp
|
||||
|
||||
|
||||
def _struct_reducer(schema_id, data):
|
||||
'Hack to deal with pypy not allowing reduce functions to be "built-in" methods (ie. compiled from a .pyx)'
|
||||
with capnp._global_schema_parser.modules_by_id[schema_id].from_bytes(data) as msg:
|
||||
return msg
|
||||
@@ -1,249 +0,0 @@
|
||||
# addressbook_fast.pyx
|
||||
# distutils: language = c++
|
||||
# distutils: include_dirs = {{include_dir}}
|
||||
# distutils: libraries = capnpc capnp capnp-rpc
|
||||
# distutils: sources = {{file.filename}}.cpp
|
||||
# cython: c_string_type = str
|
||||
# cython: c_string_encoding = default
|
||||
# cython: embedsignature = True
|
||||
|
||||
{% macro getter(field, type) -%}
|
||||
{% if 'uint' in field['type'] -%}
|
||||
uint64_t get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% elif 'int' in field['type'] -%}
|
||||
int64_t get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% elif 'void' == field['type'] -%}
|
||||
void get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% elif 'bool' == field['type'] -%}
|
||||
cbool get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% elif 'text' == field['type'] -%}
|
||||
StringPtr get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% elif 'data' == field['type'] -%}
|
||||
Data.{{type}} get{{field.c_name}}() except +reraise_kj_exception
|
||||
{% else -%}
|
||||
DynamicValue.{{type}} get{{field.c_name}}() except +reraise_kj_exception
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
# TODO: add struct/enum/list types
|
||||
|
||||
{% macro getfield(field, type) -%}
|
||||
cpdef _get_{{field.name}}(self):
|
||||
{% if 'int' in field['type'] -%}
|
||||
return self.thisptr_child.get{{field.c_name}}()
|
||||
{% elif 'void' == field['type'] -%}
|
||||
self.thisptr_child.get{{field.c_name}}()
|
||||
return None
|
||||
{% elif 'bool' == field['type'] -%}
|
||||
return self.thisptr_child.get{{field.c_name}}()
|
||||
{% elif 'text' == field['type'] -%}
|
||||
temp = self.thisptr_child.get{{field.c_name}}()
|
||||
return (<char*>temp.begin())[:temp.size()]
|
||||
{% elif 'data' == field['type'] -%}
|
||||
temp = self.thisptr_child.get{{field.c_name}}()
|
||||
return <bytes>((<char*>temp.begin())[:temp.size()])
|
||||
{% else -%}
|
||||
cdef DynamicValue.{{type}} temp = self.thisptr_child.get{{field.c_name}}()
|
||||
return to_python_{{type | lower}}(temp, self._parent)
|
||||
{% endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro setter(field) -%}
|
||||
{% if 'int' in field['type'] -%}
|
||||
void set{{field.c_name}}({{field.type}}_t) except +reraise_kj_exception
|
||||
{% elif 'bool' == field['type'] -%}
|
||||
void set{{field.c_name}}(cbool) except +reraise_kj_exception
|
||||
{% elif 'text' == field['type'] -%}
|
||||
void set{{field.c_name}}(StringPtr) except +reraise_kj_exception
|
||||
{% elif 'data' == field['type'] -%}
|
||||
void set{{field.c_name}}(ArrayPtr[byte]) except +reraise_kj_exception
|
||||
{% else -%}
|
||||
void set{{field.c_name}}(DynamicValue.Reader) except +reraise_kj_exception
|
||||
{%- endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro setfield(field) -%}
|
||||
{% if 'int' in field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, {{field.type}}_t value):
|
||||
self.thisptr_child.set{{field.c_name}}(value)
|
||||
{% elif 'void' == field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, value=None):
|
||||
pass
|
||||
{% elif 'bool' == field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, cbool value):
|
||||
self.thisptr_child.set{{field.c_name}}(value)
|
||||
{% elif 'list' == field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, list value):
|
||||
cdef uint i = 0
|
||||
self.init("{{field.name}}", len(value))
|
||||
cdef _DynamicListBuilder temp = self._get_{{field.name}}()
|
||||
for elem in value:
|
||||
{% if 'struct' in field['sub_type'] -%}
|
||||
temp._get(i).from_dict(elem)
|
||||
{% else -%}
|
||||
temp[i] = elem
|
||||
{% endif -%}
|
||||
i += 1
|
||||
{% elif 'text' == field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, value):
|
||||
cdef StringPtr temp_string
|
||||
if type(value) is bytes:
|
||||
temp_string = StringPtr(<char*>value, len(value))
|
||||
else:
|
||||
encoded_value = value.encode('utf-8')
|
||||
temp_string = StringPtr(<char*>encoded_value, len(encoded_value))
|
||||
self.thisptr_child.set{{field.c_name}}(temp_string)
|
||||
{% elif 'data' == field['type'] -%}
|
||||
cpdef _set_{{field.name}}(self, value):
|
||||
cdef StringPtr temp_string
|
||||
if type(value) is bytes:
|
||||
temp_string = StringPtr(<char*>value, len(value))
|
||||
else:
|
||||
encoded_value = value.encode('utf-8')
|
||||
temp_string = StringPtr(<char*>encoded_value, len(encoded_value))
|
||||
self.thisptr_child.set{{field.c_name}}(ArrayPtr[byte](<byte *>temp_string.begin(), temp_string.size()))
|
||||
{% else -%}
|
||||
cpdef _set_{{field.name}}(self, value):
|
||||
_setDynamicFieldStatic(self.thisptr, "{{field.name}}", value, self._parent)
|
||||
{% endif -%}
|
||||
{%- endmacro %}
|
||||
|
||||
import capnp
|
||||
import {{file.filename | replace('.', '_')}}
|
||||
|
||||
from capnp.includes.types cimport *
|
||||
from capnp cimport helpers
|
||||
from capnp.includes.capnp_cpp cimport DynamicValue, Schema, VOID, StringPtr, ArrayPtr, Data
|
||||
from capnp.lib.capnp cimport _DynamicStructReader, _DynamicStructBuilder, _DynamicListBuilder, _DynamicEnum, _StructSchemaField, to_python_builder, to_python_reader, _to_dict, _setDynamicFieldStatic, _Schema, _InterfaceSchema
|
||||
|
||||
from capnp.helpers.non_circular cimport reraise_kj_exception
|
||||
|
||||
cdef DynamicValue.Reader _extract_dynamic_struct_builder(_DynamicStructBuilder value):
|
||||
return DynamicValue.Reader(value.thisptr.asReader())
|
||||
|
||||
cdef DynamicValue.Reader _extract_dynamic_struct_reader(_DynamicStructReader value):
|
||||
return DynamicValue.Reader(value.thisptr)
|
||||
|
||||
cdef DynamicValue.Reader _extract_dynamic_enum(_DynamicEnum value):
|
||||
return DynamicValue.Reader(value.thisptr)
|
||||
|
||||
cdef _from_list(_DynamicListBuilder msg, list d):
|
||||
cdef size_t count = 0
|
||||
for val in d:
|
||||
msg._set(count, val)
|
||||
count += 1
|
||||
|
||||
|
||||
cdef extern from "{{file.filename}}.h":
|
||||
{%- for node in code.nodes %}
|
||||
Schema get{{node.module_name}}Schema"capnp::Schema::from<{{node.c_module_path}}>"()
|
||||
|
||||
cdef cppclass {{node.module_name}}"{{node.c_module_path}}":
|
||||
cppclass Reader:
|
||||
{%- for field in node.struct.fields %}
|
||||
{{ getter(field, "Reader")|indent(12)}}
|
||||
{%- endfor %}
|
||||
cppclass Builder:
|
||||
{%- for field in node.struct.fields %}
|
||||
{{ getter(field, "Builder")|indent(12)}}
|
||||
{{ setter(field)|indent(12)}}
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
|
||||
cdef cppclass C_DynamicStruct_Reader" ::capnp::DynamicStruct::Reader":
|
||||
{%- for node in code.nodes %}
|
||||
{{node.module_name}}.Reader as{{node.module_name}}"as<{{node.c_module_path}}>"()
|
||||
{%- endfor %}
|
||||
|
||||
cdef cppclass C_DynamicStruct_Builder" ::capnp::DynamicStruct::Builder":
|
||||
{%- for node in code.nodes %}
|
||||
{{node.module_name}}.Builder as{{node.module_name}}"as<{{node.c_module_path}}>"()
|
||||
{%- endfor %}
|
||||
|
||||
{%- for node in code.nodes %}
|
||||
|
||||
{{node.schema}} = _Schema()._init(get{{node.module_name}}Schema()).as_struct()
|
||||
{{node.module_path}}.schema = {{node.schema}}
|
||||
|
||||
cdef class {{node.module_name}}_Reader(_DynamicStructReader):
|
||||
cdef {{node.module_name}}.Reader thisptr_child
|
||||
def __init__(self, _DynamicStructReader struct):
|
||||
self._init(struct.thisptr, struct._parent, struct.is_root, False)
|
||||
self.thisptr_child = (<C_DynamicStruct_Reader>struct.thisptr).as{{node.module_name}}()
|
||||
{% for field in node.struct.fields %}
|
||||
|
||||
{{ getfield(field, "Reader")|indent(4) }}
|
||||
|
||||
property {{field.name}}:
|
||||
def __get__(self):
|
||||
return self._get_{{field.name}}()
|
||||
{%- endfor %}
|
||||
|
||||
def to_dict(self, verbose=False, ordered=False):
|
||||
ret = {
|
||||
{% for field in node.struct.fields %}
|
||||
{% if field.discriminantValue == 65535 %}
|
||||
'{{field.name}}': _to_dict(self.{{field.name}}, verbose, ordered),
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
{% if node.is_union %}
|
||||
which = self._which_str()
|
||||
ret[which] = getattr(self, which)
|
||||
{% endif %}
|
||||
|
||||
return ret
|
||||
|
||||
cdef class {{node.module_name}}_Builder(_DynamicStructBuilder):
|
||||
cdef {{node.module_name}}.Builder thisptr_child
|
||||
def __init__(self, _DynamicStructBuilder struct):
|
||||
self._init(struct.thisptr, struct._parent, struct.is_root, False)
|
||||
self.thisptr_child = (<C_DynamicStruct_Builder>struct.thisptr).as{{node.module_name}}()
|
||||
{% for field in node.struct.fields %}
|
||||
{{ getfield(field, "Builder")|indent(4) }}
|
||||
{{ setfield(field)|indent(4) }}
|
||||
|
||||
property {{field.name}}:
|
||||
def __get__(self):
|
||||
return self._get_{{field.name}}()
|
||||
def __set__(self, value):
|
||||
self._set_{{field.name}}(value)
|
||||
{%- endfor %}
|
||||
|
||||
def to_dict(self, verbose=False, ordered=False):
|
||||
ret = {
|
||||
{% for field in node.struct.fields %}
|
||||
{% if field.discriminantValue == 65535 %}
|
||||
'{{field.name}}': _to_dict(self.{{field.name}}, verbose, ordered),
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
{% if node.is_union %}
|
||||
which = self._which_str()
|
||||
ret[which] = getattr(self, which)
|
||||
{% endif %}
|
||||
|
||||
return ret
|
||||
|
||||
def from_dict(self, dict d):
|
||||
cdef str key
|
||||
for key, val in d.iteritems():
|
||||
if False: pass
|
||||
{% for field in node.struct.fields %}
|
||||
elif key == "{{field.name}}":
|
||||
try:
|
||||
self._set_{{field.name}}(val)
|
||||
except Exception as e:
|
||||
if 'expected isSetInUnion(field)' in str(e):
|
||||
self.init(key)
|
||||
self._set_{{field.name}}(val)
|
||||
else:
|
||||
raise
|
||||
{%- endfor %}
|
||||
else:
|
||||
raise ValueError('Key not found in struct: ' + key)
|
||||
|
||||
|
||||
capnp.register_type({{node.id}}, ({{node.module_name}}_Reader, {{node.module_name}}_Builder))
|
||||
{% endfor %}
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from distutils.core import setup
|
||||
from Cython.Build import cythonize
|
||||
from shutil import copyfile
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
files = [{% for f in code.requestedFiles %}"{{f.filename}}", {% endfor %}]
|
||||
|
||||
for f in files:
|
||||
cpp_file = f + '.cpp'
|
||||
cplus_file = f + '.c++'
|
||||
cpp_mod = 0
|
||||
try:
|
||||
cpp_mod = os.path.getmtime(cpp_file)
|
||||
except:
|
||||
pass
|
||||
cplus_mod = 0
|
||||
try:
|
||||
cplus_mod = os.path.getmtime(cplus_file)
|
||||
except:
|
||||
pass
|
||||
if not os.path.exists(cpp_file) or cpp_mod < cplus_mod:
|
||||
if not os.path.exists(cplus_file):
|
||||
raise RuntimeError("You need to run `capnp compile -oc++` in addition to `-ocython` first.")
|
||||
copyfile(cplus_file, cpp_file)
|
||||
|
||||
with open(f + '.h', "r") as file:
|
||||
lines = file.readlines()
|
||||
with open(f + '.h', "w") as file:
|
||||
for line in lines:
|
||||
file.write(re.sub(r'Builder\(\)\s*=\s*delete;', 'Builder() = default;', line))
|
||||
|
||||
setup(
|
||||
name="{{code.requestedFiles[0] | replace('.', '_')}}",
|
||||
ext_modules=cythonize('*_capnp_cython.pyx', language="c++")
|
||||
)
|
||||
153
docs/Makefile
153
docs/Makefile
@@ -1,153 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/capnp.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/capnp.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/capnp"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/capnp"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
8
docs/_templates/versioning.html
vendored
8
docs/_templates/versioning.html
vendored
@@ -1,8 +0,0 @@
|
||||
{% if versions %}
|
||||
<h3>{{ _('Versions') }}</h3>
|
||||
<ul>
|
||||
{%- for item in versions %}
|
||||
<li><a href="{{ item.url }}">{{ item.name }}</a></li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
178
docs/capnp.rst
178
docs/capnp.rst
@@ -1,178 +0,0 @@
|
||||
.. _api:
|
||||
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. automodule:: capnp
|
||||
|
||||
.. currentmodule:: capnp
|
||||
|
||||
|
||||
Classes
|
||||
-------
|
||||
|
||||
RPC
|
||||
~~~
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._RemotePromise
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Communication
|
||||
#############
|
||||
|
||||
.. autoclass:: TwoPartyClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: TwoPartyServer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: AsyncIoStream
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._AsyncIoStream
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Capability
|
||||
##########
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._DynamicCapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Response
|
||||
########
|
||||
|
||||
.. autoclass:: capnp.lib.capnp._Response
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
.. autoclass:: KjException
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: SchemaParser
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: SchemaLoader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Functions
|
||||
---------
|
||||
.. autofunction:: add_import_hook
|
||||
.. autofunction:: remove_import_hook
|
||||
.. autofunction:: cleanup_global_schema_parser
|
||||
|
||||
.. autofunction:: kj_loop
|
||||
.. autofunction:: run
|
||||
|
||||
.. autofunction:: load
|
||||
|
||||
|
||||
|
||||
Internal Classes
|
||||
----------------
|
||||
These classes are internal to the library. You will never need to allocate
|
||||
one yourself, but you may end up using some of their member methods.
|
||||
|
||||
Modules
|
||||
~~~~~~~
|
||||
These are classes that are made for you when you import a Cap'n Proto file::
|
||||
|
||||
import capnp
|
||||
import addressbook_capnp
|
||||
|
||||
print type(addressbook_capnp.Person) # capnp.capnp._StructModule
|
||||
|
||||
.. autoclass:: _InterfaceModule
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _StructModule
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Readers
|
||||
~~~~~~~
|
||||
.. autoclass:: _DynamicListReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicStructReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _PackedFdMessageReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _StreamFdMessageReader
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Builders
|
||||
~~~~~~~~
|
||||
.. autoclass:: _DynamicResizableListBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicListBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicStructBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _MallocMessageBuilder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
RPC
|
||||
~~~
|
||||
.. autoclass:: _CapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: _DynamicCapabilityClient
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
.. autoclass:: _DynamicOrphan
|
||||
:members:
|
||||
:undoc-members:
|
||||
:inherited-members:
|
||||
313
docs/conf.py
313
docs/conf.py
@@ -1,313 +0,0 @@
|
||||
"""
|
||||
Docs configuration
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# capnp documentation build configuration file, created by
|
||||
# sphinx-quickstart on Sat Aug 17 18:00:25 2013.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import string
|
||||
|
||||
# import sys, os
|
||||
import capnp
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.viewcode",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx_multiversion",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = ".rst"
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = "capnp"
|
||||
copyright = "2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander)"
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
|
||||
vs = capnp.__version__
|
||||
# The short X.Y version.
|
||||
version = vs.rstrip(string.ascii_letters)
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = vs
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ["_build"]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "nature"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
"**": [
|
||||
"globaltoc.html",
|
||||
"relations.html",
|
||||
"sourcelink.html",
|
||||
"searchbox.html",
|
||||
"versioning.html",
|
||||
]
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "capnpdoc"
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
latex_elements = {}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
("index", "capnp.tex", "capnp Documentation", "Author", "manual"),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [("index", "capnp", "capnp Documentation", ["Author"], 1)]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(
|
||||
"index",
|
||||
"capnp",
|
||||
"capnp Documentation",
|
||||
"Author",
|
||||
"capnp",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
|
||||
# -- Options for Epub output ---------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = "capnp"
|
||||
epub_author = "Author"
|
||||
epub_publisher = "Author"
|
||||
epub_copyright = "2013, Author"
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
# epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
# epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
# epub_uid = ''
|
||||
|
||||
# A tuple containing the cover image and cover page html template filenames.
|
||||
# epub_cover = ()
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
# epub_exclude_files = []
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
# epub_tocdepth = 3
|
||||
|
||||
# Allow duplicate toc entries.
|
||||
# epub_tocdup = True
|
||||
|
||||
intersphinx_mapping = {"<name>": ("http://docs.python.org/", None)}
|
||||
|
||||
smv_branch_whitelist = r"^master$"
|
||||
@@ -1,19 +0,0 @@
|
||||
.. capnp documentation master file
|
||||
|
||||
pycapnp
|
||||
=======
|
||||
|
||||
This is a python wrapping of the C++ implementation of the `Cap'n Proto <https://capnproto.org/>`_ library. Here is a short description, quoted from its docs:
|
||||
|
||||
Cap’n Proto is an insanely fast data interchange format and capability-based RPC system. Think JSON, except binary. Or think Protocol Buffers, except faster. In fact, in benchmarks, Cap’n Proto is INFINITY TIMES faster than Protocol Buffers.
|
||||
|
||||
Since the python library is just a thin wrapping of the C++ library, we inherit a lot of what makes Cap'n Proto fast. In some simplistic benchmarks (available in the `benchmark directory of the repo <https://github.com/capnproto/pycapnp/tree/master/benchmark>`_), pycapnp has proven to be decently faster than Protocol Buffers (both pure python and C++ implementations). Also, the python capnp library can load Cap'n Proto schema files directly, without the need for a seperate compile step like with Protocol Buffers or Thrift. pycapnp is available on `github <https://github.com/capnproto/pycapnp.git>`_ and `pypi <https://pypi.python.org/pypi/pycapnp>`_.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
install
|
||||
quickstart
|
||||
capnp
|
||||
@@ -1,88 +0,0 @@
|
||||
.. _install:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Pip
|
||||
---
|
||||
The pip installation will using the binary versions of the package (if possible). These contain a bundled version of capnproto (on Linux compiled with `manylinux <https://github.com/pypa/manylinux>`_). Starting from v1.0.0b1 binary releases are available for Windows, macOS and Linux from `pypi <https://pypi.org/project/pycapnp/#history>`_::
|
||||
|
||||
[sudo] pip install pycapnp
|
||||
|
||||
To force rebuilding the pip package from source (you'll need requirments.txt or pipenv)::
|
||||
|
||||
pip install --no-binary :all: pycapnp
|
||||
|
||||
To force bundling libcapnp (or force system libcapnp), just in case pip isn't doing the right thing::
|
||||
|
||||
pip install --no-binary :all: -C force-bundled-libcapnp=True
|
||||
pip install --no-binary :all: -C force-system-libcapnp=True
|
||||
|
||||
If you're using an older Linux distro (e.g. CentOS 6) you many need to set `LDFLAGS="-Wl,--no-as-needed -lrt"`::
|
||||
|
||||
LDFLAGS="-Wl,--no-as-needed -lrt" pip install --no-binary :all: pycapnp
|
||||
|
||||
It's also possible to specify the libcapnp url when bundling (this may not work, there be dragons)::
|
||||
|
||||
pip install --no-binary :all: -C force-bundled-libcapnp=True -C libcapnp-url="https://github.com/capnproto/capnproto/archive/master.tar.gz"
|
||||
|
||||
From Source
|
||||
-----------
|
||||
Source installation is generally not needed unless you're looking into an issue with capnproto or pycapnp itself.
|
||||
|
||||
C++ Cap'n Proto Library
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
You need to install the C++ Cap'n Proto library first. It requires a C++ compiler with C++14 support, such as GCC 5+ or Clang 5+. Follow installation docs at `https://capnproto.org/install.html <https://capnproto.org/install.html>`_.
|
||||
|
||||
pycapnp from git
|
||||
~~~~~~~~~~~~~~~~
|
||||
If you want the latest development version, you can clone the github repo::
|
||||
|
||||
git clone https://github.com/capnproto/pycapnp.git
|
||||
|
||||
For development packages use one of the following to install the python dependencies::
|
||||
|
||||
cd pycapnp
|
||||
uv venv
|
||||
uv sync
|
||||
source .venv/bin/activate
|
||||
|
||||
And install pycapnp with::
|
||||
|
||||
uv pip install .
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
Clone the repo from https://github.com/capnproto/pycapnp.git::
|
||||
|
||||
git clone https://github.com/capnproto/pycapnp.git
|
||||
|
||||
For development packages use one of the following to install the python dependencies::
|
||||
|
||||
cd pycapnp
|
||||
uv venv
|
||||
uv sync
|
||||
source .venv/bin/activate
|
||||
|
||||
Building::
|
||||
|
||||
uv pip install .
|
||||
|
||||
Useful targets for setup.py::
|
||||
|
||||
python setup.py clean
|
||||
|
||||
Useful command-line arguments are available for pip install::
|
||||
|
||||
-C force-bundled-libcapnp=True
|
||||
-C force-system-libcapnp=True
|
||||
-C libcapnp-url="https://github.com/capnproto/capnproto/archive/master.tar.gz"
|
||||
|
||||
Testing is done through pytest::
|
||||
|
||||
cd pycapnp
|
||||
pytest
|
||||
pytest test/test_rpc_calculator.py
|
||||
|
||||
Once you're done installing, take a look at the :ref:`quickstart`
|
||||
@@ -1,597 +0,0 @@
|
||||
.. _quickstart:
|
||||
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
This assumes you already have the capnp library installed. If you don't, please follow the instructions at :ref:`Installation <install>` first.
|
||||
|
||||
In general, this library is a very light wrapping of the `Cap'n Proto C++ library <https://capnproto.org/cxx.html>`_. You can refer to its docs for more advanced concepts, or just to get a basic idea of how the python library is structured.
|
||||
|
||||
|
||||
Load a Cap'n Proto Schema
|
||||
-------------------------
|
||||
First you need to import the library::
|
||||
|
||||
import capnp
|
||||
|
||||
Then you can load the Cap'n Proto schema with::
|
||||
|
||||
import addressbook_capnp
|
||||
|
||||
This will look all through all the directories in your sys.path/PYTHONPATH, and try to find a file of the form 'addressbook.capnp'. If you want to disable the import hook magic that `import capnp` adds, and load manually, here's how::
|
||||
|
||||
capnp.remove_import_hook()
|
||||
addressbook_capnp = capnp.load('addressbook.capnp')
|
||||
|
||||
For future reference, here is the Cap'n Proto schema. Also available in the github repository under `examples/addressbook.capnp <https://github.com/capnproto/pycapnp/tree/master/examples>`_::
|
||||
|
||||
# addressbook.capnp
|
||||
@0x934efea7f017fff0;
|
||||
|
||||
const qux :UInt32 = 123;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Text;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
|
||||
Const values
|
||||
~~~~~~~~~~~~
|
||||
Const values show up just as you'd expect under the loaded schema. For example::
|
||||
|
||||
print(addressbook_capnp.qux)
|
||||
# 123
|
||||
|
||||
|
||||
Build a message
|
||||
---------------
|
||||
|
||||
Initialize a New Cap'n Proto Object
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that you've imported your schema, you need to allocate an actual struct from that schema. In this case, we will allocate an `AddressBook`::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
|
||||
Notice that we used `addressbook_capnp` from the previous section: `Load a Cap'n Proto Schema`_.
|
||||
|
||||
Also as a shortcut, you can pass keyword arguments to the `new_message` function, and those fields will be set in the new message::
|
||||
|
||||
person = addressbook_capnp.Person.new_message(name='alice')
|
||||
# is equivalent to:
|
||||
person = addressbook_capnp.Person.new_message()
|
||||
person.name = 'alice'
|
||||
|
||||
|
||||
List
|
||||
~~~~
|
||||
Allocating a list inside of an object requires use of the `init` function::
|
||||
|
||||
people = addresses.init('people', 2)
|
||||
|
||||
For now, let's grab the first element out of this list and assign it to a variable named `alice`::
|
||||
|
||||
alice = people[0]
|
||||
|
||||
.. note:: It is a very bad idea to call `init` more than once on a single field. Every call to `init` allocates new memory inside your Cap'n Proto message, and if you call it more than once, the previous memory is left as dead space in the message. See `Tips and Best Practices <https://capnproto.org/cxx.html#tips-and-best-practices>`_ for more details.
|
||||
|
||||
|
||||
Primitive Types
|
||||
~~~~~~~~~~~~~~~
|
||||
For all primitive types, from the Cap'n Proto docs:
|
||||
|
||||
- Boolean: Bool
|
||||
- Integers: Int8, Int16, Int32, Int64
|
||||
- Unsigned integers: UInt8, UInt16, UInt32, UInt64
|
||||
- Floating-point: Float32, Float64
|
||||
- Blobs: Text, Data
|
||||
|
||||
You can assign straight to the variable with the corresponding Python type. For Blobs, you use strings. Assignment happens just by using the `.` syntax on the object you contstructed above::
|
||||
|
||||
alice.id = 123
|
||||
alice.name = 'Alice'
|
||||
alice.email = 'alice@example.com'
|
||||
|
||||
.. note:: Text fields will behave differently depending on your version of Python. In Python 2.x, Text fields will expect and return a `bytes` string, while in Python 3.x, they will expect and return a `unicode` string. Data fields will always a return `bytes` string.
|
||||
|
||||
|
||||
Enums
|
||||
~~~~~
|
||||
First we'll allocate a length one list of phonenumbers for `alice`::
|
||||
|
||||
alicePhone = alice.init('phones', 1)[0]
|
||||
|
||||
Note that even though it was a length 1 list, it was still a list that was returned, and we extracted the first (and only) element with `[0]`.
|
||||
|
||||
Enums are treated like strings, and you assign to them like they were a Text field::
|
||||
|
||||
alicePhone.type = 'mobile'
|
||||
|
||||
If you assign an invalid value to one, you will get a ValueError::
|
||||
|
||||
alicePhone.type = 'foo'
|
||||
---------------------------------------------------------------------------
|
||||
KjException Traceback (most recent call last)
|
||||
...
|
||||
AttributeError: capnp/schema.c++:566: failed: enum has no such enumerant; name = foo
|
||||
|
||||
|
||||
Unions
|
||||
~~~~~~
|
||||
For the most part, you just treat them like structs::
|
||||
|
||||
alice.employment.school = "MIT"
|
||||
|
||||
Now the `school` field is the active part of the union, and we've assigned `'MIT'` to it. You can query which field is set in a union with `which()`, shown in `Reading Unions`_
|
||||
|
||||
Also, one weird case is for Void types in Unions (and in general, but Void is really only used in Unions). For these, you will have to assign `None` to them::
|
||||
|
||||
bob.employment.unemployed = None
|
||||
|
||||
.. note:: One caveat for unions is having structs as union members. Let us assume `employment.school` was actually a struct with a field of type `Text` called `name`
|
||||
|
||||
alice.employment.school.name = "MIT"
|
||||
# Raises a KjException
|
||||
|
||||
The problem is that a struct within a union isn't initialized automatically. You have to do the following::
|
||||
|
||||
school = alice.employment.init('school')
|
||||
school.name = "MIT"
|
||||
|
||||
Note that this is similar to `init` for lists, but you don't pass a size. Requiring the `init` makes it more clear that a memory allocation is occurring, and will hopefully make you mindful that you shouldn't set more than 1 field inside of a union, else you risk a memory leak
|
||||
|
||||
|
||||
Writing to a File
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Once you're done assigning to all the fields in a message, you can write it to a file like so::
|
||||
|
||||
with open('example.bin', 'wb') as f:
|
||||
addresses.write(f)
|
||||
|
||||
There is also a `write_packed` function, that writes out the message more space-efficientally. If you use write_packed, make sure to use read_packed when reading the message.
|
||||
|
||||
Writing to a socket
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Alternatively, you can write to a socket. This is useful if you want to send the message over the network or to another process.
|
||||
A full example of this is available on GitHub `examples/async_socket_message_client.py <https://github.com/capnproto/pycapnp/blob/master/examples/async_socket_message_client.py>`_.::
|
||||
|
||||
stream = await capnp.AsyncIoStream.create_connection(host="localhost", port=6000)
|
||||
await addresses.write_async(stream)
|
||||
|
||||
.. important:: Writing to a socket is implemented using asyncio and requires a running event loop both for the python part (asyncio) and the C++ part (KJ). See :ref:`RPC <kj-event-loop>` for more information.
|
||||
|
||||
Read a message
|
||||
--------------
|
||||
|
||||
Reading from a file
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
Much like before, you will have to de-serialize the message from a file descriptor::
|
||||
|
||||
with open('example.bin', 'rb') as f:
|
||||
addresses = addressbook_capnp.AddressBook.read(f)
|
||||
|
||||
Note that this very much needs to match the type you wrote out. In general, you will always be sending the same message types out over a given channel or you should wrap all your types in an unnamed union. Unnamed unions are defined in the .capnp file like so::
|
||||
|
||||
struct Message {
|
||||
union {
|
||||
person @0 :Person;
|
||||
addressbook @1 :AddressBook;
|
||||
}
|
||||
}
|
||||
|
||||
Reading from a socket
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The same as for writing, you can read from a socket. This is useful if you want to receive the message over the network or from another process.
|
||||
A full example of this is available on GitHub `examples/async_socket_message_client.py <https://github.com/capnproto/pycapnp/blob/master/examples/async_socket_message_client.py>`_.::
|
||||
|
||||
stream = await capnp.AsyncIoStream.create_connection(host="localhost", port=6000)
|
||||
message = await addressbook_capnp.AddressBook.read_async(stream)
|
||||
|
||||
.. important:: Reading from a socket is implemented using asyncio and requires a running event loop both for the python part (asyncio) and the C++ part (KJ). See :ref:`RPC <kj-event-loop>` for more information.
|
||||
|
||||
|
||||
Reading Fields
|
||||
~~~~~~~~~~~~~~
|
||||
Fields are very easy to read. You just use the `.` syntax as before. Lists behave just like normal Python lists::
|
||||
|
||||
for person in addresses.people:
|
||||
print(person.name, ':', person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ':', phone.number)
|
||||
|
||||
|
||||
Reading Unions
|
||||
~~~~~~~~~~~~~~
|
||||
The only tricky one is unions, where you need to call `.which()` to determine the union type. The `.which()` call returns an enum, ie. a string, corresponding to the field name::
|
||||
|
||||
which = person.employment.which()
|
||||
print(which)
|
||||
|
||||
if which == 'unemployed':
|
||||
print('unemployed')
|
||||
elif which == 'employer':
|
||||
print('employer:', person.employment.employer)
|
||||
elif which == 'school':
|
||||
print('student at:', person.employment.school)
|
||||
elif which == 'selfEmployed':
|
||||
print('self employed')
|
||||
print()
|
||||
|
||||
|
||||
Serializing/Deserializing
|
||||
-------------------------
|
||||
|
||||
Files
|
||||
~~~~~
|
||||
As shown in the examples above, there is file serialization with `write()`::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
...
|
||||
with open('example.bin', 'wb') as f:
|
||||
addresses.write(f)
|
||||
|
||||
And similarly for reading::
|
||||
|
||||
with open('example.bin', 'rb') as f:
|
||||
addresses = addressbook_capnp.AddressBook.read(f)
|
||||
|
||||
There are packed versions as well::
|
||||
|
||||
with open('example.bin', 'wb') as f:
|
||||
addresses.write_packed(f)
|
||||
...
|
||||
with open('example.bin', 'rb') as f:
|
||||
addresses = addressbook_capnp.AddressBook.read_packed(f)
|
||||
|
||||
|
||||
Multi-message files
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The above methods only guaranteed to work if your file contains a single message. If you have more than 1 message serialized sequentially in your file, then you need to use these convenience functions::
|
||||
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
...
|
||||
with open('example.bin', 'wb') as f:
|
||||
addresses.write(f)
|
||||
addresses.write(f)
|
||||
addresses.write(f) # write 3 messages
|
||||
|
||||
with open('example.bin', 'rb') as f:
|
||||
for addresses in addressbook_capnp.AddressBook.read_multiple(f):
|
||||
print(addresses)
|
||||
|
||||
There is also a packed version::
|
||||
|
||||
for addresses in addressbook_capnp.AddressBook.read_multiple_packed(f):
|
||||
print addresses
|
||||
|
||||
Dictionaries
|
||||
~~~~~~~~~~~~
|
||||
There is a convenience method for converting Cap'n Proto messages to a dictionary. This works for both Builder and Reader type messages::
|
||||
|
||||
alice.to_dict()
|
||||
|
||||
For the reverse, all you have to do is pass keyword arguments to the new_message constructor::
|
||||
|
||||
my_dict = {'name' : 'alice'}
|
||||
alice = addressbook_capnp.Person.new_message(**my_dict)
|
||||
# equivalent to: alice = addressbook_capnp.Person.new_message(name='alice')
|
||||
|
||||
It's also worth noting, you can use python lists/dictionaries interchangably with their Cap'n Proto equivalent types::
|
||||
|
||||
book = addressbook_capnp.AddressBook.new_message(people=[{'name': 'Alice'}])
|
||||
...
|
||||
book = addressbook_capnp.AddressBook.new_message()
|
||||
book.init('people', 1)
|
||||
book.people[0] = {'name': 'Bob'}
|
||||
|
||||
|
||||
Byte Strings/Buffers
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
There is serialization to a byte string available::
|
||||
|
||||
encoded_message = alice.to_bytes()
|
||||
|
||||
And a corresponding from_bytes function::
|
||||
|
||||
with addressbook_capnp.Person.from_bytes(encoded_message) as alice:
|
||||
# something with alice
|
||||
|
||||
There are also packed versions::
|
||||
|
||||
alice2 = addressbook_capnp.Person.from_bytes_packed(alice.to_bytes_packed())
|
||||
|
||||
|
||||
Byte Segments
|
||||
~~~~~~~~~~~~~
|
||||
.. note:: This feature is not supported in PyPy at the moment, pending investigation.
|
||||
|
||||
Cap'n Proto supports a serialization mode which minimizes object copies. In the C++ interface, ``capnp::MessageBuilder::getSegmentsForOutput()`` returns an array of pointers to segments of the message's content without copying. ``capnp::SegmentArrayMessageReader`` performs the reverse operation, i.e., takes an array of pointers to segments and uses the underlying data, again without copying. This produces a different wire serialization format from ``to_bytes()`` serialization, which uses ``capnp::messageToFlatArray()`` and ``capnp::FlatArrayMessageReader`` (both of which use segments internally, but write them in an incompatible way).
|
||||
|
||||
For compatibility on the Python side, use the ``to_segments()`` and ``from_segments()`` functions::
|
||||
|
||||
segments = alice.to_segments()
|
||||
|
||||
This returns a list of copied, Python-owned ``bytes`` objects. Each segment can be, e.g., turned into a ZeroMQ message frame. The list of segments can also be turned back into an object::
|
||||
|
||||
alice = addressbook_capnp.Person.from_segments(segments)
|
||||
|
||||
For high-throughput code that can safely consume borrowed buffers, ``to_segment_views()`` exposes the same output segments without copying them into Python ``bytes`` objects::
|
||||
|
||||
segment_views = alice.to_segment_views()
|
||||
for segment in segment_views:
|
||||
transport.send(segment)
|
||||
|
||||
Each segment view supports the Python buffer protocol and is read-only. The returned views borrow memory from the message builder's arena, so do not mutate, reset, or reuse the builder while any segment view is still in use. If you need data that remains independent of the builder lifetime, use ``to_segments()`` instead.
|
||||
|
||||
For more information, please refer to the following links:
|
||||
|
||||
- `Advice on minimizing copies from Cap'n Proto <https://stackoverflow.com/questions/28149139/serializing-mutable-state-and-sending-it-asynchronously-over-the-network-with-ne/28156323#28156323>`_ (from the author of Cap'n Proto)
|
||||
- `Advice on using Cap'n Proto over ZeroMQ <https://stackoverflow.com/questions/32041315/how-to-send-capn-proto-message-over-zmq/32042234#32042234>`_ (from the author of Cap'n Proto)
|
||||
- `Discussion about sending and reassembling Cap'n Proto message segments in C++ <https://groups.google.com/forum/#!topic/capnproto/ClDjGbO7egA>`_ (from the Cap'n Proto mailing list; includes sample code)
|
||||
|
||||
|
||||
RPC
|
||||
---
|
||||
|
||||
Cap'n Proto has a rich RPC protocol. You should read the `RPC specification <https://capnproto.org/rpc.html>`_ as well as the `C++ RPC documentation <http://kentonv.github.io/capnproto/cxxrpc.html>`_ before using pycapnp's RPC features. As with the serialization part of this library, the RPC component tries to be a very thin wrapper on top of the C++ API.
|
||||
|
||||
The examples below will be using `calculator.capnp <https://github.com/capnproto/pycapnp/blob/master/examples/calculator.capnp>`_. Please refer to it to understand the interfaces that will be used.
|
||||
|
||||
Asyncio support was added to pycapnp in v1.0.0. Since v2.0.0, the usage of asyncio is mandatory for all RPC calls. This guarantees a more robust and flexible RPC implementation.
|
||||
|
||||
KJ Event Loop
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. _kj-event-loop:
|
||||
|
||||
Cap'n Proto uses the KJ event loop for its RPC implementation. Pycapnp handles all the required mapping between the asyncio event loop and the KJ event loop.
|
||||
To ensure proper creation, usage, and cleanup of the KJ event loop, a context manager called :py:meth:`capnp.kj_loop` is exposed by pycapnp . All RPC calls must be made within this context::
|
||||
|
||||
import capnp
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
async with capnp.kj_loop():
|
||||
# RPC calls here
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
To simplify the usage, the helper function:py:meth:`capnp.run` can execute a asyncio coroutine within the :py:meth:`capnp.kj_loop` context manager::
|
||||
|
||||
import capnp
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
# RPC calls here
|
||||
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
Client
|
||||
~~~~~~
|
||||
|
||||
.. _rpc-asyncio-client:
|
||||
|
||||
Thanks to the integration into the asyncio library, most of the boiler plate code is handled by pycapnp directly. The only thing that needs to be done is to create a client object and bootstrap the server capability.
|
||||
|
||||
Starting a Client
|
||||
#################
|
||||
|
||||
The first step is to open a socket to the server. For now this needs to be done
|
||||
through :py:meth:`~._AsyncIoStream.create_connection`. A thin wrapper around :py:meth:`asyncio.get_running_loop().create_connection()`
|
||||
that adds all required Protocol handling::
|
||||
|
||||
async def main():
|
||||
host = 'localhost'
|
||||
port = '6000'
|
||||
connection = await capnp.AsyncIoStream.create_connection(host=host, port=port)
|
||||
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
.. note:: :py:meth:`~._AsyncIoStream.create_connection` forwards all calls to the underlying asyncio create_connection function.
|
||||
|
||||
In the next step, this created connection can be passed to :py:meth:`capnp.TwoPartyClient` to create the client object::
|
||||
|
||||
async def main():
|
||||
host = 'localhost'
|
||||
port = '6000'
|
||||
connection = await capnp.AsyncIoStream.create_connection(host=host, port=port)
|
||||
client = capnp.TwoPartyClient(connection)
|
||||
## Bootstrap Here ##
|
||||
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
|
||||
SSL/TLS Client
|
||||
^^^^^^^^^^^^^^
|
||||
SSL/TLS setup effectively wraps the socket transport. You'll need an SSL certificate, for this example, we'll use a self-signed certificate. Since we wrap around the asyncio connection interface, the SSL/TLS setup is done through the :py:obj:`ssl`` parameter of :py:meth:`~._AsyncIoStream.create_connection`::
|
||||
|
||||
async def main():
|
||||
host = 'localhost'
|
||||
port = '6000'
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=os.path.join(this_dir, "selfsigned.cert"))
|
||||
|
||||
connection = await capnp.AsyncIoStream.create_connection(host=host, port=port, ssl=ctx, family=socket.AF_INET)
|
||||
client = capnp.TwoPartyClient(connection)
|
||||
## Bootstrap Here ##
|
||||
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
|
||||
Due to a `bug <https://bugs.python.org/issue36709>`_ in Python 3.8 asyncio client needs to be initialized in a slightly different way::
|
||||
|
||||
if __name__ == '__main__':
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(capnp.run(main(parse_args().host)))
|
||||
|
||||
|
||||
Bootstrap
|
||||
#########
|
||||
Before calling any methods you'll need to bootstrap the Calculator interface::
|
||||
|
||||
calculator = client.bootstrap().cast_as(calculator_capnp.Calculator)
|
||||
|
||||
There's two things worth noting here. First, we are asking for the server capability. Secondly, you see that we are casting the capability that we receive. This is because capabilities are intrinsically dynamic, and they hold no run time type information, so we need to pick what interface to interpret them as.
|
||||
|
||||
|
||||
Calling methods
|
||||
###############
|
||||
There are 2 ways to call RPC methods. First the more verbose `request` syntax::
|
||||
|
||||
request = calculator.evaluate_request()
|
||||
request.expression.literal = 123
|
||||
eval_promise = request.send()
|
||||
|
||||
This creates a request for the method named 'evaluate', sets `expression.literal` in that call's parameters to 123, and then sends the request and returns a promise (all non-blocking).
|
||||
|
||||
The shorter syntax for calling methods is::
|
||||
|
||||
eval_promise = calculator.evaluate({"literal": 123})
|
||||
|
||||
The major shortcoming with this method is that expressing complex fields with many nested sub-structs can become very tedious.
|
||||
|
||||
The returned promise can be handled like any other asyncio promise::
|
||||
|
||||
result = await eval_promise()
|
||||
|
||||
|
||||
Pipelining
|
||||
##########
|
||||
If a method returns values that are themselves capabilites, then you can access these fields before awaiting the promise. Doing this is called pipelining, and it allows Cap'n Proto to chain the calls without a round-trip occurring to the server::
|
||||
|
||||
# evaluate returns `value` which is itself an interface.
|
||||
# You can call a new method on `value` without having to call wait first
|
||||
read_promise = eval_promise.value.read()
|
||||
read_result = await read_promise # only 1 await call
|
||||
|
||||
Server
|
||||
~~~~~~
|
||||
|
||||
Starting a Server
|
||||
###########################
|
||||
|
||||
Like the client, the server uses an asyncio server that can be created with :py:meth:`~._AsyncIoStream.create_server`.
|
||||
|
||||
.. note:: :py:meth:`~._AsyncIoStream.create_server`, similar to :py:meth:`~._AsyncIoStream.create_connection`, forwards all arguments to the underlying asyncio create_connection function (with the exception of the first argument).
|
||||
|
||||
The first argument to :py:meth:`~._AsyncIoStream.create_server` must be a callback
|
||||
used by the pycapnp protocol implementation. The :py:obj:`callback` parameter will be called
|
||||
whenever a new connection is made. It receives a py:obj:`AsyncIoStream` instance as its
|
||||
only argument. If the result of py:obj:`callback` is a coroutine, it will be scheduled as a
|
||||
task. At minimum, the callback should create a :py:class:`capnp.TwoPartyServer` for the
|
||||
passed stream. :py:class:`capnp.TwoPartyServer` also exposes a
|
||||
:py:meth:`~.TwoPartyServer.on_disconnect()` function, which can be used as a task to handle
|
||||
the lifetime properly::
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=CalculatorImpl()).on_disconnect()
|
||||
|
||||
async def main():
|
||||
host = 'localhost'
|
||||
port = '6000'
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
.. note:: On systems that have both IPv4 and IPv6 addresses, IPv6 is often resolved first and needs to be handled separately. If you're certain IPv6 won't be used, you can remove it (you should also avoid localhost, and stick to something like 127.0.0.1). If you're broadcasting in general, you'll probably want to use `0.0.0.0` (IPv4) or `::/0` (IPv6).
|
||||
|
||||
|
||||
SSL/TLS Server
|
||||
^^^^^^^^^^^^^^
|
||||
Adding SSL/TLS support for a pycapnp asyncio server is fairly straight-forward. Just create an SSL context before starting the asyncio server::
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=CalculatorImpl()).on_disconnect()
|
||||
|
||||
async def main():
|
||||
host = 'localhost'
|
||||
port = '6000'
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ctx.load_cert_chain(
|
||||
os.path.join(this_dir, "selfsigned.cert"),
|
||||
os.path.join(this_dir, "selfsigned.key"),
|
||||
)
|
||||
|
||||
server = await capnp.AsyncIoStream.create_server(
|
||||
new_connection, host, port, ssl=ctx, family=socket.AF_INET
|
||||
)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
|
||||
|
||||
Implementing a Server
|
||||
#####################
|
||||
Here's a part of how you would implement a Calculator server::
|
||||
|
||||
class CalculatorImpl(calculator_capnp.Calculator.Server):
|
||||
|
||||
"Implementation of the Calculator Cap'n Proto interface."
|
||||
|
||||
def evaluate(self, expression, _context, **kwargs):
|
||||
return evaluate_impl(expression).then(lambda value: setattr(_context.results, 'value', ValueImpl(value)))
|
||||
|
||||
def defFunction_context(self, context):
|
||||
params = context.params
|
||||
context.results.func = FunctionImpl(params.paramCount, params.body)
|
||||
|
||||
def getOperator(self, op, **kwargs):
|
||||
return OperatorImpl(op)
|
||||
|
||||
Some major things worth noting.
|
||||
|
||||
- You must inherit from `your_module_capnp.YourInterface.Server`, but don't worry about calling __super__ in your __init__
|
||||
- Method names of your class must either match the interface exactly, or have '_context' appended to it
|
||||
- If your method name is exactly the same as the interface, then you will be passed all the arguments from the interface as keyword arguments, so your argument names must match the interface spec exactly. You will also receive a `_context` parameter which is equivalent to the C++ API's Context. I highly recommend having `**kwargs` as well, so that even if your interface spec is upgraded and arguments were added, your server will still operate fine.
|
||||
- Returns work with a bit of magic as well. If you return a promise, then it will be handled the same as if you returned a promise from a server method in the C++ API. Otherwise, your return statement will be filled into the results struct following the ordering in your spec, for example::
|
||||
|
||||
# capability.capnp file
|
||||
interface TestInterface {
|
||||
foo @0 (i :UInt32, j :Bool) -> (x: Text, i:UInt32);
|
||||
}
|
||||
|
||||
# python code
|
||||
class TestInterface(capability_capnp.TestInterface.Server):
|
||||
def foo(self, i, j, **kwargs):
|
||||
return str(j), i
|
||||
|
||||
- If your method ends in _context, then you will only be passed a context parameter. You will have to access params and set results yourself manually. Returning promises still works as above, but you can't return anything else from a method.
|
||||
|
||||
|
||||
Full Examples
|
||||
-------------
|
||||
`Full examples <https://github.com/capnproto/pycapnp/blob/master/examples>`_ are available on github. There is also an example of a very simplistic RPC available in `test_rpc.py <https://github.com/capnproto/pycapnp/blob/master/test/test_rpc.py>`_.
|
||||
@@ -1,40 +0,0 @@
|
||||
@0x934efea7f017fff0;
|
||||
|
||||
const qux :UInt32 = 123;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Text;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
|
||||
testGroup :group {
|
||||
field1 @8 :UInt32;
|
||||
field2 @9 :UInt32;
|
||||
field3 @10 :UInt32;
|
||||
}
|
||||
extraData @11 :Data;
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import capnp # noqa: F401
|
||||
|
||||
import addressbook_capnp
|
||||
|
||||
|
||||
def writeAddressBook(file):
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
people = addresses.init("people", 2)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = 123
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = alice.init("phones", 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = "mobile"
|
||||
alice.employment.school = "MIT"
|
||||
|
||||
bob = people[1]
|
||||
bob.id = 456
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = bob.init("phones", 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = "home"
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = "work"
|
||||
bob.employment.unemployed = None
|
||||
|
||||
addresses.write(file)
|
||||
|
||||
|
||||
def printAddressBook(file):
|
||||
addresses = addressbook_capnp.AddressBook.read(file)
|
||||
|
||||
for person in addresses.people:
|
||||
print(person.name, ":", person.email)
|
||||
for phone in person.phones:
|
||||
print(phone.type, ":", phone.number)
|
||||
|
||||
which = person.employment.which()
|
||||
print(which)
|
||||
|
||||
if which == "unemployed":
|
||||
print("unemployed")
|
||||
elif which == "employer":
|
||||
print("employer:", person.employment.employer)
|
||||
elif which == "school":
|
||||
print("student at:", person.employment.school)
|
||||
elif which == "selfEmployed":
|
||||
print("self employed")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
f = open("example", "w")
|
||||
writeAddressBook(f)
|
||||
|
||||
f = open("example", "r")
|
||||
printAddressBook(f)
|
||||
@@ -1,306 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import capnp
|
||||
|
||||
import calculator_capnp
|
||||
|
||||
|
||||
class PowerFunction(calculator_capnp.Calculator.Function.Server):
|
||||
"""An implementation of the Function interface wrapping pow(). Note that
|
||||
we're implementing this on the client side and will pass a reference to
|
||||
the server. The server will then be able to make calls back to the client."""
|
||||
|
||||
async def call(self, params, **kwargs):
|
||||
"""Note the **kwargs. This is very necessary to include, since
|
||||
protocols can add parameters over time. Also, by default, a _context
|
||||
variable is passed to all server methods, but you can also return
|
||||
results directly as python objects, and they'll be added to the
|
||||
results struct in the correct order"""
|
||||
|
||||
return pow(params[0], params[1])
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="Connects to the Calculator server at the given address and does some RPCs")
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main(connection):
|
||||
client = capnp.TwoPartyClient(connection)
|
||||
|
||||
# Bootstrap the Calculator interface
|
||||
calculator = client.bootstrap().cast_as(calculator_capnp.Calculator)
|
||||
|
||||
"""Make a request that just evaluates the literal value 123.
|
||||
|
||||
What's interesting here is that evaluate() returns a "Value", which is
|
||||
another interface and therefore points back to an object living on the
|
||||
server. We then have to call read() on that object to read it.
|
||||
However, even though we are making two RPC's, this block executes in
|
||||
*one* network round trip because of promise pipelining: we do not wait
|
||||
for the first call to complete before we send the second call to the
|
||||
server."""
|
||||
|
||||
print("Evaluating a literal... ", end="")
|
||||
|
||||
# Make the request. Note we are using the shorter function form (instead
|
||||
# of evaluate_request), and we are passing a dictionary that represents a
|
||||
# struct and its member to evaluate
|
||||
eval_promise = calculator.evaluate({"literal": 123})
|
||||
|
||||
# This is equivalent to:
|
||||
"""
|
||||
request = calculator.evaluate_request()
|
||||
request.expression.literal = 123
|
||||
|
||||
# Send it, which returns a promise for the result (without blocking).
|
||||
eval_promise = request.send()
|
||||
"""
|
||||
|
||||
# Using the promise, create a pipelined request to call read() on the
|
||||
# returned object. Note that here we are using the shortened method call
|
||||
# syntax read(), which is mostly just sugar for read_request().send()
|
||||
read_promise = eval_promise.value.read()
|
||||
|
||||
# Now that we've sent all the requests, wait for the response. Until this
|
||||
# point, we haven't waited at all!
|
||||
response = await read_promise
|
||||
assert response.value == 123
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Make a request to evaluate 123 + 45 - 67.
|
||||
|
||||
The Calculator interface requires that we first call getOperator() to
|
||||
get the addition and subtraction functions, then call evaluate() to use
|
||||
them. But, once again, we can get both functions, call evaluate(), and
|
||||
then read() the result -- four RPCs -- in the time of *one* network
|
||||
round trip, because of promise pipelining."""
|
||||
|
||||
print("Using add and subtract... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "subtract" function from the server.
|
||||
subtract = calculator.getOperator(op="subtract").func
|
||||
|
||||
# Build the request to evaluate 123 + 45 - 67. Note the form is 'evaluate'
|
||||
# + '_request', where 'evaluate' is the name of the method we want to call
|
||||
request = calculator.evaluate_request()
|
||||
subtract_call = request.expression.init("call")
|
||||
subtract_call.function = subtract
|
||||
subtract_params = subtract_call.init("params", 2)
|
||||
subtract_params[1].literal = 67.0
|
||||
|
||||
add_call = subtract_params[0].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].literal = 123
|
||||
add_params[1].literal = 45
|
||||
|
||||
# Send the evaluate() request, read() the result, and wait for read() to finish.
|
||||
eval_promise = request.send()
|
||||
read_promise = eval_promise.value.read()
|
||||
|
||||
response = await read_promise
|
||||
assert response.value == 101
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""
|
||||
Note: a one liner version of building the previous request (I highly
|
||||
recommend not doing it this way for such a complicated structure, but I
|
||||
just wanted to demonstrate it is possible to set all of the fields with a
|
||||
dictionary):
|
||||
|
||||
eval_promise = calculator.evaluate(
|
||||
{'call': {'function': subtract,
|
||||
'params': [{'call': {'function': add,
|
||||
'params': [{'literal': 123},
|
||||
{'literal': 45}]}},
|
||||
{'literal': 67.0}]}})
|
||||
"""
|
||||
|
||||
"""Make a request to evaluate 4 * 6, then use the result in two more
|
||||
requests that add 3 and 5.
|
||||
|
||||
Since evaluate() returns its result wrapped in a `Value`, we can pass
|
||||
that `Value` back to the server in subsequent requests before the first
|
||||
`evaluate()` has actually returned. Thus, this example again does only
|
||||
one network round trip."""
|
||||
|
||||
print("Pipelining eval() calls... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "multiply" function from the server.
|
||||
multiply = calculator.getOperator(op="multiply").func
|
||||
|
||||
# Build the request to evaluate 4 * 6
|
||||
request = calculator.evaluate_request()
|
||||
|
||||
multiply_call = request.expression.init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[0].literal = 4
|
||||
multiply_params[1].literal = 6
|
||||
|
||||
multiply_result = request.send().value
|
||||
|
||||
# Use the result in two calls that add 3 and add 5.
|
||||
|
||||
add_3_request = calculator.evaluate_request()
|
||||
add_3_call = add_3_request.expression.init("call")
|
||||
add_3_call.function = add
|
||||
add_3_params = add_3_call.init("params", 2)
|
||||
add_3_params[0].previousResult = multiply_result
|
||||
add_3_params[1].literal = 3
|
||||
add_3_promise = add_3_request.send().value.read()
|
||||
|
||||
add_5_request = calculator.evaluate_request()
|
||||
add_5_call = add_5_request.expression.init("call")
|
||||
add_5_call.function = add
|
||||
add_5_params = add_5_call.init("params", 2)
|
||||
add_5_params[0].previousResult = multiply_result
|
||||
add_5_params[1].literal = 5
|
||||
add_5_promise = add_5_request.send().value.read()
|
||||
|
||||
# Now wait for the results.
|
||||
assert (await add_3_promise).value == 27
|
||||
assert (await add_5_promise).value == 29
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Our calculator interface supports defining functions. Here we use it
|
||||
to define two functions and then make calls to them as follows:
|
||||
|
||||
f(x, y) = x * 100 + y
|
||||
g(x) = f(x, x + 1) * 2;
|
||||
f(12, 34)
|
||||
g(21)
|
||||
|
||||
Once again, the whole thing takes only one network round trip."""
|
||||
|
||||
print("Defining functions... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "multiply" function from the server.
|
||||
multiply = calculator.getOperator(op="multiply").func
|
||||
|
||||
# Define f.
|
||||
request = calculator.defFunction_request()
|
||||
request.paramCount = 2
|
||||
|
||||
# Build the function body.
|
||||
add_call = request.body.init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[1].parameter = 1 # y
|
||||
|
||||
multiply_call = add_params[0].init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[0].parameter = 0 # x
|
||||
multiply_params[1].literal = 100
|
||||
|
||||
f = request.send().func
|
||||
|
||||
# Define g.
|
||||
request = calculator.defFunction_request()
|
||||
request.paramCount = 1
|
||||
|
||||
# Build the function body.
|
||||
multiply_call = request.body.init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[1].literal = 2
|
||||
|
||||
f_call = multiply_params[0].init("call")
|
||||
f_call.function = f
|
||||
f_params = f_call.init("params", 2)
|
||||
f_params[0].parameter = 0
|
||||
|
||||
add_call = f_params[1].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].parameter = 0
|
||||
add_params[1].literal = 1
|
||||
|
||||
g = request.send().func
|
||||
|
||||
# OK, we've defined all our functions. Now create our eval requests.
|
||||
|
||||
# f(12, 34)
|
||||
f_eval_request = calculator.evaluate_request()
|
||||
f_call = f_eval_request.expression.init("call")
|
||||
f_call.function = f
|
||||
f_params = f_call.init("params", 2)
|
||||
f_params[0].literal = 12
|
||||
f_params[1].literal = 34
|
||||
f_eval_promise = f_eval_request.send().value.read()
|
||||
|
||||
# g(21)
|
||||
g_eval_request = calculator.evaluate_request()
|
||||
g_call = g_eval_request.expression.init("call")
|
||||
g_call.function = g
|
||||
g_call.init("params", 1)[0].literal = 21
|
||||
g_eval_promise = g_eval_request.send().value.read()
|
||||
|
||||
# Wait for the results.
|
||||
assert (await f_eval_promise).value == 1234
|
||||
assert (await g_eval_promise).value == 4244
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Make a request that will call back to a function defined locally.
|
||||
|
||||
Specifically, we will compute 2^(4 + 5). However, exponent is not
|
||||
defined by the Calculator server. So, we'll implement the Function
|
||||
interface locally and pass it to the server for it to use when
|
||||
evaluating the expression.
|
||||
|
||||
This example requires two network round trips to complete, because the
|
||||
server calls back to the client once before finishing. In this
|
||||
particular case, this could potentially be optimized by using a tail
|
||||
call on the server side -- see CallContext::tailCall(). However, to
|
||||
keep the example simpler, we haven't implemented this optimization in
|
||||
the sample server."""
|
||||
|
||||
print("Using a callback... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
|
||||
# Build the eval request for 2^(4+5).
|
||||
request = calculator.evaluate_request()
|
||||
|
||||
pow_call = request.expression.init("call")
|
||||
pow_call.function = PowerFunction()
|
||||
pow_params = pow_call.init("params", 2)
|
||||
pow_params[0].literal = 2
|
||||
|
||||
add_call = pow_params[1].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].literal = 4
|
||||
add_params[1].literal = 5
|
||||
|
||||
# Send the request and wait.
|
||||
response = await request.send().value.read()
|
||||
assert response.value == 512
|
||||
|
||||
print("PASS")
|
||||
|
||||
|
||||
async def cmd_main(host):
|
||||
host, port = host.split(":")
|
||||
await main(await capnp.AsyncIoStream.create_connection(host=host, port=port))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(cmd_main(parse_args().host)))
|
||||
@@ -1,129 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import capnp
|
||||
import calculator_capnp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
async def evaluate_impl(expression, params=None):
|
||||
"""Implementation of CalculatorImpl::evaluate(), also shared by
|
||||
FunctionImpl::call(). In the latter case, `params` are the parameter
|
||||
values passed to the function; in the former case, `params` is just an
|
||||
empty list."""
|
||||
|
||||
which = expression.which()
|
||||
|
||||
if which == "literal":
|
||||
return expression.literal
|
||||
elif which == "previousResult":
|
||||
return (await expression.previousResult.read()).value
|
||||
elif which == "parameter":
|
||||
assert expression.parameter < len(params)
|
||||
return params[expression.parameter]
|
||||
elif which == "call":
|
||||
call = expression.call
|
||||
func = call.function
|
||||
|
||||
# Evaluate each parameter.
|
||||
paramPromises = [evaluate_impl(param, params) for param in call.params]
|
||||
vals = await asyncio.gather(*paramPromises)
|
||||
|
||||
# When the parameters are complete, call the function.
|
||||
result = await func.call(vals)
|
||||
return result.value
|
||||
else:
|
||||
raise ValueError("Unknown expression type: " + which)
|
||||
|
||||
|
||||
class ValueImpl(calculator_capnp.Calculator.Value.Server):
|
||||
"Simple implementation of the Calculator.Value Cap'n Proto interface."
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
async def read(self, **kwargs):
|
||||
return self.value
|
||||
|
||||
|
||||
class FunctionImpl(calculator_capnp.Calculator.Function.Server):
|
||||
"""Implementation of the Calculator.Function Cap'n Proto interface, where the
|
||||
function is defined by a Calculator.Expression."""
|
||||
|
||||
def __init__(self, paramCount, body):
|
||||
self.paramCount = paramCount
|
||||
self.body = body.as_builder()
|
||||
|
||||
async def call(self, params, _context, **kwargs):
|
||||
"""Note that we're returning a Promise object here, and bypassing the
|
||||
helper functionality that normally sets the results struct from the
|
||||
returned object. Instead, we set _context.results directly inside of
|
||||
another promise"""
|
||||
|
||||
assert len(params) == self.paramCount
|
||||
return await evaluate_impl(self.body, params)
|
||||
|
||||
|
||||
class OperatorImpl(calculator_capnp.Calculator.Function.Server):
|
||||
"""Implementation of the Calculator.Function Cap'n Proto interface, wrapping
|
||||
basic binary arithmetic operators."""
|
||||
|
||||
def __init__(self, op):
|
||||
self.op = op
|
||||
|
||||
async def call(self, params, **kwargs):
|
||||
assert len(params) == 2
|
||||
|
||||
op = self.op
|
||||
|
||||
if op == "add":
|
||||
return params[0] + params[1]
|
||||
elif op == "subtract":
|
||||
return params[0] - params[1]
|
||||
elif op == "multiply":
|
||||
return params[0] * params[1]
|
||||
elif op == "divide":
|
||||
return params[0] / params[1]
|
||||
else:
|
||||
raise ValueError("Unknown operator")
|
||||
|
||||
|
||||
class CalculatorImpl(calculator_capnp.Calculator.Server):
|
||||
"Implementation of the Calculator Cap'n Proto interface."
|
||||
|
||||
async def evaluate(self, expression, _context, **kwargs):
|
||||
return ValueImpl(await evaluate_impl(expression))
|
||||
|
||||
async def defFunction(self, paramCount, body, _context, **kwargs):
|
||||
return FunctionImpl(paramCount, body)
|
||||
|
||||
async def getOperator(self, op, **kwargs):
|
||||
return OperatorImpl(op)
|
||||
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=CalculatorImpl()).on_disconnect()
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="""Runs the server bound to the given address/port ADDRESS. """)
|
||||
|
||||
parser.add_argument("address", help="ADDRESS:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main():
|
||||
host, port = parse_args().address.split(":")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
import time
|
||||
import capnp
|
||||
|
||||
import thread_capnp
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
usage="Connects to the Example thread server at the given address and does some RPCs"
|
||||
)
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
class StatusSubscriber(thread_capnp.Example.StatusSubscriber.Server):
|
||||
"""An implementation of the StatusSubscriber interface"""
|
||||
|
||||
async def status(self, value, **kwargs):
|
||||
print("status: {}".format(time.time()))
|
||||
|
||||
|
||||
async def main(host):
|
||||
host, port = host.split(":")
|
||||
connection = await capnp.AsyncIoStream.create_connection(host=host, port=port)
|
||||
client = capnp.TwoPartyClient(connection)
|
||||
cap = client.bootstrap().cast_as(thread_capnp.Example)
|
||||
|
||||
# Start background task for subscriber
|
||||
task = asyncio.ensure_future(cap.subscribeStatus(StatusSubscriber()))
|
||||
|
||||
# Run blocking tasks
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
|
||||
task.cancel()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
asyncio.run(capnp.run(main(args.host)))
|
||||
|
||||
# Test that we can run multiple asyncio loops in sequence. This is particularly tricky, because
|
||||
# main contains a background task that we never cancel. The entire loop gets cleaned up anyways,
|
||||
# and we can start a new loop.
|
||||
asyncio.run(capnp.run(main(args.host)))
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
import os
|
||||
import time
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
import capnp
|
||||
|
||||
import thread_capnp
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
usage="Connects to the Example thread server at the given address and does some RPCs"
|
||||
)
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
class StatusSubscriber(thread_capnp.Example.StatusSubscriber.Server):
|
||||
"""An implementation of the StatusSubscriber interface"""
|
||||
|
||||
def status(self, value, **kwargs):
|
||||
print("status: {}".format(time.time()))
|
||||
|
||||
|
||||
async def watch_connection(cap):
|
||||
while True:
|
||||
try:
|
||||
await asyncio.wait_for(cap.alive(), timeout=5)
|
||||
await asyncio.sleep(1)
|
||||
except asyncio.TimeoutError:
|
||||
print("Watch timeout!")
|
||||
asyncio.get_running_loop().stop()
|
||||
return False
|
||||
|
||||
|
||||
async def main(host):
|
||||
addr, port = host.split(":")
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=os.path.join(this_dir, "selfsigned.cert"))
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET6)
|
||||
|
||||
client = capnp.TwoPartyClient(stream)
|
||||
cap = client.bootstrap().cast_as(thread_capnp.Example)
|
||||
|
||||
# Start watcher to restart socket connection if it is lost and subscriber background task
|
||||
background_tasks = asyncio.gather(
|
||||
cap.subscribeStatus(StatusSubscriber()),
|
||||
watch_connection(cap),
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
# Run blocking tasks
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
|
||||
background_tasks.cancel()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Using asyncio.run hits an asyncio ssl bug
|
||||
# https://bugs.python.org/issue36709
|
||||
# asyncio.run(main(parse_args().host), loop=loop, debug=True)
|
||||
retry = True
|
||||
while retry:
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
retry = not loop.run_until_complete(capnp.run(main(parse_args().host)))
|
||||
except RuntimeError:
|
||||
# If an IO is hung, the event loop will be stopped
|
||||
# and will throw RuntimeError exception
|
||||
continue
|
||||
if retry:
|
||||
time.sleep(1)
|
||||
print("Retrying...")
|
||||
|
||||
# How this works
|
||||
# - There are two retry mechanisms
|
||||
# 1. Connection retry
|
||||
# 2. alive RPC verification
|
||||
# - The connection retry just loops the connection (IPv4+IPv6 until there is a connection or Ctrl+C)
|
||||
# - The alive RPC verification attempts a very basic rpc call with a timeout
|
||||
# * If there is a timeout, stop the current event loop
|
||||
# * Use the RuntimeError exception to force a reconnect
|
||||
# * myreader and mywriter must also be wrapped in wait_for in order for the events to get triggered correctly
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import capnp
|
||||
import thread_capnp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
class ExampleImpl(thread_capnp.Example.Server):
|
||||
"Implementation of the Example threading Cap'n Proto interface."
|
||||
|
||||
async def subscribeStatus(self, subscriber, **kwargs):
|
||||
await asyncio.sleep(0.1)
|
||||
await subscriber.status(True)
|
||||
await self.subscribeStatus(subscriber)
|
||||
|
||||
async def longRunning(self, **kwargs):
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=ExampleImpl()).on_disconnect()
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="""Runs the server bound to the given address/port ADDRESS. """)
|
||||
|
||||
parser.add_argument("address", help="ADDRESS:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main():
|
||||
host, port = parse_args().address.split(":")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
import capnp
|
||||
|
||||
import addressbook_capnp
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
usage="Connects to the Example thread server at the given address and does some RPCs"
|
||||
)
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def writeAddressBook(stream, bob_id):
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
people = addresses.init("people", 1)
|
||||
|
||||
bob = people[0]
|
||||
bob.id = bob_id
|
||||
bob.name = "Bob"
|
||||
bob.email = "bob@example.com"
|
||||
bobPhones = bob.init("phones", 2)
|
||||
bobPhones[0].number = "555-4567"
|
||||
bobPhones[0].type = "home"
|
||||
bobPhones[1].number = "555-7654"
|
||||
bobPhones[1].type = "work"
|
||||
bob.employment.unemployed = None
|
||||
|
||||
await addresses.write_async(stream)
|
||||
|
||||
|
||||
async def main(host):
|
||||
host, port = host.split(":")
|
||||
stream = await capnp.AsyncIoStream.create_connection(host=host, port=port)
|
||||
|
||||
await writeAddressBook(stream, 0)
|
||||
|
||||
message = await addressbook_capnp.AddressBook.read_async(stream)
|
||||
print(message)
|
||||
assert message.people[0].name == "Alice"
|
||||
assert message.people[0].id == 0
|
||||
|
||||
await writeAddressBook(stream, 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
asyncio.run(capnp.run(main(args.host)))
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
|
||||
import capnp
|
||||
import addressbook_capnp
|
||||
|
||||
|
||||
async def writeAddressBook(stream, alice_id):
|
||||
addresses = addressbook_capnp.AddressBook.new_message()
|
||||
people = addresses.init("people", 1)
|
||||
|
||||
alice = people[0]
|
||||
alice.id = alice_id
|
||||
alice.name = "Alice"
|
||||
alice.email = "alice@example.com"
|
||||
alicePhones = alice.init("phones", 1)
|
||||
alicePhones[0].number = "555-1212"
|
||||
alicePhones[0].type = "mobile"
|
||||
alice.employment.school = "MIT"
|
||||
|
||||
await addresses.write_async(stream)
|
||||
|
||||
|
||||
async def new_connection(stream):
|
||||
message = await addressbook_capnp.AddressBook.read_async(stream)
|
||||
print(message)
|
||||
assert message.people[0].name == "Bob"
|
||||
assert message.people[0].id == 0
|
||||
|
||||
await writeAddressBook(stream, 0)
|
||||
|
||||
message = await addressbook_capnp.AddressBook.read_async(stream)
|
||||
print(message)
|
||||
assert message.people[0].name == "Bob"
|
||||
assert message.people[0].id == 1
|
||||
|
||||
message = await addressbook_capnp.AddressBook.read_async(stream)
|
||||
print(message)
|
||||
assert message is None
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="""Runs the server bound to the given address/port ADDRESS. """)
|
||||
|
||||
parser.add_argument("address", help="ADDRESS:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main():
|
||||
host, port = parse_args().address.split(":")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port)
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
@@ -1,323 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import os
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
import capnp
|
||||
import calculator_capnp
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class PowerFunction(calculator_capnp.Calculator.Function.Server):
|
||||
"""An implementation of the Function interface wrapping pow(). Note that
|
||||
we're implementing this on the client side and will pass a reference to
|
||||
the server. The server will then be able to make calls back to the client."""
|
||||
|
||||
async def call(self, params, **kwargs):
|
||||
"""Note the **kwargs. This is very necessary to include, since
|
||||
protocols can add parameters over time. Also, by default, a _context
|
||||
variable is passed to all server methods, but you can also return
|
||||
results directly as python objects, and they'll be added to the
|
||||
results struct in the correct order"""
|
||||
|
||||
return pow(params[0], params[1])
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="Connects to the Calculator server at the given address and does some RPCs")
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main(host):
|
||||
addr, port = host.split(":")
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=os.path.join(this_dir, "selfsigned.cert"))
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET6)
|
||||
|
||||
client = capnp.TwoPartyClient(stream)
|
||||
|
||||
# Bootstrap the Calculator interface
|
||||
calculator = client.bootstrap().cast_as(calculator_capnp.Calculator)
|
||||
|
||||
"""Make a request that just evaluates the literal value 123.
|
||||
|
||||
What's interesting here is that evaluate() returns a "Value", which is
|
||||
another interface and therefore points back to an object living on the
|
||||
server. We then have to call read() on that object to read it.
|
||||
However, even though we are making two RPC's, this block executes in
|
||||
*one* network round trip because of promise pipelining: we do not wait
|
||||
for the first call to complete before we send the second call to the
|
||||
server."""
|
||||
|
||||
print("Evaluating a literal... ", end="")
|
||||
|
||||
# Make the request. Note we are using the shorter function form (instead
|
||||
# of evaluate_request), and we are passing a dictionary that represents a
|
||||
# struct and its member to evaluate
|
||||
eval_promise = calculator.evaluate({"literal": 123})
|
||||
|
||||
# This is equivalent to:
|
||||
"""
|
||||
request = calculator.evaluate_request()
|
||||
request.expression.literal = 123
|
||||
|
||||
# Send it, which returns a promise for the result (without blocking).
|
||||
eval_promise = request.send()
|
||||
"""
|
||||
|
||||
# Using the promise, create a pipelined request to call read() on the
|
||||
# returned object. Note that here we are using the shortened method call
|
||||
# syntax read(), which is mostly just sugar for read_request().send()
|
||||
read_promise = eval_promise.value.read()
|
||||
|
||||
# Now that we've sent all the requests, wait for the response. Until this
|
||||
# point, we haven't waited at all!
|
||||
response = await read_promise
|
||||
assert response.value == 123
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Make a request to evaluate 123 + 45 - 67.
|
||||
|
||||
The Calculator interface requires that we first call getOperator() to
|
||||
get the addition and subtraction functions, then call evaluate() to use
|
||||
them. But, once again, we can get both functions, call evaluate(), and
|
||||
then read() the result -- four RPCs -- in the time of *one* network
|
||||
round trip, because of promise pipelining."""
|
||||
|
||||
print("Using add and subtract... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "subtract" function from the server.
|
||||
subtract = calculator.getOperator(op="subtract").func
|
||||
|
||||
# Build the request to evaluate 123 + 45 - 67. Note the form is 'evaluate'
|
||||
# + '_request', where 'evaluate' is the name of the method we want to call
|
||||
request = calculator.evaluate_request()
|
||||
subtract_call = request.expression.init("call")
|
||||
subtract_call.function = subtract
|
||||
subtract_params = subtract_call.init("params", 2)
|
||||
subtract_params[1].literal = 67.0
|
||||
|
||||
add_call = subtract_params[0].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].literal = 123
|
||||
add_params[1].literal = 45
|
||||
|
||||
# Send the evaluate() request, read() the result, and wait for read() to finish.
|
||||
eval_promise = request.send()
|
||||
read_promise = eval_promise.value.read()
|
||||
|
||||
response = await read_promise
|
||||
assert response.value == 101
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""
|
||||
Note: a one liner version of building the previous request (I highly
|
||||
recommend not doing it this way for such a complicated structure, but I
|
||||
just wanted to demonstrate it is possible to set all of the fields with a
|
||||
dictionary):
|
||||
|
||||
eval_promise = calculator.evaluate(
|
||||
{'call': {'function': subtract,
|
||||
'params': [{'call': {'function': add,
|
||||
'params': [{'literal': 123},
|
||||
{'literal': 45}]}},
|
||||
{'literal': 67.0}]}})
|
||||
"""
|
||||
|
||||
"""Make a request to evaluate 4 * 6, then use the result in two more
|
||||
requests that add 3 and 5.
|
||||
|
||||
Since evaluate() returns its result wrapped in a `Value`, we can pass
|
||||
that `Value` back to the server in subsequent requests before the first
|
||||
`evaluate()` has actually returned. Thus, this example again does only
|
||||
one network round trip."""
|
||||
|
||||
print("Pipelining eval() calls... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "multiply" function from the server.
|
||||
multiply = calculator.getOperator(op="multiply").func
|
||||
|
||||
# Build the request to evaluate 4 * 6
|
||||
request = calculator.evaluate_request()
|
||||
|
||||
multiply_call = request.expression.init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[0].literal = 4
|
||||
multiply_params[1].literal = 6
|
||||
|
||||
multiply_result = request.send().value
|
||||
|
||||
# Use the result in two calls that add 3 and add 5.
|
||||
|
||||
add_3_request = calculator.evaluate_request()
|
||||
add_3_call = add_3_request.expression.init("call")
|
||||
add_3_call.function = add
|
||||
add_3_params = add_3_call.init("params", 2)
|
||||
add_3_params[0].previousResult = multiply_result
|
||||
add_3_params[1].literal = 3
|
||||
add_3_promise = add_3_request.send().value.read()
|
||||
|
||||
add_5_request = calculator.evaluate_request()
|
||||
add_5_call = add_5_request.expression.init("call")
|
||||
add_5_call.function = add
|
||||
add_5_params = add_5_call.init("params", 2)
|
||||
add_5_params[0].previousResult = multiply_result
|
||||
add_5_params[1].literal = 5
|
||||
add_5_promise = add_5_request.send().value.read()
|
||||
|
||||
# Now wait for the results.
|
||||
assert (await add_3_promise).value == 27
|
||||
assert (await add_5_promise).value == 29
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Our calculator interface supports defining functions. Here we use it
|
||||
to define two functions and then make calls to them as follows:
|
||||
|
||||
f(x, y) = x * 100 + y
|
||||
g(x) = f(x, x + 1) * 2;
|
||||
f(12, 34)
|
||||
g(21)
|
||||
|
||||
Once again, the whole thing takes only one network round trip."""
|
||||
|
||||
print("Defining functions... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
# Get the "multiply" function from the server.
|
||||
multiply = calculator.getOperator(op="multiply").func
|
||||
|
||||
# Define f.
|
||||
request = calculator.defFunction_request()
|
||||
request.paramCount = 2
|
||||
|
||||
# Build the function body.
|
||||
add_call = request.body.init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[1].parameter = 1 # y
|
||||
|
||||
multiply_call = add_params[0].init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[0].parameter = 0 # x
|
||||
multiply_params[1].literal = 100
|
||||
|
||||
f = request.send().func
|
||||
|
||||
# Define g.
|
||||
request = calculator.defFunction_request()
|
||||
request.paramCount = 1
|
||||
|
||||
# Build the function body.
|
||||
multiply_call = request.body.init("call")
|
||||
multiply_call.function = multiply
|
||||
multiply_params = multiply_call.init("params", 2)
|
||||
multiply_params[1].literal = 2
|
||||
|
||||
f_call = multiply_params[0].init("call")
|
||||
f_call.function = f
|
||||
f_params = f_call.init("params", 2)
|
||||
f_params[0].parameter = 0
|
||||
|
||||
add_call = f_params[1].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].parameter = 0
|
||||
add_params[1].literal = 1
|
||||
|
||||
g = request.send().func
|
||||
|
||||
# OK, we've defined all our functions. Now create our eval requests.
|
||||
|
||||
# f(12, 34)
|
||||
f_eval_request = calculator.evaluate_request()
|
||||
f_call = f_eval_request.expression.init("call")
|
||||
f_call.function = f
|
||||
f_params = f_call.init("params", 2)
|
||||
f_params[0].literal = 12
|
||||
f_params[1].literal = 34
|
||||
f_eval_promise = f_eval_request.send().value.read()
|
||||
|
||||
# g(21)
|
||||
g_eval_request = calculator.evaluate_request()
|
||||
g_call = g_eval_request.expression.init("call")
|
||||
g_call.function = g
|
||||
g_call.init("params", 1)[0].literal = 21
|
||||
g_eval_promise = g_eval_request.send().value.read()
|
||||
|
||||
# Wait for the results.
|
||||
assert (await f_eval_promise).value == 1234
|
||||
assert (await g_eval_promise).value == 4244
|
||||
|
||||
print("PASS")
|
||||
|
||||
"""Make a request that will call back to a function defined locally.
|
||||
|
||||
Specifically, we will compute 2^(4 + 5). However, exponent is not
|
||||
defined by the Calculator server. So, we'll implement the Function
|
||||
interface locally and pass it to the server for it to use when
|
||||
evaluating the expression.
|
||||
|
||||
This example requires two network round trips to complete, because the
|
||||
server calls back to the client once before finishing. In this
|
||||
particular case, this could potentially be optimized by using a tail
|
||||
call on the server side -- see CallContext::tailCall(). However, to
|
||||
keep the example simpler, we haven't implemented this optimization in
|
||||
the sample server."""
|
||||
|
||||
print("Using a callback... ", end="")
|
||||
|
||||
# Get the "add" function from the server.
|
||||
add = calculator.getOperator(op="add").func
|
||||
|
||||
# Build the eval request for 2^(4+5).
|
||||
request = calculator.evaluate_request()
|
||||
|
||||
pow_call = request.expression.init("call")
|
||||
pow_call.function = PowerFunction()
|
||||
pow_params = pow_call.init("params", 2)
|
||||
pow_params[0].literal = 2
|
||||
|
||||
add_call = pow_params[1].init("call")
|
||||
add_call.function = add
|
||||
add_params = add_call.init("params", 2)
|
||||
add_params[0].literal = 4
|
||||
add_params[1].literal = 5
|
||||
|
||||
# Send the request and wait.
|
||||
response = await request.send().value.read()
|
||||
assert response.value == 512
|
||||
|
||||
print("PASS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Using asyncio.run hits an asyncio ssl bug
|
||||
# https://bugs.python.org/issue36709
|
||||
# asyncio.run(main(parse_args().host), loop=loop, debug=True)
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(capnp.run(main(parse_args().host)))
|
||||
@@ -1,149 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
import capnp
|
||||
import calculator_capnp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
async def evaluate_impl(expression, params=None):
|
||||
"""Implementation of CalculatorImpl::evaluate(), also shared by
|
||||
FunctionImpl::call(). In the latter case, `params` are the parameter
|
||||
values passed to the function; in the former case, `params` is just an
|
||||
empty list."""
|
||||
|
||||
which = expression.which()
|
||||
|
||||
if which == "literal":
|
||||
return expression.literal
|
||||
elif which == "previousResult":
|
||||
return (await expression.previousResult.read()).value
|
||||
elif which == "parameter":
|
||||
assert expression.parameter < len(params)
|
||||
return params[expression.parameter]
|
||||
elif which == "call":
|
||||
call = expression.call
|
||||
func = call.function
|
||||
|
||||
# Evaluate each parameter.
|
||||
paramPromises = [evaluate_impl(param, params) for param in call.params]
|
||||
vals = await asyncio.gather(*paramPromises)
|
||||
|
||||
# When the parameters are complete, call the function.
|
||||
result = await func.call(vals)
|
||||
return result.value
|
||||
else:
|
||||
raise ValueError("Unknown expression type: " + which)
|
||||
|
||||
|
||||
class ValueImpl(calculator_capnp.Calculator.Value.Server):
|
||||
"Simple implementation of the Calculator.Value Cap'n Proto interface."
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
async def read(self, **kwargs):
|
||||
return self.value
|
||||
|
||||
|
||||
class FunctionImpl(calculator_capnp.Calculator.Function.Server):
|
||||
"""Implementation of the Calculator.Function Cap'n Proto interface, where the
|
||||
function is defined by a Calculator.Expression."""
|
||||
|
||||
def __init__(self, paramCount, body):
|
||||
self.paramCount = paramCount
|
||||
self.body = body.as_builder()
|
||||
|
||||
async def call(self, params, _context, **kwargs):
|
||||
"""Note that we're returning a Promise object here, and bypassing the
|
||||
helper functionality that normally sets the results struct from the
|
||||
returned object. Instead, we set _context.results directly inside of
|
||||
another promise"""
|
||||
|
||||
assert len(params) == self.paramCount
|
||||
return await evaluate_impl(self.body, params)
|
||||
|
||||
|
||||
class OperatorImpl(calculator_capnp.Calculator.Function.Server):
|
||||
"""Implementation of the Calculator.Function Cap'n Proto interface, wrapping
|
||||
basic binary arithmetic operators."""
|
||||
|
||||
def __init__(self, op):
|
||||
self.op = op
|
||||
|
||||
async def call(self, params, **kwargs):
|
||||
assert len(params) == 2
|
||||
|
||||
op = self.op
|
||||
|
||||
if op == "add":
|
||||
return params[0] + params[1]
|
||||
elif op == "subtract":
|
||||
return params[0] - params[1]
|
||||
elif op == "multiply":
|
||||
return params[0] * params[1]
|
||||
elif op == "divide":
|
||||
return params[0] / params[1]
|
||||
else:
|
||||
raise ValueError("Unknown operator")
|
||||
|
||||
|
||||
class CalculatorImpl(calculator_capnp.Calculator.Server):
|
||||
"Implementation of the Calculator Cap'n Proto interface."
|
||||
|
||||
async def evaluate(self, expression, _context, **kwargs):
|
||||
return ValueImpl(await evaluate_impl(expression))
|
||||
|
||||
async def defFunction(self, paramCount, body, _context, **kwargs):
|
||||
return FunctionImpl(paramCount, body)
|
||||
|
||||
async def getOperator(self, op, **kwargs):
|
||||
return OperatorImpl(op)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="""Runs the server bound to the given address/port ADDRESS. """)
|
||||
|
||||
parser.add_argument("address", help="ADDRESS:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=CalculatorImpl()).on_disconnect()
|
||||
|
||||
|
||||
async def main():
|
||||
host, port = parse_args().address.split(":")
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ctx.load_cert_chain(
|
||||
os.path.join(this_dir, "selfsigned.cert"),
|
||||
os.path.join(this_dir, "selfsigned.key"),
|
||||
)
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port, ssl=ctx, family=socket.AF_INET)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port, ssl=ctx, family=socket.AF_INET6)
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import os
|
||||
import ssl
|
||||
import time
|
||||
import socket
|
||||
|
||||
import capnp
|
||||
import thread_capnp
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
usage="Connects to the Example thread server at the given address and does some RPCs"
|
||||
)
|
||||
parser.add_argument("host", help="HOST:PORT")
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
class StatusSubscriber(thread_capnp.Example.StatusSubscriber.Server):
|
||||
"""An implementation of the StatusSubscriber interface"""
|
||||
|
||||
async def status(self, value, **kwargs):
|
||||
print("status: {}".format(time.time()))
|
||||
|
||||
|
||||
async def main(host):
|
||||
addr, port = host.split(":")
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=os.path.join(this_dir, "selfsigned.cert"))
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
stream = await capnp.AsyncIoStream.create_connection(addr, port, ssl=ctx, family=socket.AF_INET6)
|
||||
|
||||
client = capnp.TwoPartyClient(stream)
|
||||
cap = client.bootstrap().cast_as(thread_capnp.Example)
|
||||
|
||||
# Start background task for subscriber
|
||||
task = asyncio.ensure_future(cap.subscribeStatus(StatusSubscriber()))
|
||||
|
||||
# Run blocking tasks
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
await cap.longRunning()
|
||||
print("main: {}".format(time.time()))
|
||||
|
||||
task.cancel()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Using asyncio.run hits an asyncio ssl bug
|
||||
# https://bugs.python.org/issue36709
|
||||
# asyncio.run(main(parse_args().host), loop=loop, debug=True)
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(capnp.run(main(parse_args().host)))
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import ssl
|
||||
import socket
|
||||
|
||||
import capnp
|
||||
import thread_capnp
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class ExampleImpl(thread_capnp.Example.Server):
|
||||
"Implementation of the Example threading Cap'n Proto interface."
|
||||
|
||||
async def subscribeStatus(self, subscriber, **kwargs):
|
||||
await asyncio.sleep(0.1)
|
||||
await subscriber.status(True)
|
||||
await self.subscribeStatus(subscriber)
|
||||
|
||||
async def longRunning(self, **kwargs):
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
async def alive(self, **kwargs):
|
||||
return True
|
||||
|
||||
|
||||
async def new_connection(stream):
|
||||
await capnp.TwoPartyServer(stream, bootstrap=ExampleImpl()).on_disconnect()
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(usage="""Runs the server bound to the given address/port ADDRESS. """)
|
||||
parser.add_argument("address", help="ADDRESS:PORT")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
async def main():
|
||||
host, port = parse_args().address.split(":")
|
||||
|
||||
# Setup SSL context
|
||||
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ctx.load_cert_chain(
|
||||
os.path.join(this_dir, "selfsigned.cert"),
|
||||
os.path.join(this_dir, "selfsigned.key"),
|
||||
)
|
||||
|
||||
# Handle both IPv4 and IPv6 cases
|
||||
try:
|
||||
print("Try IPv4")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port, ssl=ctx, family=socket.AF_INET)
|
||||
except Exception:
|
||||
print("Try IPv6")
|
||||
server = await capnp.AsyncIoStream.create_server(new_connection, host, port, ssl=ctx, family=socket.AF_INET6)
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capnp.run(main()))
|
||||
@@ -1,97 +0,0 @@
|
||||
@0x85150b117366d14b;
|
||||
|
||||
interface Calculator {
|
||||
# A "simple" mathematical calculator, callable via RPC.
|
||||
#
|
||||
# But, to show off Cap'n Proto, we add some twists:
|
||||
#
|
||||
# - You can use the result from one call as the input to the next
|
||||
# without a network round trip. To accomplish this, evaluate()
|
||||
# returns a `Value` object wrapping the actual numeric value.
|
||||
# This object may be used in a subsequent expression. With
|
||||
# promise pipelining, the Value can actually be used before
|
||||
# the evaluate() call that creates it returns!
|
||||
#
|
||||
# - You can define new functions, and then call them. This again
|
||||
# shows off pipelining, but it also gives the client the
|
||||
# opportunity to define a function on the client side and have
|
||||
# the server call back to it.
|
||||
#
|
||||
# - The basic arithmetic operators are exposed as Functions, and
|
||||
# you have to call getOperator() to obtain them from the server.
|
||||
# This again demonstrates pipelining -- using getOperator() to
|
||||
# get each operator and then using them in evaluate() still
|
||||
# only takes one network round trip.
|
||||
|
||||
evaluate @0 (expression: Expression) -> (value: Value);
|
||||
# Evaluate the given expression and return the result. The
|
||||
# result is returned wrapped in a Value interface so that you
|
||||
# may pass it back to the server in a pipelined request. To
|
||||
# actually get the numeric value, you must call read() on the
|
||||
# Value -- but again, this can be pipelined so that it incurs
|
||||
# no additional latency.
|
||||
|
||||
struct Expression {
|
||||
# A numeric expression.
|
||||
|
||||
union {
|
||||
literal @0 :Float64;
|
||||
# A literal numeric value.
|
||||
|
||||
previousResult @1 :Value;
|
||||
# A value that was (or, will be) returned by a previous
|
||||
# evaluate().
|
||||
|
||||
parameter @2 :UInt32;
|
||||
# A parameter to the function (only valid in function bodies;
|
||||
# see defFunction).
|
||||
|
||||
call :group {
|
||||
# Call a function on a list of parameters.
|
||||
function @3 :Function;
|
||||
params @4 :List(Expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface Value {
|
||||
# Wraps a numeric value in an RPC object. This allows the value
|
||||
# to be used in subsequent evaluate() requests without the client
|
||||
# waiting for the evaluate() that returns the Value to finish.
|
||||
|
||||
read @0 () -> (value :Float64);
|
||||
# Read back the raw numeric value.
|
||||
}
|
||||
|
||||
defFunction @1 (paramCount :Int32, body :Expression)
|
||||
-> (func :Function);
|
||||
# Define a function that takes `paramCount` parameters and returns the
|
||||
# evaluation of `body` after substituting these parameters.
|
||||
|
||||
interface Function {
|
||||
# An algebraic function. Can be called directly, or can be used inside
|
||||
# an Expression.
|
||||
#
|
||||
# A client can create a Function that runs on the server side using
|
||||
# `defFunction()` or `getOperator()`. Alternatively, a client can
|
||||
# implement a Function on the client side and the server will call back
|
||||
# to it. However, a function defined on the client side will require a
|
||||
# network round trip whenever the server needs to call it, whereas
|
||||
# functions defined on the server and then passed back to it are called
|
||||
# locally.
|
||||
|
||||
call @0 (params :List(Float64)) -> (value: Float64);
|
||||
# Call the function on the given parameters.
|
||||
}
|
||||
|
||||
getOperator @2 (op: Operator) -> (func: Function);
|
||||
# Get a Function representing an arithmetic operator, which can then be
|
||||
# used in Expressions.
|
||||
|
||||
enum Operator {
|
||||
add @0;
|
||||
subtract @1;
|
||||
multiply @2;
|
||||
divide @3;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import capnp # noqa: F401
|
||||
import addressbook_capnp
|
||||
|
||||
|
||||
class Allocator:
|
||||
def __init__(self):
|
||||
self.cur_size = 0
|
||||
self.last_size = 0
|
||||
|
||||
def __call__(self, minimum_size: int) -> bytearray:
|
||||
actual_size = max(minimum_size, self.cur_size)
|
||||
print(
|
||||
f"minimum_size: {minimum_size}, last_size: {self.last_size}, "
|
||||
f"actual_size: {actual_size}, cur_size: {self.cur_size}"
|
||||
)
|
||||
self.last_size = actual_size
|
||||
self.cur_size += actual_size
|
||||
|
||||
WORD_SIZE = 8
|
||||
byte_count = actual_size * WORD_SIZE
|
||||
return bytearray(byte_count)
|
||||
|
||||
|
||||
class MemoryViewAllocator:
|
||||
def __init__(self):
|
||||
self.buffers = []
|
||||
|
||||
def __call__(self, minimum_size: int) -> memoryview:
|
||||
WORD_SIZE = 8
|
||||
buffer = bytearray(minimum_size * WORD_SIZE)
|
||||
self.buffers.append(buffer)
|
||||
return memoryview(buffer)
|
||||
|
||||
|
||||
person = addressbook_capnp.Person.new_message(allocate_seg_callable=Allocator())
|
||||
|
||||
person.init("extraData", 5)
|
||||
print(person.extraData)
|
||||
print(type(person.extraData))
|
||||
print()
|
||||
|
||||
person.extraData = b"hello"
|
||||
print(person.extraData)
|
||||
print(type(person.extraData))
|
||||
print()
|
||||
|
||||
person = person.as_reader()
|
||||
print(person.extraData)
|
||||
print(type(person.extraData))
|
||||
print()
|
||||
|
||||
person = addressbook_capnp.Person.new_message(allocate_seg_callable=MemoryViewAllocator())
|
||||
|
||||
person.init("extraData", 5)
|
||||
print(person.extraData)
|
||||
print(type(person.extraData))
|
||||
print()
|
||||
|
||||
person.extraData = b"world"
|
||||
print(person.extraData)
|
||||
print(type(person.extraData))
|
||||
@@ -1,29 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFCTCCAvGgAwIBAgIUAd1KPs1sZ9NMbVRLXRM+XKSQ/gUwDQYJKoZIhvcNAQEL
|
||||
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDUxMjE4MjEwOFoXDTM1MDUx
|
||||
MDE4MjEwOFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAz52p5xMMrjqRtwp9oH6m7TOVNkIU2WohUQfhbPgBdpkj
|
||||
ZJnv6BBJyeUnDfJHayYGA7VPNh3MEWCIzFUok98fSZzIh4qqfSTg+2YdNIfM/WP8
|
||||
6jA6+YXutLl+dBZfTLW0DNUsCElgTWFpmi5AD2oqlMqdJKqOh5e8e6LBlpukXcc5
|
||||
ykNzFbTpkpc99f7k/2zMBe1208EjHXMscXDxuETdlTySo9FtetUxLuc7p6a6g472
|
||||
296/HYDaqjYZextKCTpZb4YXAG/+IcRqPi0qfSy4BKsYImo0G6lTIm28zvQBOkU9
|
||||
hpWw5GtKeWjdsF/Gr780QBY3AeUMX0cC4iU+goq4NLChRC9dde+yHZOUL7UgPAPD
|
||||
tQYjLZ9pbfi3pkJZ4879YIkDD/M0Zh6LPgstfG9PRNBFEXdb9Ima8ZsxN8YeelsM
|
||||
ZfmHKcWN8PAnH2ETGxVsXPXUuz1PoIxEh/MWWx1lASqF0bQtLsJP39XGWiRkhg4v
|
||||
djMv9lk4+uZwZ7oDiXqGEBTzdxGtvFYNjAOWtspumdtP9MwC+oI1IR6S/bG45jtz
|
||||
kkeiWz/sW1nktjFkSgdvECOBJAQM+RsNEs/9bpQV+Y8di12a/7imsSQacsvkf3Mw
|
||||
m127zYcdMJVfg84AZCz+EmDaFqfO9OQdsLPtQhL6Qpixd3Mtn1WoxIhhtb+blTUC
|
||||
AwEAAaNTMFEwHQYDVR0OBBYEFGBQtFxHoLu3xrGrXiulqFXhPHQTMB8GA1UdIwQY
|
||||
MBaAFGBQtFxHoLu3xrGrXiulqFXhPHQTMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggIBAI84QsV4nUP6nHhHnmrUBDRpUy7VcJVD5rfq3LBSd4j2sKtd
|
||||
Kv0REUDjYQVAjP6XvrX8Q+pHBrQmtoNoUWCPkp8dl8CpnjB5Jl2QPy2HOyZMmL/7
|
||||
LJN2KziZ/JprGxX1sYKKu7OQQ45ZwVByTwJ1JQRtQNhCYSV7IdA6nKbaWpnU98Xs
|
||||
usq0JZQ97HNCYqOHicFl0zzSAtRPDMwMc981LYeD31Pn48pnUOpyvVvnOhA+djr6
|
||||
kjxysSaDaqq+lemqXk/AcxISFp2AaljdK8eeDnSacULGjLQmdTG1Jpc6ko/kGbD4
|
||||
J2yeB4HQJ6cFsZTVicqORffqXV9rPoe+0BUDQweSs2/aTYsyiNOsWixkzdj5ZKFw
|
||||
xTEOwXBIyTt970TNJPOPRIVG5Fau5H+U/DnhrKVyLxlWYEVoYT35zAHOtlUZNR8E
|
||||
7NPPsn2zTkaG5S68vZ/w8GGLQkRIl1fC89HEFYiIdPYNbNlyKsVlODQZ53zr39VS
|
||||
pBTP7igelastMizfIECzkSKU2i+/io6kV9PUQ2oq1mEeEdDBYi0TwGX5d9zXOf15
|
||||
yG+PIHzb1EGQRamRC89ij0jTS/uCwgdd8ibWVzekeC43/BiSAIuGWKFF9Sq9XBdb
|
||||
HEO0V8WoMxRDKe0S3FchXNWbMBcWBUVBFs5YXCmo0O+4KJNp+2XoLA7ayweW
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,52 +0,0 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDPnannEwyuOpG3
|
||||
Cn2gfqbtM5U2QhTZaiFRB+Fs+AF2mSNkme/oEEnJ5ScN8kdrJgYDtU82HcwRYIjM
|
||||
VSiT3x9JnMiHiqp9JOD7Zh00h8z9Y/zqMDr5he60uX50Fl9MtbQM1SwISWBNYWma
|
||||
LkAPaiqUyp0kqo6Hl7x7osGWm6RdxznKQ3MVtOmSlz31/uT/bMwF7XbTwSMdcyxx
|
||||
cPG4RN2VPJKj0W161TEu5zunprqDjvbb3r8dgNqqNhl7G0oJOllvhhcAb/4hxGo+
|
||||
LSp9LLgEqxgiajQbqVMibbzO9AE6RT2GlbDka0p5aN2wX8avvzRAFjcB5QxfRwLi
|
||||
JT6Cirg0sKFEL11177Idk5QvtSA8A8O1BiMtn2lt+LemQlnjzv1giQMP8zRmHos+
|
||||
Cy18b09E0EURd1v0iZrxmzE3xh56Wwxl+YcpxY3w8CcfYRMbFWxc9dS7PU+gjESH
|
||||
8xZbHWUBKoXRtC0uwk/f1cZaJGSGDi92My/2WTj65nBnugOJeoYQFPN3Ea28Vg2M
|
||||
A5a2ym6Z20/0zAL6gjUhHpL9sbjmO3OSR6JbP+xbWeS2MWRKB28QI4EkBAz5Gw0S
|
||||
z/1ulBX5jx2LXZr/uKaxJBpyy+R/czCbXbvNhx0wlV+DzgBkLP4SYNoWp8705B2w
|
||||
s+1CEvpCmLF3cy2fVajEiGG1v5uVNQIDAQABAoICACSN+wM/fGUU1OEojLP8eMGc
|
||||
6nGyMt+Q6yrMO2mnRQKvteaZn/75FzTgDv9KoD6CZF60xqydlHeeypdHiyx2BZk9
|
||||
bKVIyfncy2wYL543JuWafEZzlX6nkT7qxhQEeGUWPQxhYC5ZVQZq12AZMphENhka
|
||||
j46MJSpEkiAmqPUulEMat9cgBxxUTSfNT1CHv6QlcMq+Y8Sm5drik3mpzDWIkocb
|
||||
Mip7zk3pSY6bkgpTtdVCD77oujekn1uGyPe+90smpeaX8mbWUSV64sXtx+RgQko4
|
||||
Ibi1gFU6e/O85Jh/p9Otq0aOBqZBKcy0pQvP4TjCbp22C5tey83ev+g3bIkpiYMT
|
||||
3JDJ5hF1qcdmuFbmFrXWn/4cdnSXRr+LGacOBMeIsJzgU7mIwsvOFqX89/oarRA/
|
||||
aG5RrzhJyt1rHb+tVre8jcgLQnpferENdJ2/UWiSi5J1BhPBYoPUxDeqqABItD31
|
||||
n6Rn6UZAOOzkMzpsB1SLSTg/hbTR2Y6kvQC2MZBLssnf+fxOH1/q+iYh8AL/kj57
|
||||
oRRbN+Pk5WxAN8+NT/meGhgsjKTeo5GooIHPmDIHBWurLZ5tBqbEQrqQHYWaOLig
|
||||
5tUhlZ/mD3/6kZQHjbmUwIjebRNeNmJhu0z8qhwpwVpPcUspUoHcDH84eWCDHC0H
|
||||
OHYWGW1swE4g30W09FsFAoIBAQD+pC3XP/l3yzOPX8/5W2uQWihWSm92+8USrZSm
|
||||
vbYz9jPb4vq36rRzLduxvkxrVeqF5ldykBYKWXXVJCirpBoTC5+eh1oTAzRGutm5
|
||||
+U2zbwtsDSvUmzI6MbH7xx8d2Beh0IgfWGIB3Veh2yxoPvo6QcUHR37I9KMrZsPQ
|
||||
70hg9+zWAF0Sh0t3ecoBocRqkrk0Lw1DARGOA4uTNbZcDJQAzPDmSWxAlbU2MoRU
|
||||
HWjTN3WXmOrsIC7VrFcByaFXKxwSL0j1yMlBQyGg4bclYb/8fb3StWVWU2XBUIgS
|
||||
xqsFGNnXpGkmY9HQ1RJF+wvJUMsWjgXqwf4iTzGJ1TPrJSgLAoIBAQDQuUBafgII
|
||||
Jc8YHGCn4IDobE5yM9BKHu1M3YnthenoJFu2DYRCiQV9QMNUzF2XrqfGQwAqydQi
|
||||
2Wfwqnh7tXoZbIRljEyDxfBI1YQmr9icFyoq7JIa+4f2AIuUl0AQk8wYvwELVPC/
|
||||
fU/N3LMnVLjq5VajPt9blwSBeUpjGA8y0qJaQ2KV7knFQwtI7OrAeNFH8I/wkIRH
|
||||
vNy+/Sz0BcDHRRL0120/45DFNCuxyvs5EN/dAuvOfSJqndY1t/fAVoNg2wvAyns/
|
||||
sbKQeexd/clTYGjqAMEU/sImtQEwldeCPvuCAhUwASylrcHWoHsCDb4pKcgiFsIN
|
||||
tPWgsdrbGD+/AoIBAQD2fVh+Z0gGBOYJIFcCatNJbWxkczNIutf+h5ZAfZ202NtE
|
||||
O4g0pfY9FCP4/1ub/xPAv8Lge8dKB2T/iDvyQiyXSQYe/6hahRyCZvbBhikHyzME
|
||||
Sg+mgwBwwpAmR47AZeAiW+iYZwagBXGBlNZ8ppGz+NxPeo6o2d5k8doVErs+Wl+g
|
||||
m8N8XwjXQ0YepEesXhD3CaDNvmgOzzG5syGuIuLVj4yVbndiYUiDiQz9G2bQJnwm
|
||||
3fhxz4lmfqfObC5IYcuPcsQuX0kpamFQCY4umlusfs9T+xF4Kcxy/5BolHURvweI
|
||||
LXc3mSKOAuLoaOX03sdoMtxZbaWh8oTihkX2lgYXAoIBAGhhHCOk/FMixUwjdNq/
|
||||
VPfmodxOuQ04JifYak+UNoNXG14RqGC1sT8QEh7oDK38M/7cJss/H41F98rNFW+Y
|
||||
M7VfJV67KNCFPkLONEY8jjCRDQ9mOzKvMzD82NC4Stt/bgO6EUWfdr3sZupmQlma
|
||||
7tbZVdhRatWc0i4FgAPKVl9uIq7NIBImllHF03DmugcC5HX7gaAmRWCyvBnu9noa
|
||||
HmwIyRAUY5gdr5pPGsLQ5Y2GOM2H1nDu9zUmNaerloRjP1RCdsA1Aim6Lbg+oMvo
|
||||
TLQbdJwBQI3FUUaWIkAvzxRddt1vOTVGgRNhr5wrqRg/0yc2s9UIWIcORf/UscP7
|
||||
fnUCggEAPi88JcWuzlJmEUSJdgjRzdCnNh3ZyOUG8L8SIQaLs098hsJhR4cpHc6Q
|
||||
2uXr7kuTkXlNh/vUZmfFMQJe+8Pg+fjUzom7oM4iOEGozJRyY9phjPWvUgE2UWfd
|
||||
Pa2nLqhP2UIplayvr6gGPYVlxSnaddZPMBB53W641nYxO1iuEHY25vdkAnhQU1K+
|
||||
BfaR9E7Rc80AAhK+Fhs2xzoHUKJ/z1ciFtZylqWZNDu2jHSgBLawDljhCqLstJ3q
|
||||
vx7Qlq+j6dh68BVb60/R4RRsRy6pvUFbYcKPFiuEPK3EYr3Ascy7P2tn9Z8LdXie
|
||||
flYh3XPcmbDCBLfXFvforZR+CYzBlA==
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -1,12 +0,0 @@
|
||||
@0xf5745ea9c82baa3a;
|
||||
|
||||
interface Example {
|
||||
interface StatusSubscriber {
|
||||
status @0 (value: Bool);
|
||||
# Call the function on the given parameters.
|
||||
}
|
||||
|
||||
longRunning @0 () -> (value: Bool);
|
||||
subscribeStatus @1 (subscriber: StatusSubscriber);
|
||||
alive @2 () -> (value: Bool);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ backend-path = ["_custom_build"]
|
||||
|
||||
[project]
|
||||
name = "pycapnp"
|
||||
requires-python = ">=3.9"
|
||||
requires-python = ">=3.12"
|
||||
dynamic = [
|
||||
"authors",
|
||||
"classifiers",
|
||||
@@ -13,60 +13,31 @@ dynamic = [
|
||||
"keywords",
|
||||
"license",
|
||||
"readme",
|
||||
"scripts",
|
||||
"version",
|
||||
]
|
||||
dependencies = [
|
||||
"jinja2",
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"tox",
|
||||
"pkgconfig",
|
||||
"cython>=3.0",
|
||||
"setuptools",
|
||||
{include-group = "lint"},
|
||||
{include-group = "test"},
|
||||
{include-group = "docs"},
|
||||
]
|
||||
|
||||
lint = [
|
||||
# TODO: Update lint and formatting for newer Ruff releases, then remove the pin.
|
||||
"ruff==0.15.22",
|
||||
]
|
||||
|
||||
test = [
|
||||
"pytest",
|
||||
"anyio",
|
||||
"pytest-asyncio",
|
||||
"pytest-tornasync",
|
||||
"pytest-trio",
|
||||
"pytest-twisted",
|
||||
"twisted",
|
||||
]
|
||||
|
||||
docs = [
|
||||
"sphinx",
|
||||
"sphinx-multiversion",
|
||||
]
|
||||
dev = ["cython>=3.0", "setuptools", "wheel", "pkgconfig", "build", {include-group = "test"}, {include-group = "lint"}]
|
||||
test = ["pytest"]
|
||||
lint = ["ruff==0.16.8"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
testpaths = ["test"]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py37"
|
||||
target-version = "py312"
|
||||
line-length = 120
|
||||
exclude = ["benchmark", "build", "capnp/templates/module.pyx"]
|
||||
exclude = ["build", "build64", "bundled"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = [
|
||||
"E203", "E211", "E225", "E226", "E227", "E231", "E251",
|
||||
"E261", "E262", "E265", "E402",
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"test/test_examples.py" = ["C901"]
|
||||
"capnp/__init__.py" = ["F401", "F403", "F405"]
|
||||
|
||||
[tool.ruff.lint.mccabe]
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import capnp
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("command")
|
||||
parser.add_argument("schema_file")
|
||||
parser.add_argument("struct_name")
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--defaults",
|
||||
help="include default values in json output",
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def encode(schema_file, struct_name, **kwargs):
|
||||
schema = capnp.load(schema_file)
|
||||
|
||||
struct_schema = getattr(schema, struct_name)
|
||||
|
||||
struct_dict = json.load(sys.stdin)
|
||||
struct = struct_schema.from_dict(struct_dict)
|
||||
|
||||
struct.write(sys.stdout)
|
||||
|
||||
|
||||
def decode(schema_file, struct_name, defaults):
|
||||
schema = capnp.load(schema_file)
|
||||
|
||||
struct_schema = getattr(schema, struct_name)
|
||||
struct = struct_schema.read(sys.stdin)
|
||||
|
||||
json.dump(struct.to_dict(defaults), sys.stdout)
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
|
||||
command = args.command
|
||||
kwargs = vars(args)
|
||||
del kwargs["command"]
|
||||
|
||||
globals()[command](**kwargs) # hacky way to get defined functions, and call function with name=command
|
||||
|
||||
|
||||
main()
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
import capnp
|
||||
|
||||
capnp.add_import_hook([os.getcwd(), "/usr/local/include/"]) # change this to be auto-detected?
|
||||
|
||||
import test_capnp # noqa: E402
|
||||
|
||||
|
||||
def decode(name):
|
||||
class_name = name[0].upper() + name[1:]
|
||||
with getattr(test_capnp, class_name).from_bytes(sys.stdin.read()) as msg:
|
||||
print(msg._short_str())
|
||||
|
||||
|
||||
def encode(name):
|
||||
val = getattr(test_capnp, name)
|
||||
class_name = name[0].upper() + name[1:]
|
||||
message = getattr(test_capnp, class_name).from_dict(val.to_dict())
|
||||
print(message.to_bytes())
|
||||
|
||||
|
||||
if sys.argv[1] == "decode":
|
||||
decode(sys.argv[2])
|
||||
else:
|
||||
encode(sys.argv[2])
|
||||
@@ -1,170 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Download GitHub Actions build artifacts for a tagged commit (or specific run
|
||||
# ID) and upload the wheels + sdist to PyPI via twine.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/release-pypi.sh <tag-or-run-id> [output-dir] [--force] [--test]
|
||||
#
|
||||
# Examples:
|
||||
# scripts/release-pypi.sh v2.2.1
|
||||
# scripts/release-pypi.sh 2.2.1 dist_221
|
||||
# scripts/release-pypi.sh 1234567890 dist_run_1234567890
|
||||
# scripts/release-pypi.sh v2.2.1 --test # dry-run upload to TestPyPI
|
||||
#
|
||||
# Requirements:
|
||||
# - gh CLI (authenticated; `gh auth status` must succeed)
|
||||
# - python3
|
||||
# - Twine credentials configured in the environment or ~/.pypirc.
|
||||
# For real uploads: ~/.pypirc [pypi] section, or TWINE_USERNAME/TWINE_PASSWORD.
|
||||
# For --test uploads: ~/.pypirc [testpypi] section (separate TestPyPI account
|
||||
# and API token; see https://packaging.python.org/en/latest/guides/using-testpypi/).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
WORKFLOW_FILE="wheels.yml"
|
||||
ARTIFACT_PATTERN="cibw-*"
|
||||
VENV_DIR=".venv-release"
|
||||
|
||||
usage() {
|
||||
cat >&2 <<EOF
|
||||
Usage: $0 <tag-or-run-id> [output-dir] [--force] [--test]
|
||||
|
||||
<tag-or-run-id> Git tag (e.g. v2.2.1 or 2.2.1) or a GitHub Actions run ID.
|
||||
[output-dir] Directory to place wheels/sdist into. Defaults to
|
||||
dist_<digits> for a tag, or dist_run_<id> for a run ID.
|
||||
--force Allow reusing a non-empty output directory.
|
||||
--test Dry-run: upload to TestPyPI (https://test.pypi.org) instead
|
||||
of the real PyPI. Requires a [testpypi] entry in ~/.pypirc
|
||||
or a TestPyPI API token.
|
||||
EOF
|
||||
exit 2
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command '$1' not found in PATH"
|
||||
}
|
||||
|
||||
FORCE=0
|
||||
TEST_UPLOAD=0
|
||||
POSITIONAL=()
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--force) FORCE=1 ;;
|
||||
--test|--testpypi|--dry-run) TEST_UPLOAD=1 ;;
|
||||
-h|--help) usage ;;
|
||||
*) POSITIONAL+=("$arg") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ${#POSITIONAL[@]} -ge 1 && ${#POSITIONAL[@]} -le 2 ]] || usage
|
||||
|
||||
INPUT="${POSITIONAL[0]}"
|
||||
OUTPUT_DIR="${POSITIONAL[1]:-}"
|
||||
|
||||
require_cmd gh
|
||||
require_cmd python3
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
echo ">> verifying gh authentication"
|
||||
gh auth status >/dev/null || die "gh is not authenticated; run 'gh auth login'"
|
||||
|
||||
# Resolve run ID. A bare integer is treated as a run ID; otherwise we look up
|
||||
# the latest successful run on the branch/tag matching the build workflow.
|
||||
if [[ "$INPUT" =~ ^[0-9]+$ ]]; then
|
||||
RUN_ID="$INPUT"
|
||||
DEFAULT_OUT="dist_run_${RUN_ID}"
|
||||
echo ">> using GitHub Actions run id: $RUN_ID"
|
||||
elif [[ "$INPUT" =~ ^v?[0-9].* ]]; then
|
||||
TAG="$INPUT"
|
||||
TAG_NO_V="${TAG#v}"
|
||||
DEFAULT_OUT="dist_${TAG_NO_V//./}"
|
||||
echo ">> resolving latest successful '$WORKFLOW_FILE' run for tag '$TAG'"
|
||||
RUN_ID="$(gh run list \
|
||||
--workflow="$WORKFLOW_FILE" \
|
||||
--branch "$TAG" \
|
||||
--status success \
|
||||
--limit 1 \
|
||||
--json databaseId \
|
||||
--jq '.[0].databaseId')"
|
||||
if [[ -z "${RUN_ID:-}" || "$RUN_ID" == "null" ]]; then
|
||||
die "no successful '$WORKFLOW_FILE' run found for tag '$TAG'"
|
||||
fi
|
||||
echo ">> resolved run id: $RUN_ID"
|
||||
else
|
||||
die "first arg must be a git tag (vX.Y.Z) or a numeric run ID"
|
||||
fi
|
||||
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$DEFAULT_OUT}"
|
||||
|
||||
if [[ -e "$OUTPUT_DIR" ]]; then
|
||||
if [[ -d "$OUTPUT_DIR" ]]; then
|
||||
if [[ -n "$(ls -A "$OUTPUT_DIR" 2>/dev/null)" && "$FORCE" -ne 1 ]]; then
|
||||
die "'$OUTPUT_DIR' is not empty; pass --force to reuse it"
|
||||
fi
|
||||
else
|
||||
die "'$OUTPUT_DIR' exists and is not a directory"
|
||||
fi
|
||||
fi
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
OUTPUT_DIR_ABS="$(cd "$OUTPUT_DIR" && pwd)"
|
||||
|
||||
TMP_DIR="$(mktemp -d -t pycapnp-release-XXXXXX)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
echo ">> downloading artifacts (pattern '$ARTIFACT_PATTERN') into $TMP_DIR"
|
||||
gh run download "$RUN_ID" --dir "$TMP_DIR" --pattern "$ARTIFACT_PATTERN"
|
||||
|
||||
echo ">> flattening wheels and sdists into $OUTPUT_DIR_ABS"
|
||||
shopt -s nullglob globstar
|
||||
moved=0
|
||||
for f in "$TMP_DIR"/**/*.whl "$TMP_DIR"/**/*.tar.gz; do
|
||||
[[ -f "$f" ]] || continue
|
||||
mv -n "$f" "$OUTPUT_DIR_ABS/"
|
||||
moved=$((moved + 1))
|
||||
done
|
||||
shopt -u globstar
|
||||
[[ "$moved" -gt 0 ]] || die "no .whl or .tar.gz files found in downloaded artifacts"
|
||||
echo ">> collected $moved files"
|
||||
|
||||
echo ">> setting up release virtualenv at $VENV_DIR"
|
||||
if [[ ! -x "$VENV_DIR/bin/python" ]]; then
|
||||
python3 -m venv "$VENV_DIR"
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
source "$VENV_DIR/bin/activate"
|
||||
python -m pip install --quiet --upgrade pip
|
||||
python -m pip install --quiet --upgrade twine
|
||||
|
||||
echo ">> running 'twine check'"
|
||||
python -m twine check "$OUTPUT_DIR_ABS"/*
|
||||
|
||||
if [[ "$TEST_UPLOAD" -eq 1 ]]; then
|
||||
TARGET_LABEL="TestPyPI (https://test.pypi.org)"
|
||||
TWINE_REPO_ARGS=(--repository testpypi)
|
||||
else
|
||||
TARGET_LABEL="PyPI (https://pypi.org)"
|
||||
TWINE_REPO_ARGS=()
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Target: $TARGET_LABEL"
|
||||
echo "Files to upload from $OUTPUT_DIR_ABS:"
|
||||
ls -1 "$OUTPUT_DIR_ABS"
|
||||
echo
|
||||
read -r -p "Upload these to $TARGET_LABEL? [y/N] " reply
|
||||
case "$reply" in
|
||||
[yY]|[yY][eE][sS]) ;;
|
||||
*) echo "aborted; files remain in $OUTPUT_DIR_ABS"; exit 0 ;;
|
||||
esac
|
||||
|
||||
echo ">> uploading to $TARGET_LABEL via twine"
|
||||
python -m twine upload "${TWINE_REPO_ARGS[@]}" "$OUTPUT_DIR_ABS"/*
|
||||
|
||||
echo ">> done"
|
||||
42
setup.py
42
setup.py
@@ -55,13 +55,9 @@ short_version = '%s'
|
||||
|
||||
write_version_py()
|
||||
|
||||
# Try to use README.md and CHANGELOG.md as description and changelog
|
||||
# Use the fork README as the package description
|
||||
with open("README.md", encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
with open("CHANGELOG.md", encoding="utf-8") as f:
|
||||
changelog = f.read()
|
||||
changelog = "\nChangelog\n=============\n" + changelog
|
||||
long_description += changelog
|
||||
|
||||
|
||||
class clean(_clean):
|
||||
@@ -154,8 +150,6 @@ class build_libcapnp_ext(build_ext_c):
|
||||
|
||||
# Check if we've already built capnproto
|
||||
capnp_bin = os.path.join(build_dir, "bin", "capnp")
|
||||
if os.name == "nt":
|
||||
capnp_bin = os.path.join(build_dir, "bin", "capnp.exe")
|
||||
|
||||
if not os.path.exists(capnp_bin):
|
||||
# Not built, fetch and build
|
||||
@@ -170,23 +164,10 @@ class build_libcapnp_ext(build_ext_c):
|
||||
os.path.join(build_dir, "lib"),
|
||||
] + self.library_dirs
|
||||
|
||||
# Copy .capnp files from source
|
||||
src_glob = glob.glob(os.path.join(build_dir, "include", "capnp", "*.capnp"))
|
||||
dst_dir = os.path.join(self.build_lib, "capnp")
|
||||
os.makedirs(dst_dir, exist_ok=True)
|
||||
for file in src_glob:
|
||||
print("copying {} -> {}".format(file, dst_dir))
|
||||
shutil.copy(file, dst_dir)
|
||||
|
||||
return build_ext_c.run(self)
|
||||
|
||||
|
||||
extra_compile_args = ["--std=c++14"]
|
||||
extra_link_args = []
|
||||
if os.name == "nt":
|
||||
extra_compile_args = ["/std:c++14", "/MD"]
|
||||
extra_link_args = ["/MANIFEST"]
|
||||
|
||||
import Cython.Build # noqa: E402
|
||||
import Cython # noqa: E402
|
||||
|
||||
@@ -194,26 +175,24 @@ extensions = [
|
||||
Extension(
|
||||
"*",
|
||||
[
|
||||
"capnp/helpers/capabilityHelper.cpp",
|
||||
"capnp/includes/PyCustomMessageBuilder.cpp",
|
||||
"capnp/helpers/exception.cpp",
|
||||
"capnp/lib/*.pyx",
|
||||
],
|
||||
extra_compile_args=extra_compile_args,
|
||||
extra_link_args=extra_link_args,
|
||||
language="c++",
|
||||
)
|
||||
]
|
||||
|
||||
setup(
|
||||
python_requires=">=3.9",
|
||||
python_requires=">=3.12",
|
||||
name="pycapnp",
|
||||
packages=["capnp"],
|
||||
packages=["capnp", "capnp.lib"],
|
||||
include_package_data=False,
|
||||
version=VERSION,
|
||||
package_data={
|
||||
"capnp": [
|
||||
"*.pxd",
|
||||
"*.h",
|
||||
"*.capnp",
|
||||
"helpers/*.pxd",
|
||||
"helpers/*.h",
|
||||
"includes/*.h",
|
||||
@@ -222,13 +201,11 @@ setup(
|
||||
"lib/*.py",
|
||||
"lib/*.pyx",
|
||||
"lib/*.h",
|
||||
"templates/*",
|
||||
]
|
||||
},
|
||||
ext_modules=Cython.Build.cythonize(extensions),
|
||||
cmdclass={"clean": clean, "build_ext": build_libcapnp_ext},
|
||||
install_requires=[],
|
||||
entry_points={"console_scripts": ["capnpc-cython = capnp._gen:main"]},
|
||||
# PyPi info
|
||||
description="A cython wrapping of the C++ Cap'n Proto library",
|
||||
long_description=long_description,
|
||||
@@ -237,24 +214,19 @@ setup(
|
||||
# (setup.py only supports 1 author...)
|
||||
author="Jacob Alexander", # <- Current maintainer; Original author -> Jason Paryani
|
||||
author_email="haata@kiibohd.com",
|
||||
url="https://github.com/capnproto/pycapnp",
|
||||
download_url="https://github.com/capnproto/pycapnp/archive/v%s.zip" % VERSION,
|
||||
url="https://github.com/commaai/pycapnp",
|
||||
download_url="https://github.com/commaai/pycapnp/archive/v%s.zip" % VERSION,
|
||||
keywords=["capnp", "capnproto", "Cap'n Proto", "pycapnp"],
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows :: Windows 10",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: C++",
|
||||
"Programming Language :: Cython",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Communications",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
@0xc39aee9191aedcf3;
|
||||
|
||||
const qux :UInt32 = 123;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Employer;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
}
|
||||
|
||||
struct Employer {
|
||||
name @0 :Text;
|
||||
boss @1 :Person;
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
@0xd33206731939e03b;
|
||||
|
||||
const qux :UInt32 = 123;
|
||||
|
||||
struct Person {
|
||||
id @0 :UInt32;
|
||||
name @1 :Text;
|
||||
email @2 :Text;
|
||||
phones @3 :List(PhoneNumber);
|
||||
|
||||
struct PhoneNumber {
|
||||
number @0 :Text;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
mobile @0;
|
||||
home @1;
|
||||
work @2;
|
||||
}
|
||||
}
|
||||
|
||||
employment :union {
|
||||
unemployed @4 :Void;
|
||||
employer @5 :Employer;
|
||||
school @6 :Text;
|
||||
selfEmployed @7 :Void;
|
||||
# We assume that a person is only one of these.
|
||||
}
|
||||
}
|
||||
|
||||
struct Employer {
|
||||
name @0 :Text;
|
||||
boss @1 :Person;
|
||||
}
|
||||
|
||||
struct AddressBook {
|
||||
people @0 :List(Person);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,17 +0,0 @@
|
||||
@0xfb9a160831eee9bb;
|
||||
|
||||
struct AnnotationStruct {
|
||||
test @0: Int32;
|
||||
}
|
||||
|
||||
annotation test1(*): Text;
|
||||
annotation test2(*): AnnotationStruct;
|
||||
annotation test3(*): List(AnnotationStruct);
|
||||
annotation test4(*): List(UInt16);
|
||||
|
||||
$test1("TestFile");
|
||||
|
||||
struct TestAnnotationOne $test1("Test") { }
|
||||
struct TestAnnotationTwo $test2(test = 100) { }
|
||||
struct TestAnnotationThree $test3([(test=100), (test=101)]) { }
|
||||
struct TestAnnotationFour $test4([200, 201]) { }
|
||||
@@ -1,5 +0,0 @@
|
||||
@0x9afc0f7513269df3;
|
||||
|
||||
struct Child {
|
||||
name @0 :Text;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
@0x95c41c96183b9c2f;
|
||||
|
||||
using import "/schemas/child.capnp".Child;
|
||||
|
||||
struct Parent {
|
||||
child @0 :List(Child);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user