Skip to content

Commit 302b29c

Browse files
committed
Cythonize ewkb reader and writer
1 parent 03a7354 commit 302b29c

File tree

5 files changed

+63
-33
lines changed

5 files changed

+63
-33
lines changed

benchmark/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from timeit import timeit
2+
3+
from postgis import Point
4+
5+
6+
def main():
7+
point = Point([1.23456789, 48.765432])
8+
ewkb = point.to_ewkb()
9+
time = timeit('read(ewkb)',
10+
setup='from postgis.ewkb import read',
11+
number=1000000, globals=locals())
12+
print("Read Point", time)
13+
time = timeit('point.to_ewkb()',
14+
number=1000000, globals=locals())
15+
print("Write Point", time)
16+
17+
18+
if __name__ == '__main__':
19+
main()

circle.yml

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
machine:
2-
python:
3-
version: 3.6.1
2+
python:
3+
version: 3.6.1
44

55
database:
6-
pre:
7-
- psql -U ubuntu -c "CREATE DATABASE test;"
8-
- psql -U ubuntu -c "create extension postgis" -d test
6+
pre:
7+
- psql -U ubuntu -c "CREATE DATABASE test;"
8+
- psql -U ubuntu -c "create extension postgis" -d test
99

1010
test:
11-
override:
12-
- py.test tests/
11+
override:
12+
- py.test tests/
1313

1414
dependencies:
15-
pre:
16-
- pip install pytest psycopg2 asyncpg cython
15+
override:
16+
- python setup.py develop
17+
pre:
18+
- pip install -U pip setuptools pytest psycopg2 asyncpg cython

postgis/ewkb.py renamed to postgis/ewkb.pyx

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import binascii
22
from io import BytesIO
33
import struct
4+
from cpython cimport bool
45

56

67
class Typed(type):
@@ -20,17 +21,23 @@ def __call__(cls, *args, **kwargs):
2021
return super().__call__(*args, **kwargs)
2122

2223

23-
class Reader:
24+
cdef class Reader:
2425

25-
__slots__ = ['stream', 'endianness', 'has_z', 'has_m']
26+
cdef object stream
27+
cdef bytes endianness
28+
cdef public bool has_z
29+
cdef public bool has_m
2630

27-
def __init__(self, stream):
31+
def __cinit__(self, object stream):
2832
self.stream = stream
2933

30-
def clone(self):
34+
cpdef clone(self):
3135
return type(self)(self.stream)
3236

33-
def read(self):
37+
cpdef read(self):
38+
return self._read()
39+
40+
cdef _read(self):
3441
# https://en.wikipedia.org/wiki/Well-known_text#Well-known_binary
3542
byte_order = self.stream.read(1)
3643
if byte_order == b'\x00':
@@ -53,22 +60,22 @@ def read(self):
5360
else:
5461
return class_.from_ewkb_body(self, srid)
5562

56-
def read_int(self):
63+
cpdef read_int(self):
5764
return struct.unpack(self.endianness + b'I', self.stream.read(4))[0]
5865

59-
def read_double(self):
66+
cpdef read_double(self):
6067
return struct.unpack(self.endianness + b'd', self.stream.read(8))[0]
6168

62-
@classmethod
63-
def from_hex(cls, value):
64-
return cls(BytesIO(binascii.a2b_hex(value))).read()
69+
70+
cpdef read(str value):
71+
return Reader(BytesIO(binascii.a2b_hex(value))).read()
6572

6673

67-
class Writer:
74+
cdef class Writer:
6875

69-
__slots__ = ['stream']
76+
cdef object stream
7077

71-
def __init__(self, geometry, stream=None):
78+
def __cinit__(self, object geometry, object stream=None):
7279
self.stream = stream or BytesIO()
7380
try:
7481
type_ = geometry.TYPE
@@ -85,17 +92,17 @@ def __init__(self, geometry, stream=None):
8592
if geometry.has_srid:
8693
self.write_int(geometry.srid)
8794

88-
def write_int(self, value):
95+
cpdef write_int(self, value):
8996
self.stream.write(struct.pack(b'<I', value))
9097

91-
def write_double(self, value):
98+
cpdef write_double(self, value):
9299
self.stream.write(struct.pack(b'<d', value))
93100

94-
def clone(self, geometry):
101+
cpdef clone(self, object geometry):
95102
return type(self)(geometry, self.stream)
96103

97-
@classmethod
98-
def to_hex(cls, value):
99-
writer = cls(value)
100-
value.write_ewkb_body(writer)
101-
return binascii.b2a_hex(writer.stream.getvalue()).upper()
104+
105+
cpdef bytes write(object value):
106+
writer = Writer(value)
107+
value.write_ewkb_body(writer)
108+
return binascii.b2a_hex(writer.stream.getvalue()).upper()

postgis/geometry.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
print('psycopg2 not installed')
66

77

8-
from .ewkb import Reader, Typed, Writer
8+
from .ewkb import read, Typed, write
99
from .geojson import GeoJSON
1010

1111

@@ -27,10 +27,10 @@ def has_srid(self):
2727
def from_ewkb(value, cursor=None):
2828
if not value:
2929
return None
30-
return Reader.from_hex(value)
30+
return read(value)
3131

3232
def to_ewkb(self):
33-
return Writer.to_hex(self).decode()
33+
return write(self).decode()
3434

3535
def write_ewkb(self, writer):
3636
self.write_ewkb_body(writer.clone(self))

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from codecs import open # To use a consistent encoding
66
from os import path
77
import sys
8+
# from Cython.Build import cythonize
89

910
HERE = path.abspath(path.dirname(__file__))
1011

@@ -42,6 +43,7 @@ def list_modules(dirname):
4243
ext_modules = [
4344
Extension('postgis.' + ext, [path.join('postgis', ext + '.py')])
4445
for ext in list_modules(path.join(HERE, 'postgis'))]
46+
ext_modules.append(Extension('postgis.ewkb', ['postgis/ewkb.pyx']))
4547

4648
cmdclass = {'build_ext': build_ext}
4749

0 commit comments

Comments
 (0)