Skip to content

Commit 261e469

Browse files
committed
First shot at making pypx more pythoinc.
1 parent f465747 commit 261e469

File tree

11 files changed

+175
-64
lines changed

11 files changed

+175
-64
lines changed

.gitignore

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
build/
2-
pxpy.c
1+
*.pyc
2+
/build/
3+
/pxpy.c
4+
/.pyrex/
5+
/dist/
6+
/pxpy.so
7+
/*.egg-info/

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#
99

1010
all:
11-
python Setup.py build_ext --inplace
11+
python setup.py build_ext --inplace
1212

1313
clean:
1414
rm -f *.c *.o *.so *~ core

pxpy.pyx

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ import datetime
2121

2222
import sys
2323

24+
cdef extern from *:
25+
ctypedef char* const_char_ptr "const char*"
26+
27+
from cpython.mem cimport PyMem_Malloc, PyMem_Free
28+
from cpython.version cimport PY_MAJOR_VERSION
29+
2430
from libc.stdlib cimport *
2531
cdef extern from "stdlib.h" nogil:
2632
void *memset(void *str, int c, size_t n)
@@ -35,6 +41,8 @@ cdef extern from "Python.h":
3541

3642
cdef extern from "string.h":
3743
int strnlen(char *s, int maxlen)
44+
char *strcpy(char *, char *)
45+
3846

3947
cdef extern from "paradox.h":
4048
ctypedef enum fieldtype_t:
@@ -116,13 +124,27 @@ cdef extern from "paradox.h":
116124
ctypedef struct pxpindex_t
117125
ctypedef struct pxstream_t
118126

127+
128+
ctypedef struct Pxval_str:
129+
char *val
130+
int len
131+
ctypedef union Pxval_value:
132+
long lval
133+
double dval
134+
Pxval_str str
135+
ctypedef struct pxval_t:
136+
char isnull
137+
int type
138+
Pxval_value value
139+
140+
119141
pxdoc_t *PX_new()
120-
pxdoc_t* PX_new2(void (*errorhandler)(pxdoc_t *p, int type, char *msg, void *data),
121-
void* (*allocproc)(pxdoc_t *p, size_t size, char *caller),
122-
void* (*reallocproc)(pxdoc_t *p, void *mem, size_t size, char *caller),
142+
pxdoc_t* PX_new2(void (*errorhandler)(pxdoc_t *p, int type, const_char_ptr msg, void *data),
143+
void* (*allocproc)(pxdoc_t *p, size_t size, const_char_ptr caller),
144+
void* (*reallocproc)(pxdoc_t *p, void *mem, size_t size, const_char_ptr caller),
123145
void (*freeproc)(pxdoc_t *p, void *mem))
124146
char *PX_strdup(pxdoc_t *pxdoc, char *str)
125-
int PX_open_file(pxdoc_t *pxdoc, char *filename)
147+
int PX_open_file(pxdoc_t *pxdoc, const_char_ptr filename)
126148
int PX_create_file(pxdoc_t *pxdoc, pxfield_t *px_fields, unsigned int numfields, char *filename, int type)
127149
int PX_read_primary_index(pxdoc_t *pindex)
128150
int PX_add_primary_index(pxdoc_t *pxdoc, pxdoc_t *pindex)
@@ -163,14 +185,15 @@ cdef class PXDoc:
163185
"""
164186

165187
cdef pxdoc_t *doc
166-
cdef char *filename
188+
cdef bytes filename
167189
cdef char isopen
168190

169191
def __init__(self, filename):
170192
"""
171193
Create a PXDoc instance, associated to the given external filename.
172194
"""
173-
195+
if PY_MAJOR_VERSION >= 3 or isinstance(filename, unicode):
196+
filename = filename.encode('utf8')
174197
self.filename = filename
175198
self.doc = PX_new2(&errorhandler, NULL, NULL, NULL)
176199
self.isopen = 0
@@ -179,8 +202,7 @@ cdef class PXDoc:
179202
"""
180203
Open the data file.
181204
"""
182-
183-
if PX_open_file(self.doc, self.filename)<0:
205+
if PX_open_file(self.doc, self.filename) < 0:
184206
raise Exception("Couldn't open `%s`" % self.filename)
185207
self.isopen = 1
186208

@@ -357,7 +379,7 @@ cdef class Table(PXDoc):
357379
Get number of fields in the table.
358380
"""
359381

360-
return self.record.getFieldsCount()
382+
return len(self.record)
361383

362384
def readRecord(self, recno=None):
363385
"""
@@ -376,17 +398,36 @@ cdef class Table(PXDoc):
376398
"""
377399

378400
if not recno:
379-
recno = self.current_recno+1
401+
recno = self.current_recno + 1
380402
else:
381403
self.current_recno = recno
382404

383-
if recno>=self.doc.px_head.px_numrecords:
405+
if recno >= self.doc.px_head.px_numrecords:
384406
return False
385407

386408
self.current_recno = recno
387409

388410
return self.record.read(recno)
389411

412+
def __iter__(self):
413+
return self
414+
415+
def __next__(self):
416+
ok = self.readRecord()
417+
if not ok:
418+
self.current_recno = -1
419+
raise StopIteration()
420+
return self.record
421+
422+
def __getitem__(self, key):
423+
if key >= self.doc.px_head.px_numrecords:
424+
raise IndexError()
425+
self.record.read(key)
426+
return self.record
427+
428+
def __len__(self):
429+
return self.doc.px_head.px_numrecords
430+
390431
def append(self, values):
391432
cdef char *b
392433
l = len(self.fields)
@@ -629,6 +670,7 @@ cdef class Record:
629670
"""
630671

631672
cdef void *data
673+
cdef int current_fieldno
632674
cdef Table table
633675
cdef public fields
634676

@@ -643,15 +685,17 @@ cdef class Record:
643685
self.data = table.doc.malloc(table.doc,
644686
table.doc.px_head.px_recordsize,
645687
"Memory for record")
688+
self.current_fieldno = -1
689+
646690
self.table = table
647691
self.fields = []
648692
offset = 0
649-
for i in range(self.getFieldsCount()):
693+
for i in range(len(self)):
650694
field = RecordField(self, i, offset)
651695
self.fields.append(field)
652-
offset = offset + table.doc.px_head.px_fields[i].px_flen
696+
offset = offset + table.doc.px_head.px_fields[i].px_flen
653697

654-
def getFieldsCount(self):
698+
def __len__(self):
655699
"""
656700
Get number of fields in the record.
657701
"""
@@ -667,3 +711,18 @@ cdef class Record:
667711
self.table.filename))
668712
return True
669713

714+
def __iter__(self):
715+
return self
716+
717+
def __next__(self):
718+
fieldno = self.current_fieldno + 1
719+
try:
720+
field = self.fields[fieldno]
721+
self.current_fieldno = fieldno
722+
return field
723+
except IndexError:
724+
self.current_fieldno = -1
725+
raise StopIteration()
726+
727+
def __getitem__(self, key):
728+
return self.fields[key]

setup.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
1-
# -*- Python -*- -*- coding: iso-8859-1 -*-
1+
# -*- coding: utf-8 -*-
22
# :Project: pxpy -- Module setup
33
# :Source: $Source: /cvsroot/pxlib/bindings/python/Setup.py,v $
44
# :Created: Sun, Apr 04 2004 00:14:12 CEST
55
# :Author: Lele Gaifax <[email protected]>
66
# :Revision: $Revision: 1.3 $ by $Author: lele $
77
# :Date: $Date: 2004/07/19 23:04:35 $
88
#
9+
import os
10+
import sys
911

10-
from distutils.core import setup
11-
from distutils.extension import Extension
12+
sys.path.append(os.path.join(os.path.dirname(__file__), '.pyrex'))
13+
14+
from setuptools import setup, Extension
15+
from distutils.command.clean import clean as _clean
16+
from distutils import log
1217
from Cython.Distutils import build_ext
1318

1419

15-
setup(
16-
name = 'python-pxlib',
17-
description = "Python wrapper around pxlib",
18-
version = '0.0.1',
19-
author = "Lele Gaifax",
20-
author_email = "[email protected]",
21-
url = "http://pxlib.sourceforge.net/",
20+
class clean(_clean):
21+
"""
22+
Subclass clean so it removes all the Cython generated C files.
23+
"""
24+
25+
def run(self):
26+
super(_clean, self).run()
27+
for ext in self.distribution.ext_modules:
28+
cy_sources = [s for s in ext.sources if s.endswith('.pyx')]
29+
for cy_source in cy_sources:
30+
c_source = cy_source[:-3] + 'c'
31+
if os.path.exists(c_source):
32+
log.info('removing %s', c_source)
33+
os.remove(c_source)
2234

23-
ext_modules=[
24-
Extension("pxpy", ["pxpy.pyx"],
25-
# Uncomment, to use current version
26-
#include_dirs=["../../pxlib/include/"],
27-
#library_dirs=["../../pxlib/src/.libs"],
28-
libraries=["px"]),
35+
setup(
36+
name='python-pxlib',
37+
description="Python wrapper around pxlib",
38+
version='0.0.1',
39+
author="Lele Gaifax",
40+
author_email = "[email protected]",
41+
url = "http://pxlib.sourceforge.net/",
42+
test_suite='tests',
43+
cmdclass={
44+
'build_ext': build_ext,
45+
'clean': clean
46+
},
47+
data_files=[
48+
('', ['pxpy.pyx'])
2949
],
30-
cmdclass = {'build_ext': build_ext}
31-
)
50+
zip_safe=False,
51+
setup_requires=["Cython>=0.13"],
52+
ext_modules=[
53+
Extension('pxpy', ['pxpy.pyx'],
54+
libraries=['px']),
55+
],
56+
)

test.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

tests/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#! /usr/bin/python
2+
# -*- mode: python; coding: utf-8 -*-
3+
# :Project: pxpy -- silly tester
4+
# :Source: $Source: /cvsroot/pxlib/bindings/python/test.py,v $
5+
# :Created: Thu, May 13 2004 01:48:30 CEST
6+
# :Author: Lele Gaifax <[email protected]>
7+
# :Revision: $Revision: 1.2 $ by $Author: lele $
8+
# :Date: $Date: 2004/07/19 14:38:04 $
9+
#
10+
import unittest
11+
import os.path
12+
import pxpy
13+
14+
FIXTURE_DIR = os.path.join(os.path.dirname(__file__), 'fixtures')
15+
16+
class PxLibTest(unittest.TestCase):
17+
def test_iteration(self):
18+
table = pxpy.Table(os.path.join(FIXTURE_DIR, 'LAND.DB'))
19+
table.open()
20+
21+
self.assertEqual(table.getFieldsCount(), 9)
22+
self.assertEqual(len(table), 216)
23+
try:
24+
table[216]
25+
except IndexError:
26+
pass
27+
else:
28+
self.fail()
29+
try:
30+
table[0][10]
31+
except IndexError:
32+
pass
33+
else:
34+
self.fail()
35+
36+
afg = table[0][2]
37+
self.assertEquals(afg.getValue(), 'Afghanistan')
38+
self.assertEquals(afg.fname, 'Land_navn')
39+
40+
for i, record in enumerate(table):
41+
for j, field in enumerate(record):
42+
same_field = table[i][j]
43+
self.assertEquals(field.getValue(), same_field.getValue())
44+
self.assertEquals(field.fname, same_field.fname)
45+
table.close()
46+
47+
48+
if __name__ == "__main__":
49+
unittest.main()
50+
51+

tests/fixtures/KOMMENT.DB

50 KB
Binary file not shown.

tests/fixtures/KOMMENT.MB

52 KB
Binary file not shown.

tests/fixtures/KOMMENT.PX

4 KB
Binary file not shown.

tests/fixtures/LAND.DB

22 KB
Binary file not shown.

0 commit comments

Comments
 (0)