Merge pull request #285 from madhavajay/madhava/python_310

Add Python 3.10 support
This commit is contained in:
Jacob Alexander
2022-05-23 21:08:24 -07:00
committed by GitHub
15 changed files with 90 additions and 64 deletions

View File

@@ -9,7 +9,7 @@ jobs:
container: ${{ matrix.container-image }} container: ${{ matrix.container-image }}
strategy: strategy:
matrix: matrix:
python-version: ['cp37-cp37m', 'cp38-cp38', 'cp39-cp39'] python-version: ['cp37-cp37m', 'cp38-cp38', 'cp39-cp39', 'cp310-cp310']
container-image: ['quay.io/pypa/manylinux2010_x86_64', 'quay.io/pypa/manylinux2010_i686'] container-image: ['quay.io/pypa/manylinux2010_x86_64', 'quay.io/pypa/manylinux2010_i686']
steps: steps:
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ['cp37-cp37m', 'cp38-cp38', 'cp39-cp39'] python-version: ['cp37-cp37m', 'cp38-cp38', 'cp39-cp39', 'cp310-cp310']
fail-fast: false fail-fast: false
env: env:
py: /opt/python/${{ matrix.python-version }}/bin/python py: /opt/python/${{ matrix.python-version }}/bin/python

View File

@@ -12,7 +12,7 @@ jobs:
matrix: matrix:
# Some asyncio commands require 3.7+ # Some asyncio commands require 3.7+
# It may be possible to use 3.6 and maybe 3.5; however, this will take some patching to get examples to work # It may be possible to use 3.6 and maybe 3.5; however, this will take some patching to get examples to work
python-version: [3.7, 3.8, 3.9] python-version: [3.7, 3.8, 3.9, "3.10"]
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
steps: steps:

View File

@@ -35,13 +35,12 @@ def writeAddressBook():
def printAddressBook(msg_bytes): def printAddressBook(msg_bytes):
addressBook = addressbook.AddressBook.from_bytes(msg_bytes) with addressbook.AddressBook.from_bytes(msg_bytes) as addressBook:
for person in addressBook.people:
for person in addressBook.people: print(person.name, ":", person.email)
print(person.name, ":", person.email) for phone in person.phones:
for phone in person.phones: print(phone.type, ":", phone.number)
print(phone.type, ":", phone.number) print()
print()
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -40,12 +40,11 @@ def writeAddressBook():
@profile @profile
def printAddressBook(msg_bytes): def printAddressBook(msg_bytes):
addressBook = addressbook.AddressBook.from_bytes(msg_bytes) with addressbook.AddressBook.from_bytes(msg_bytes) as addressBook:
for person in addressBook.people:
for person in addressBook.people: person.name, person.email
person.name, person.email for phone in person.phones:
for phone in person.phones: phone.type, phone.number
phone.type, phone.number
@profile @profile

View File

@@ -4,8 +4,8 @@ from common import rand_int, rand_double, rand_bool, from_bytes_helper
from random import choice from random import choice
import eval_pb2 import eval_pb2
MAX_INT = 2 ** 31 - 1 MAX_INT = 2**31 - 1
MIN_INT = -(2 ** 31) MIN_INT = -(2**31)
OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"] OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"]

View File

@@ -5,8 +5,8 @@ import eval_capnp
from common import rand_int, rand_double, rand_bool from common import rand_int, rand_double, rand_bool
from random import choice from random import choice
MAX_INT = 2 ** 31 - 1 MAX_INT = 2**31 - 1
MIN_INT = -(2 ** 31) MIN_INT = -(2**31)
OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"] OPERATIONS = ["add", "subtract", "multiply", "divide", "modulus"]

View File

