Minimal pycapnp

This commit is contained in:
Adeeb Shihadeh
2026-09-21 18:57:37 -07:00
parent a0cb5cdf06
commit 5befba15f3
125 changed files with 423 additions and 14793 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -53,3 +53,7 @@ example
# capnp files
*.capnp
.venv/
.pytest_cache/
.ruff_cache/

View File

@@ -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

View File

@@ -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
View File

@@ -1,265 +1,62 @@
# pycapnp
# pycapnp for openpilot
[![Packaging Status](https://github.com/capnproto/pycapnp/workflows/Packaging%20Test/badge.svg)](https://github.com/capnproto/pycapnp/actions)
[![manylinux2014 Status](https://github.com/capnproto/pycapnp/workflows/manylinux2014/badge.svg)](https://github.com/capnproto/pycapnp/actions)
[![PyPI version](https://badge.fury.io/py/pycapnp.svg)](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.

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
```

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
pyproto-carsales $@

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
pyproto-catrank $@

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
pyproto-eval $@

View File

@@ -1 +0,0 @@
protobuf

View File

@@ -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()

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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);
# }
# }

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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()

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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");

View File

@@ -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>();
}

View 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
View 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();

View File

@@ -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

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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;
};
}

View File

@@ -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)

View File

@@ -1,40 +1,17 @@
# 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:
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:
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:
cdef cppclass StructSchema nogil:
pass
cdef extern from "capnp/blob.h" namespace " ::capnp":
@@ -48,601 +25,47 @@ cdef extern from "capnp/blob.h" namespace " ::capnp":
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()
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:
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:
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)
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)
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>'()
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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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 %}

View File

@@ -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++")
)

View File

@@ -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."

View File

@@ -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 %}

View File

@@ -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:

View File

@@ -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$"

View File

@@ -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:
Capn 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, Capn 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

View File

@@ -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`

View File

@@ -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>`_.

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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)))

View File

@@ -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()))

View File

@@ -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)))

View File

@@ -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

View File

@@ -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()))

View File

@@ -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)))

View File

@@ -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()))

View File

@@ -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)))

View File

@@ -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()))

View File

@@ -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)))

View File

@@ -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()))

View File

@@ -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;
}
}

View File

@@ -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))

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -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);
}

View File

@@ -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]

View File

@@ -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()

View File

@@ -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])

View File

@@ -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"

View File

@@ -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",
],
)

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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]) { }

View File

@@ -1,5 +0,0 @@
@0x9afc0f7513269df3;
struct Child {
name @0 :Text;
}

View File

@@ -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