Commit Graph

176 Commits

Author SHA1 Message Date
Jacob Alexander
162fddbcf6 Replace black and flake8 with ruff for linting and formatting
- Remove .flake8; add [tool.ruff] and [tool.ruff.format] config in pyproject.toml
  (line-length 120, excludes, ignore list, per-file-ignores, mccabe complexity)
- Update GitHub workflow lint job to run `ruff check .` and `ruff format --check .`
- Swap black and flake8 for ruff in requirements.txt and Pipfile
- Change capnp/__init__.py to ruff-style noqa comment
- Move max-complexity into [tool.ruff.lint.mccabe], lint options into [tool.ruff.lint]
- Add per-file-ignores for capnp/__init__.py (F401, F403, F405), remove inline noqa
- Run ruff format across codebase (24 files) for consistent style
2026-05-30 11:59:52 -07:00
Brian Xu
76a41c8efe refine documentation for PyCustomMessageBuilder (#395)
* refine document for py custom message builder

* refine example

* refine example
2026-01-31 13:15:56 -08:00
Brian Xu
9237ba123a Revert Data fields to bytes and add get_data_as_view for zero-copy access (#390)
* get data field with view

* refine tc

* refine based on flake check

* run black again

* rebase upstream master

* add comment to tc

* refine raise exception
2026-01-15 21:40:22 -08:00
André Cruz
9754258d46 Fix use-after-free in async write causing corruption with large payloads (#392)
In _PyAsyncIoStreamProtocol.write_loop(), memoryview objects pointing to
C++ message memory were passed directly to transport.write(). Since
transport.write() is non-blocking and only queues data for later
transmission, the memoryview could reference freed memory after
fulfill() was called.

This caused message corruption when pipelining RPC calls with payloads
larger than ~4000 bytes, as the C++ message memory would be freed before
asyncio had a chance to transmit the data.

The fix copies the data to Python bytes objects before passing to
transport.write(), ensuring the data remains valid until asyncio
transmits it.

Includes regression test that verifies large payload integrity with both
sequential and pipelined RPC calls.
2026-01-15 16:35:50 -08:00
Brian Xu
c14fd7036a Make message.to_dict() return bytes for DATA type field (#386)
* change to_dict return bytes for DATA type

* reformat

* reformat

* add includes .h

---------

Co-authored-by: Brian Xu <brian.xu1@bytedance.com>
2025-09-30 07:48:10 -07:00
Brian Xu
84674909a2 Support python custom message builder and make Data field's type return MemoryView (#380)
This PR is for resolving the following issue:
[issue](https://github.com/capnproto/pycapnp/issues/379)

1. Created `_PyCustomMessageBuilder` extends `MessageBuilder`, enabling the ability to customise the `SegmentAllocate` method in Python. This allows allocation and data population within shared memory, and supports zero-copy inter-process data transfer by passing segment offsets.

2. Fields of type `Data` now support being set with a `memoryview`. When retrieving a `Data` field from a `DynamicStructBuilder`, it will return a writable `memoryview`, allowing users to modify the data directly. This enables memory to be pre-allocated and content to be modified in later, eliminating an extra copy. When retrieving a `Data` field from a `DynamicStructReader`, it will return a read-only `memoryview`, allowing user to read data without memory copy.

* add memoryview and custom builder

* support set dynamic field

* add curSize

* add initialSize and lastSize

* change StringPtr name

* add test case

* refine test case

* convert func to py callable object

* add initial value

* refine example

* add copy as_reader and new_message, make structReader's data field return RO memoryView

* rebase master and bugfix

* reformat flake8

* refine test case

* refine test cases for blob

* remove unused import for flake8

* run black .

---------

Co-authored-by: Brian Xu <brian.xu1@bytedance.com>
2025-09-10 06:48:58 -07:00
Yevhenii Kizim
62682977cb add schema for tests 2025-09-06 19:14:58 -07:00
Yevhenii Kizim
144acbea7a Add structure-free read_multiple_bytes_packed
Motivation: A server sends data packages that consist of multiple
serialized capnproto messages of different structures. Every message is
guaranteed to have the same first field, which works as a message header
containing information about the message structure type.
The scheme comprises the `UnknownMessage` structure that allows parsing
the header only.

Solution: provide a public interface that iterates buffer with
AnyPointer readers to cast a message to `UnknownMessage` first and then
to a specific structure type.
2025-09-06 19:14:58 -07:00
Dominik Andreas
237fa7dd4b add test/blob_test.capnp 2025-09-06 11:37:18 -07:00
Dominik Andreas
ed461ed2f5 added binary support in dictionaries via base64 encoding 2025-09-06 11:37:18 -07:00
Yian Chen
78dd54e641 address flake8 format check 2024-04-11 19:01:59 -07:00
Lasse Blaauwbroek
3aade70bfa Some fixes to the magic import system
- Stop adding the directory of every .capnp file to the import path. If a .capnp
  file wants to import a file in its own directory, it should use a relative
  import. Fixes #278
- Stop using /usr/include/capnp as an import path. This is incorrect. It should
  only be /usr/include.
- Stop allowing additional paths to be specified for magic imports. This leads
  to inconsistencies. More specifically, the way that a nested import like
  `ma.mb.mc_capnp` gets imported by python, is to first import `ma`, then import
  `ma.mb`, and finally `ma.mb.mc_capnp`. Pycapnp's magic importing is only
  involved in the last step. So any additional paths specified don't work for
  nested imports. It is very confusing to only have this for non-nested imports.
  Users with folder layouts that don't follow pythons import paths can still use
  `capnp.load(.., .., imports=[blah])`.
2023-11-25 08:16:38 -08:00
Lasse Blaauwbroek
0ec4d0b778 Allow cancellation of all capability contexts 2023-11-08 07:09:10 -08:00
Lasse Blaauwbroek
ef5e039067 Support _DynamicListReader in _setDynamicField
See the test for an explanation.

Note that I'm not sure what the purpose of `_setDynamicFieldWithField` and
`_setDynamicFieldStatic` is. It does not appear to be used. I've kept them for
now (they are a public API), but perhaps this can be removed.
2023-11-08 07:02:09 -08:00
Fabio Rossetto
42665a61c9 Properly join list of methods in _DynamicCapabilityClient
This was already fixed in c9bea05f44, but the fix does not seem to work.
This commit uses a set union, which should be more robust. It also adds
a couple of assertions to verify that it indeed works.
2023-11-01 19:03:11 -07:00
Tobias Ahrens
bb15822850 Fixes for capnp 1.0 (#1)
* add capnp_api.h to gitignore

* Change type of read_min_bytes from size to int

Not sure why this was not causing issues before or if that
is the right fix ... but it seems to be fine :)

* Adapt python_requires to >=3.8

This was overlooked when 3.7 was deprecated. The ci no longer
works with python 3.7 and cibuildwheel uses python_requires ...

* Replace deprecated find_module with find_spec (importlib)

find_module was deprecated with python 3.4 and python 3.12
removed it (https://docs.python.org/3.12/whatsnew/3.12.html#importlib).

The new command is find_spec and only required a few adaptions
2023-10-11 11:28:24 -07:00
Lasse Blaauwbroek
e13a0c9254 Experiment: Wrap all capnp code in a context-manager to avoid segfaults (#317)
* Experiment: Wrap all capnp code in a context-manager

* Fix segfault in on_disconnect
2023-10-03 09:04:51 -07:00
DaneSlattery
f59b3fdc91 adjust tests for async 2023-06-19 12:21:38 +02:00
Rowan Reeve
a5c29a74d2 Schema loading from the wire
Cap'n Proto provides a schema loader, which can be used to dynamically
load schemas during runtime. To port this functionality to pycapnp,
a new class is provided `C_SchemaLoader`, which exposes the Cap'n
Proto C++ interface, and `SchemaLoader`, which is part of the pycapnp
library.

The specific use case for this is when a capnp message contains
a Node.Reader: The schema for a yet unseen message can be loaded
dynamically, allowing the future message to be properly processed.

If the message is a struct containing other structs, all the schemas for
every struct must be loaded to correctly parse the message. See
https://github.com/DaneSlattery/capnp_generic_poc for a
proof-of-concept.

Add docs and cleanup

Add more docs

Reduce changes

Fix flake8 formatting

Fix get datatype
2023-06-12 14:10:13 +02:00
Lasse Blaauwbroek
5edf700548 Remove .capnp fixture from test_capability_context.py
Using a fixture makes things more complicated. If we want to test explicit
capnp.load() functionality, we can do that separately
2023-06-11 04:09:01 +02:00
Lasse Blaauwbroek
95bb528ea2 Delete test_capability_old.py, which is mostly redundant
All of these tests also exist in test_capability.py. The only difference is the
way the .capnp file is loaded. But that could be tested with much less code.
2023-06-11 03:51:23 +02:00
Lasse Blaauwbroek
84d0f365ad Fix formatting 2023-06-08 08:18:50 +02:00
Lasse Blaauwbroek
4b5c4211f1 Force server methods to be async and client calls to use await 2023-06-08 03:56:57 +02:00
Lasse Blaauwbroek
af99e388fb Fix and improve a bunch of tests 2023-06-08 02:29:54 +02:00
Lasse Blaauwbroek
97bdeaea12 Disable option to run capnp without asyncio 2023-06-08 02:27:38 +02:00
Lasse Blaauwbroek
6e011cfe78 Get rid of capnp timer functionality.
The asyncio timer should now be used
2023-06-07 20:55:43 +02:00
Lasse Blaauwbroek
8feb377217 Allow reading and writing messages from sockets in async mode 2023-06-07 11:24:43 -07:00
Lasse Blaauwbroek
d32854eb00 Integrate the KJ event loop into Python's asyncio event loop (#310)
* Integrate the KJ event loop into Python's asyncio event loop

Fix #256

This PR attempts to remove the slow and expensive polling behavior for asyncio
in favor of proper linking of the KJ event loop to the asyncio event loop.

* Don't memcopy buffer

* Improve promise cancellation and prepare for timer implementation

* Add attribution for asyncProvider.cpp

* Implement timeout

* Cleanup

* First round of simplifications

* Add more a_wait functions and a shutdown function

* Fix edge-cases with loop shutdown

* Clean up calculator examples

* Cleanup

* Cleanup

* Reformat

* Fix warnings

* Reformat again

* Compatibility with macos

* Inline the asyncio loop in some places where this is feasible

* Add todo

* Fix

* Remove synchronous wait

* Wrap fd listening callbacks in a class

* Remove poll_forever

* Remove the thread-local/thread-global optimization

This will not matter much soon anyway, and simplifies things

* Share promise code by using fused types

* Improve refcounting of python objects in promises

We replace many instances of PyObject* by Own<PyRefCounter> for more automatic
reference management.

* Code wrapPyFunc in a similar way to wrapPyFuncNoArg

* Refactor capabilityHelper, fix several memory bugs for promises and add __await__

* Improve promise ownership, reduce memory leaks

Promise wrappers now hold a Own<Promise<Own<PyRefCounter>>> object. This might
seem like excessive nesting of objects (which to some degree it is, but with
good reason):
- The outer Own is needed because Cython cannot allocate objects without a
  nullary constructor on the stack (Promise doesn't have a nullary constructor).
  Additionally, I believe it would be difficult or impossible to detect when a
  promise is cancelled/moved if we use a bare Promise.
- Every promise returns a Owned PyRefCounter. PyRefCounter makes sure that a
  reference to the returned object keeps existing until the promise is fulfilled
  or cancelled. Previously, this was attempted using attach, which is redundant
  and makes reasoning about PyINCREF and PyDECREF very difficult.
- Because a promise holds a Own<Promise<...>>, when we perform any kind of
  action on that promise (a_wait, then, ...), we have to explicitly move() the
  ownership around. This will leave the original promise with a NULL-pointer,
  which we can easily detect as a cancelled promise.

Promises now only hold references to their 'parents' when strictly needed. This
should reduce memory pressure.

* Simplify and test the promise joining functionality

* Attach forgotten parent

* Catch exceptions in add_reader and friends

* Further cleanup of memory leaks

* Get rid of a_wait() in examples

* Cancel all fd read operations when the python asyncio loop is closed

* Formatting

* Remove support for capnp < 7000

* Bring asyncProvider.cpp more in line with upstream async-io-unix.c++

It was originally copied from the nodejs implementation, which in turn copied
from async-io-unix.c++. But that copy is pretty old.

* Fix a bug that caused file descriptors to never be closed

* Implement AsyncIoStream based on Python transports and protocols

* Get rid of asyncProvider

All asyncio now goes through _AsyncIoStream

* Formatting

* Add __dict__ to  PyAsyncIoStreamProtocol for python 3.7

* Reintroduce strange ipv4/ipv6 selection code to make ci happy

* Extra pause_reading()

* Work around more python bugs

* Be careful to only close transport when this is still possible

* Move pause_reading() workaround
2023-06-06 11:08:15 -07:00
Jacob Alexander
5061cdd1ee Fix black formatting 2023-03-12 11:33:41 -07:00
Madhava Jay
9c4f80ad21 Fixed linting issues
- re-enabled lint checks
2022-05-24 11:42:53 +10:00
Madhava Jay
26de61d847 Merge branch 'fix-mmap-buf' into madhava/python_310 2022-05-24 10:42:57 +10:00
Madhava Jay
ea70dac5cb Fixed black issues
- Disabled flake8 checks from blocking CI
- Added python 3.10 to packaging and manylinux
2022-04-06 13:32:52 +10:00
Bernhard Liebl
e2f3e5da46 adapted test cases to:
- work with contextual from_bytes()
- add regression test against original issue with buf release (see test_roundtrip_bytes_buffer)
2022-01-18 12:13:12 +01:00
Bernhard Liebl
c8c168fdd0 add test case for buffer release issue 2022-01-18 12:04:01 +01:00
Jacob Alexander
6e7fffd7de Applying black formatting
- Fixing flake8 configuration to agree with black
- Adding black validation check to github actions
2021-10-01 11:12:21 -07:00
John Vandenberg
4797654323 remove_event_loop: Allow ignoring specific errors
Replaces a bare except with Exception, and updates the
test case to specify only a single exception that is
allowed to occur.

Related to https://github.com/capnproto/pycapnp/issues/254
2021-06-01 16:30:35 +08:00
John Vandenberg
0f6df849cd Refine exception invoking which on non-union type
Related to https://github.com/capnproto/pycapnp/issues/254
2021-06-01 15:40:35 +08:00
John Vandenberg
c179c72087 Allow enum to be used with .init 2021-05-31 19:31:53 +08:00
John Vandenberg
1795cac230 _StructModuleWhich: Use enum 2021-05-31 18:57:40 +08:00
John Vandenberg
0c904443bd Add Union on top level union messages
Closes https://github.com/capnproto/pycapnp/issues/247
2021-05-31 16:33:10 +08:00
John Vandenberg
8c15feb4eb Remove many lint warnings
Mostly whitespace changes, with max-line-length set to 120
and double-quotes used, adopting black settings.

Related to https://github.com/capnproto/pycapnp/issues/128
2021-05-03 17:02:30 +08:00
Jacob Alexander
a4ef16e831 Fixing flake8 linting errors
- Enabling builds to fail on simple lint failures now
2020-11-20 23:38:13 -08:00
Jacob Alexander
3de13c3718 Updating simple example servers to not use run_forever (can't Ctrl+c)
- Adding xfail to simple servers as they tend to not like certain
  versions of python when called repeated for testing
2020-11-20 00:17:52 -08:00
Jacob Alexander
5e8ccba536 Updating docs for v1.0.0b2
- Full cleanup of all the docs
- General sphinx housekeeping
- Updated all the old/bad links
- More reliable tests
- Updated Changelog
- Removed dead/deprecated code
- Added documentation generation test
2020-06-14 17:05:45 -07:00
Jacob Alexander
6532ca571d Adding SSL version of the calculator example
- Used to test asyncio SSL on Windows mainly
- Skipping asyncio thread tests on Windows (due to getTimer wrapper bug
  on Windows)
2020-06-10 14:33:45 -07:00
Jacob Alexander
255119b838 Updating to capnproto v0.8.0
- Includes some test stabilization
- Fixes manylinux2010 build issues (linker flag order due to old gcc)
- More rigorous python setup.py clean
- Requires capnproto v0.8.0 or greater
- Including system libcapnp include path for import (e.g. import
  stream_capnp)
- Bundle libcapnp .capnp files when not using system libcapnp
- Removing more distutils usage. Now using pkg-config to determine the
  system version of libcapnp (mainly for Linux, but should work on macOS
  with brew)
- Removed dead code

Resolves issues #215 #216 #217
Lots of fixes for Issue #218 (all sorts of retry methods needed for
GitHub Actions)
2020-06-08 11:49:17 -07:00
Andrey Cizov
d94d60db8b get rid of all warnings during tests 2019-12-26 22:25:56 -08:00
Jacob Alexander
fc75e4083d Fixing remaining flake8 lint warnings 2019-12-11 22:44:44 -08:00
Jacob Alexander
362cce345b Fixing Windows tests
- Adding import path filter to exclude non-directories
  Otherwise kj will through exceptions
- Skipped AF_UNIX socket test
- Use default socket configuration when it doesn't matter the type of
socket used
- Open files with utf8 encoding (needed for text validation)
- Explictly call python executable when running external scripts
- Fix path creation to always use os.path.join
- Added timeout to client wait in some tests

- Some broken tests still remain (most likely asyncio related)
2019-10-19 00:26:55 -07:00
Jacob Alexander
7789ebbf96 Fixing flake8 linting errors 2019-10-15 01:08:11 -07:00