@@ -22,6 +22,7 @@ from libc.string cimport memcpy
import array import array
import asyncio import asyncio
import collections as _collections import collections as _collections
import contextlib
import enum as _enum import enum as _enum
import inspect as _inspect import inspect as _inspect
import os as _os import os as _os
@@ -1098,7 +1099,8 @@ if getattr(_sys, 'subversion', [''])[0] == 'PyPy':
from pickle_helper import _struct_reducer from pickle_helper import _struct_reducer
else: else:
def _struct_reducer(schema_id, data): def _struct_reducer(schema_id, data):
return _global_schema_parser.modules_by_id[schema_id].from_bytes(data) with _global_schema_parser.modules_by_id[schema_id].from_bytes(data) as msg:
return msg
cdef class _DynamicStructReader: cdef class _DynamicStructReader:
@@ -3320,6 +3322,7 @@ class _StructModule(object):
reader = _MultipleBytesPackedMessageReader(buf, self.schema, traversal_limit_in_words, nesting_limit) reader = _MultipleBytesPackedMessageReader(buf, self.schema, traversal_limit_in_words, nesting_limit)
return reader return reader
@contextlib.contextmanager
def from_bytes(self, buf, traversal_limit_in_words=None, nesting_limit=None, builder=False): def from_bytes(self, buf, traversal_limit_in_words=None, nesting_limit=None, builder=False):
"""Returns a Reader for the unpacked object in buf. """Returns a Reader for the unpacked object in buf.
@@ -3340,13 +3343,18 @@ class _StructModule(object):
:rtype: :class:`_DynamicStructReader` or :class:`_DynamicStructBuilder` :rtype: :class:`_DynamicStructReader` or :class:`_DynamicStructBuilder`
""" """
if builder: message = None
# message = _FlatMessageBuilder(buf) try:
message = _FlatArrayMessageReader(buf, traversal_limit_in_words, nesting_limit) if builder:
return message.get_root(self.schema).as_builder() # message = _FlatMessageBuilder(buf)
else: message = _FlatArrayMessageReader(buf, traversal_limit_in_words, nesting_limit)
message = _FlatArrayMessageReader(buf, traversal_limit_in_words, nesting_limit) yield message.get_root(self.schema).as_builder()
return message.get_root(self.schema) else:
message = _FlatArrayMessageReader(buf, traversal_limit_in_words, nesting_limit)
yield message.get_root(self.schema)
finally:
if message:
message.close()
def from_segments(self, segments, traversal_limit_in_words=None, nesting_limit=None): def from_segments(self, segments, traversal_limit_in_words=None, nesting_limit=None):
"""Returns a Reader for a list of segment bytes. """Returns a Reader for a list of segment bytes.
@@ -4088,15 +4096,22 @@ cdef class _AlignedBuffer:
cdef class _BufferView: cdef class _BufferView:
cdef Py_buffer view cdef Py_buffer view
cdef char * buf cdef char * buf
cdef int closed
def __init__(self, other): def __init__(self, other):
cdef int ret = PyObject_GetBuffer(other, &self.view, PyBUF_SIMPLE) cdef int ret = PyObject_GetBuffer(other, &self.view, PyBUF_SIMPLE)
if ret < 0: if ret < 0:
raise ValueError("Invalid buffer passed to BufferView") raise ValueError("Invalid buffer passed to BufferView")
self.buf = <char*>self.view.buf self.buf = <char*>self.view.buf
self.closed = False
def close(self):
if not self.closed:
PyBuffer_Release(&self.view)
self.closed = True
def __dealloc__(self): def __dealloc__(self):
PyBuffer_Release(&self.view) self.close()
@cython.internal @cython.internal
@@ -4133,6 +4148,7 @@ cdef class _FlatArrayMessageReaderAligned(_MessageReader):
@cython.internal @cython.internal
cdef class _FlatArrayMessageReader(_MessageReader): cdef class _FlatArrayMessageReader(_MessageReader):
cdef object _object_to_pin cdef object _object_to_pin
cdef _BufferView _buffer_view
def __init__(self, buf, traversal_limit_in_words=None, nesting_limit=None): def __init__(self, buf, traversal_limit_in_words=None, nesting_limit=None):
cdef schema_cpp.ReaderOptions opts = make_reader_opts(traversal_limit_in_words, nesting_limit) cdef schema_cpp.ReaderOptions opts = make_reader_opts(traversal_limit_in_words, nesting_limit)
@@ -4151,10 +4167,12 @@ cdef class _FlatArrayMessageReader(_MessageReader):
self._object_to_pin = aligned self._object_to_pin = aligned
else: else:
self._object_to_pin = buf self._object_to_pin = buf
self._buffer_view = None
elif PyObject_CheckBuffer(buf): elif PyObject_CheckBuffer(buf):
view = _BufferView(buf) view = _BufferView(buf)
ptr = view.buf ptr = view.buf
self._object_to_pin = view self._object_to_pin = view
self._buffer_view = view
else: else:
raise TypeError('expected buffer-like object in FlatArrayMessageReader') raise TypeError('expected buffer-like object in FlatArrayMessageReader')
@@ -4162,7 +4180,12 @@ cdef class _FlatArrayMessageReader(_MessageReader):
schema_cpp.WordArrayPtr(<schema_cpp.word*>ptr, sz//8), schema_cpp.WordArrayPtr(<schema_cpp.word*>ptr, sz//8),
opts) opts)
def close(self):
if self._buffer_view:
self._buffer_view.close()
def __dealloc__(self): def __dealloc__(self):
self.close()
del self.thisptr del self.thisptr

