Skip to content

Commit

Permalink
Cythonize ewkb reader and writer
Browse files Browse the repository at this point in the history
  • Loading branch information
yohanboniface committed Aug 28, 2017
1 parent aac4940 commit f6314ab
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 33 deletions.
19 changes: 19 additions & 0 deletions benchmark/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from timeit import timeit

from postgis import Point


def main():
point = Point([1.23456789, 48.765432])
ewkb = point.to_ewkb()
time = timeit('read(ewkb)',
setup='from postgis.ewkb import read',
number=1000000, globals=locals())
print("Read Point", time)
time = timeit('point.to_ewkb()',
number=1000000, globals=locals())
print("Write Point", time)


if __name__ == '__main__':
main()
20 changes: 11 additions & 9 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
machine:
python:
version: 3.6.1
python:
version: 3.6.1

database:
pre:
- psql -U ubuntu -c "CREATE DATABASE test;"
- psql -U ubuntu -c "create extension postgis" -d test
pre:
- psql -U ubuntu -c "CREATE DATABASE test;"
- psql -U ubuntu -c "create extension postgis" -d test

test:
override:
- py.test tests/
override:
- py.test tests/

dependencies:
pre:
- pip install pytest psycopg2 asyncpg cython
override:
- python setup.py develop
pre:
- pip install -U pip setuptools pytest psycopg2 asyncpg cython
49 changes: 28 additions & 21 deletions postgis/ewkb.py → postgis/ewkb.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import binascii
from io import BytesIO
import struct
from cpython cimport bool


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


class Reader:
cdef class Reader:

__slots__ = ['stream', 'endianness', 'has_z', 'has_m']
cdef object stream
cdef bytes endianness
cdef public bool has_z
cdef public bool has_m

def __init__(self, stream):
def __cinit__(self, object stream):
self.stream = stream

def clone(self):
cpdef clone(self):
return type(self)(self.stream)

def read(self):
cpdef read(self):
return self._read()

cdef _read(self):
# https://en.wikipedia.org/wiki/Well-known_text#Well-known_binary
byte_order = self.stream.read(1)
if byte_order == b'\x00':
Expand All @@ -53,22 +60,22 @@ def read(self):
else:
return class_.from_ewkb_body(self, srid)

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

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

@classmethod
def from_hex(cls, value):
return cls(BytesIO(binascii.a2b_hex(value))).read()

cpdef read(str value):
return Reader(BytesIO(binascii.a2b_hex(value))).read()


class Writer:
cdef class Writer:

__slots__ = ['stream']
cdef object stream

def __init__(self, geometry, stream=None):
def __cinit__(self, object geometry, object stream=None):
self.stream = stream or BytesIO()
try:
type_ = geometry.TYPE
Expand All @@ -85,17 +92,17 @@ def __init__(self, geometry, stream=None):
if geometry.has_srid:
self.write_int(geometry.srid)

def write_int(self, value):
cpdef write_int(self, value):
self.stream.write(struct.pack(b'<I', value))

def write_double(self, value):
cpdef write_double(self, value):
self.stream.write(struct.pack(b'<d', value))

def clone(self, geometry):
cpdef clone(self, object geometry):
return type(self)(geometry, self.stream)

@classmethod
def to_hex(cls, value):
writer = cls(value)
value.write_ewkb_body(writer)
return binascii.b2a_hex(writer.stream.getvalue()).upper()

cpdef bytes write(object value):
writer = Writer(value)
value.write_ewkb_body(writer)
return binascii.b2a_hex(writer.stream.getvalue()).upper()
6 changes: 3 additions & 3 deletions postgis/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
warnings.warn('psycopg2 not installed', ImportWarning)


from .ewkb import Reader, Typed, Writer
from .ewkb import read, Typed, write
from .geojson import GeoJSON


Expand All @@ -29,10 +29,10 @@ def has_srid(self):
def from_ewkb(value, cursor=None):
if not value:
return None
return Reader.from_hex(value)
return read(value)

def to_ewkb(self):
return Writer.to_hex(self).decode()
return write(self).decode()

def write_ewkb(self, writer):
self.write_ewkb_body(writer.clone(self))
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from codecs import open # To use a consistent encoding
from os import path
import sys
# from Cython.Build import cythonize

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

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

cmdclass = {'build_ext': build_ext}

Expand Down

0 comments on commit f6314ab

Please sign in to comment.