View File

@@ -3,4 +3,5 @@ import capnp
def _struct_reducer(schema_id, data): 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)' 'Hack to deal with pypy not allowing reduce functions to be "built-in" methods (ie. compiled from a .pyx)'
return capnp._global_schema_parser.modules_by_id[schema_id].from_bytes(data) with capnp._global_schema_parser.modules_by_id[schema_id].from_bytes(data) as msg:
return msg

View File

@@ -51,8 +51,8 @@ source_suffix = ".rst"
master_doc = "index" master_doc = "index"
# General information about the project. # General information about the project.
project = u"capnp" project = "capnp"
copyright = u"2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander)" copyright = "2013-2019 (Jason Paryani), 2019-2020 (Jacob Alexander)"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@@ -204,7 +204,7 @@ latex_elements = {}
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [ latex_documents = [
("index", "capnp.tex", u"capnp Documentation", u"Author", "manual"), ("index", "capnp.tex", "capnp Documentation", "Author", "manual"),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@@ -232,7 +232,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [("index", "capnp", u"capnp Documentation", [u"Author"], 1)] man_pages = [("index", "capnp", "capnp Documentation", ["Author"], 1)]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
# man_show_urls = False # man_show_urls = False
@@ -247,8 +247,8 @@ texinfo_documents = [
( (
"index", "index",
"capnp", "capnp",
u"capnp Documentation", "capnp Documentation",
u"Author", "Author",
"capnp", "capnp",
"One line description of project.", "One line description of project.",
"Miscellaneous", "Miscellaneous",
@@ -268,10 +268,10 @@ texinfo_documents = [
# -- Options for Epub output --------------------------------------------------- # -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info. # Bibliographic Dublin Core info.
epub_title = u"capnp" epub_title = "capnp"
epub_author = u"Author" epub_author = "Author"
epub_publisher = u"Author" epub_publisher = "Author"
epub_copyright = u"2013, Author" epub_copyright = "2013, Author"
# The language of the text. It defaults to the language option # The language of the text. It defaults to the language option
# or en if the language is not set. # or en if the language is not set.

View File

@@ -20,13 +20,13 @@ class ExampleImpl(thread_capnp.Example.Server):
def subscribeStatus(self, subscriber, **kwargs): def subscribeStatus(self, subscriber, **kwargs):
return ( return (
capnp.getTimer() capnp.getTimer()
.after_delay(10 ** 9) .after_delay(10**9)
.then(lambda: subscriber.status(True)) .then(lambda: subscriber.status(True))
.then(lambda _: self.subscribeStatus(subscriber)) .then(lambda _: self.subscribeStatus(subscriber))
) )
def longRunning(self, **kwargs): def longRunning(self, **kwargs):
return capnp.getTimer().after_delay(1 * 10 ** 9) return capnp.getTimer().after_delay(1 * 10**9)
class Server: class Server:

View File

@@ -24,13 +24,13 @@ class ExampleImpl(thread_capnp.Example.Server):
def subscribeStatus(self, subscriber, **kwargs): def subscribeStatus(self, subscriber, **kwargs):
return ( return (
capnp.getTimer() capnp.getTimer()
.after_delay(10 ** 9) .after_delay(10**9)
.then(lambda: subscriber.status(True)) .then(lambda: subscriber.status(True))
.then(lambda _: self.subscribeStatus(subscriber)) .then(lambda _: self.subscribeStatus(subscriber))
) )
def longRunning(self, **kwargs): def longRunning(self, **kwargs):
return capnp.getTimer().after_delay(1 * 10 ** 9) return capnp.getTimer().after_delay(1 * 10**9)
def alive(self, **kwargs): def alive(self, **kwargs):
return True return True

View File

@@ -14,13 +14,13 @@ class ExampleImpl(thread_capnp.Example.Server):
def subscribeStatus(self, subscriber, **kwargs): def subscribeStatus(self, subscriber, **kwargs):
return ( return (
capnp.getTimer() capnp.getTimer()
.after_delay(10 ** 9) .after_delay(10**9)
.then(lambda: subscriber.status(True)) .then(lambda: subscriber.status(True))
.then(lambda _: self.subscribeStatus(subscriber)) .then(lambda _: self.subscribeStatus(subscriber))
) )
def longRunning(self, **kwargs): def longRunning(self, **kwargs):
return capnp.getTimer().after_delay(1 * 10 ** 9) return capnp.getTimer().after_delay(1 * 10**9)
def parse_args(): def parse_args():

View File

@@ -13,7 +13,8 @@ import test_capnp # noqa: E402
def decode(name): def decode(name):
class_name = name[0].upper() + name[1:] class_name = name[0].upper() + name[1:]
print(getattr(test_capnp, class_name).from_bytes(sys.stdin.read())._short_str()) with getattr(test_capnp, class_name).from_bytes(sys.stdin.read()) as msg:
print(msg._short_str())
def encode(name): def encode(name):

View File

@@ -46,8 +46,8 @@ def test_roundtrip_bytes(all_types):
test_regression.init_all_types(msg) test_regression.init_all_types(msg)
message_bytes = msg.to_bytes() message_bytes = msg.to_bytes()
msg = all_types.TestAllTypes.from_bytes(message_bytes) with all_types.TestAllTypes.from_bytes(message_bytes) as msg:
test_regression.check_all_types(msg) test_regression.check_all_types(msg)
@pytest.mark.skipif( @pytest.mark.skipif(
@@ -77,8 +77,8 @@ def test_roundtrip_bytes_mmap(all_types):
f.seek(0) f.seek(0)
memory = mmap.mmap(f.fileno(), length) memory = mmap.mmap(f.fileno(), length)
msg = all_types.TestAllTypes.from_bytes(memory) with all_types.TestAllTypes.from_bytes(memory) as msg:
test_regression.check_all_types(msg) test_regression.check_all_types(msg)
@pytest.mark.skipif( @pytest.mark.skipif(
@@ -91,15 +91,16 @@ def test_roundtrip_bytes_buffer(all_types):
b = msg.to_bytes() b = msg.to_bytes()
v = memoryview(b) v = memoryview(b)
try: try:
msg = all_types.TestAllTypes.from_bytes(v) with all_types.TestAllTypes.from_bytes(v) as msg:
test_regression.check_all_types(msg) test_regression.check_all_types(msg)
finally: finally:
v.release() v.release()
def test_roundtrip_bytes_fail(all_types): def test_roundtrip_bytes_fail(all_types):
with pytest.raises(TypeError): with pytest.raises(TypeError):
all_types.TestAllTypes.from_bytes(42) with all_types.TestAllTypes.from_bytes(42) as _:
pass
@pytest.mark.skipif( @pytest.mark.skipif(
@@ -232,14 +233,16 @@ def test_from_bytes_traversal_limit(all_types):
bld.init("structList", size) bld.init("structList", size)
data = bld.to_bytes() data = bld.to_bytes()
msg = all_types.TestAllTypes.from_bytes(data) with all_types.TestAllTypes.from_bytes(data) as msg:
with pytest.raises(capnp.KjException): with pytest.raises(capnp.KjException):
for i in range(0, size): for i in range(0, size):
msg.structList[i].uInt8Field == 0 msg.structList[i].uInt8Field == 0
msg = all_types.TestAllTypes.from_bytes(data, traversal_limit_in_words=2 ** 62) with all_types.TestAllTypes.from_bytes(
for i in range(0, size): data, traversal_limit_in_words=2**62
assert msg.structList[i].uInt8Field == 0 ) as msg:
for i in range(0, size):
assert msg.structList[i].uInt8Field == 0
def test_from_bytes_packed_traversal_limit(all_types): def test_from_bytes_packed_traversal_limit(all_types):
@@ -254,7 +257,7 @@ def test_from_bytes_packed_traversal_limit(all_types):
msg.structList[i].uInt8Field == 0 msg.structList[i].uInt8Field == 0
msg = all_types.TestAllTypes.from_bytes_packed( msg = all_types.TestAllTypes.from_bytes_packed(
data, traversal_limit_in_words=2 ** 62 data, traversal_limit_in_words=2**62
) )
for i in range(0, size): for i in range(0, size):
assert msg.structList[i].uInt8Field == 0 assert msg.structList[i].uInt8Field == 0

View File

@@ -150,9 +150,9 @@ def test_unicode_str(all_types):
msg = all_types.TestAllTypes.new_message() msg = all_types.TestAllTypes.new_message()
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
msg.textField = u"f\u00e6oo".encode("utf-8") msg.textField = "f\u00e6oo".encode("utf-8")
assert msg.textField.decode("utf-8") == u"f\u00e6oo" assert msg.textField.decode("utf-8") == "f\u00e6oo"
else: else:
msg.textField = "f\u00e6oo" msg.textField = "f\u00e6oo"