From 627719cae59bbcc47149e9390da9de196eb0f0c2 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 09:08:10 +0200 Subject: [PATCH 01/37] Vectorize all d2dtf() imputs. For later automation, it is easier to have all inputs vectorized. --- cython_numpy/erfa.pyx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cython_numpy/erfa.pyx b/cython_numpy/erfa.pyx index 6504f38..01abb66 100644 --- a/cython_numpy/erfa.pyx +++ b/cython_numpy/erfa.pyx @@ -74,14 +74,16 @@ def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, php def d2dtf(scale, ndp, d1, d2): - shape = np.broadcast(d1, d2).shape + shape = np.broadcast(scale, ndp, d1, d2).shape iy = np.empty(shape, dtype=np.int) im = np.empty(shape, dtype=np.int) id = np.empty(shape, dtype=np.int) ihmsf = np.empty(shape, dtype=[('h','i'),('m','i'),('s','i'),('f','i')]) - cdef np.broadcast it = np.broadcast(d1, d2, iy, im, id, ihmsf) + cdef np.broadcast it = np.broadcast(scale, ndp, d1, d2, iy, im, id, ihmsf) + cdef char *_scale + cdef int _ndp cdef int _iy cdef int _im cdef int _id @@ -89,16 +91,18 @@ def d2dtf(scale, ndp, d1, d2): while np.PyArray_MultiIter_NOTDONE(it): - _d1 = (np.PyArray_MultiIter_DATA(it, 0))[0] - _d2 = (np.PyArray_MultiIter_DATA(it, 1))[0] + _scale = ( np.PyArray_MultiIter_DATA(it, 0)) + _ndp = ( np.PyArray_MultiIter_DATA(it, 1))[0] + _d1 = (np.PyArray_MultiIter_DATA(it, 2))[0] + _d2 = (np.PyArray_MultiIter_DATA(it, 3))[0] - ret = eraD2dtf(scale, ndp, _d1, _d2, &_iy, &_im, &_id, _ihmsf) + ret = eraD2dtf(_scale, _ndp, _d1, _d2, &_iy, &_im, &_id, _ihmsf) - (np.PyArray_MultiIter_DATA(it, 2))[0] = _iy - (np.PyArray_MultiIter_DATA(it, 3))[0] = _im - (np.PyArray_MultiIter_DATA(it, 4))[0] = _id - (np.PyArray_MultiIter_DATA(it, 5))[0:4] = _ihmsf + (np.PyArray_MultiIter_DATA(it, 4))[0] = _iy + (np.PyArray_MultiIter_DATA(it, 5))[0] = _im + (np.PyArray_MultiIter_DATA(it, 6))[0] = _id + (np.PyArray_MultiIter_DATA(it, 7))[0:4] = _ihmsf np.PyArray_MultiIter_NEXT(it) - return iy, im, id, ihmsf + return iy, im, id, ihmsf \ No newline at end of file From b815ba73db86f13006e9176c1a3c3b3590476074 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 09:09:24 +0200 Subject: [PATCH 02/37] Added aper() Just because one of its parameters is an input and an output at the same time. And it also uses the complex eraASTROM structure. --- cython_numpy/erfa.pyx | 71 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/cython_numpy/erfa.pyx b/cython_numpy/erfa.pyx index 01abb66..62a6ca5 100644 --- a/cython_numpy/erfa.pyx +++ b/cython_numpy/erfa.pyx @@ -3,7 +3,8 @@ cimport numpy as np np.import_array() -__all__ = ['atco13', 'd2dtf'] +__all__ = ['atco13', 'd2dtf', 'aper'] + cdef extern from "erfa.h": int eraAtco13(double rc, double dc, @@ -15,10 +16,50 @@ cdef extern from "erfa.h": double *dob, double *rob, double *eo) int eraD2dtf(const char *scale, int ndp, double d1, double d2, int *iy, int *im, int *id, int ihmsf[4]) + void eraAper(double theta, eraASTROM *astrom) + +cdef struct eraASTROM: + double pmt + double eb[3] + double eh[3] + double em + double v[3] + double bm1 + double bpn[3][3] + double along + double phi + double xpl + double ypl + double sphi + double cphi + double diurab + double eral + double refa + double refb + +dt_eraASTROM = np.dtype([('pmt','d'), + ('eb','d',(3,)), + ('eh','d',(3,)), + ('em','d'), + ('v','d',(3,)), + ('bm1 ','d'), + ('bpn','d',(3,3)), + ('along','d'), + ('phi','d'), + ('xpl','d'), + ('ypl','d'), + ('sphi','d'), + ('cphi','d'), + ('diurab','d'), + ('eral','d'), + ('refa','d'), + ('refb','d')], align=True) + #Note: the pattern used here follows https://github.com/cython/cython/wiki/tutorials-numpy#dimensionally-simple-functions + def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl): shape = np.broadcast(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl).shape @@ -72,6 +113,8 @@ def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, php return aob, zob, hob, dob, rob, eo + + def d2dtf(scale, ndp, d1, d2): shape = np.broadcast(scale, ndp, d1, d2).shape @@ -105,4 +148,28 @@ def d2dtf(scale, ndp, d1, d2): np.PyArray_MultiIter_NEXT(it) - return iy, im, id, ihmsf \ No newline at end of file + return iy, im, id, ihmsf + + + +def aper(theta, astrom): + + shape = np.broadcast(theta, astrom).shape + astrom_out = np.empty(shape, dtype=dt_eraASTROM) + np.copyto(astrom_out, astrom) + + cdef np.broadcast it = np.broadcast(theta, astrom_out) + + cdef double _theta + cdef eraASTROM *_astrom + + while np.PyArray_MultiIter_NOTDONE(it): + + _theta = ( np.PyArray_MultiIter_DATA(it, 0))[0] + _astrom = (np.PyArray_MultiIter_DATA(it, 1)) + + eraAper(_theta, _astrom) + + np.PyArray_MultiIter_NEXT(it) + + return astrom_out \ No newline at end of file From f1b7ce9e41dd01388c605891c540eabcc6a977e7 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 09:09:45 +0200 Subject: [PATCH 03/37] Added a test for aper() --- tests/test_erfa.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_erfa.py b/tests/test_erfa.py index 80653bc..9bdd66d 100644 --- a/tests/test_erfa.py +++ b/tests/test_erfa.py @@ -22,3 +22,9 @@ print(iy.shape, ihmsf.shape, ihmsf.dtype) print(ihmsf) +astrom = np.zeros([2],dtype=erfa.dt_eraASTROM) +theta = np.arange(0,10.0) +print(theta.shape) +print(astrom.shape) +astrom = erfa.aper(theta[:,None], astrom[None,:]) +print(astrom) From 32097053d5906264d1aa28d0aa46bb127fb53629 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 09:26:09 +0200 Subject: [PATCH 04/37] Postfixed all output arguments with '_out' --- cython_numpy/erfa.pyx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cython_numpy/erfa.pyx b/cython_numpy/erfa.pyx index 62a6ca5..b0d8030 100644 --- a/cython_numpy/erfa.pyx +++ b/cython_numpy/erfa.pyx @@ -63,14 +63,14 @@ dt_eraASTROM = np.dtype([('pmt','d'), def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl): shape = np.broadcast(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl).shape - aob = np.empty(shape, dtype=np.double) - zob = np.empty(shape, dtype=np.double) - hob = np.empty(shape, dtype=np.double) - dob = np.empty(shape, dtype=np.double) - rob = np.empty(shape, dtype=np.double) - eo = np.empty(shape, dtype=np.double) + aob_out = np.empty(shape, dtype=np.double) + zob_out = np.empty(shape, dtype=np.double) + hob_out = np.empty(shape, dtype=np.double) + dob_out = np.empty(shape, dtype=np.double) + rob_out = np.empty(shape, dtype=np.double) + eo_out = np.empty(shape, dtype=np.double) - cdef np.broadcast it = np.broadcast(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl, aob, zob, hob, dob, rob, eo) + cdef np.broadcast it = np.broadcast(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl, aob_out, zob_out, hob_out, dob_out, rob_out, eo_out) cdef double _aob cdef double _zob @@ -111,19 +111,19 @@ def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, php np.PyArray_MultiIter_NEXT(it) - return aob, zob, hob, dob, rob, eo + return aob_out, zob_out, hob_out, dob_out, rob_out, eo_out def d2dtf(scale, ndp, d1, d2): shape = np.broadcast(scale, ndp, d1, d2).shape - iy = np.empty(shape, dtype=np.int) - im = np.empty(shape, dtype=np.int) - id = np.empty(shape, dtype=np.int) - ihmsf = np.empty(shape, dtype=[('h','i'),('m','i'),('s','i'),('f','i')]) + iy_out = np.empty(shape, dtype=np.int) + im_out = np.empty(shape, dtype=np.int) + id_out = np.empty(shape, dtype=np.int) + ihmsf_out = np.empty(shape, dtype=[('h','i'),('m','i'),('s','i'),('f','i')]) - cdef np.broadcast it = np.broadcast(scale, ndp, d1, d2, iy, im, id, ihmsf) + cdef np.broadcast it = np.broadcast(scale, ndp, d1, d2, iy_out, im_out, id_out, ihmsf_out) cdef char *_scale cdef int _ndp @@ -148,7 +148,7 @@ def d2dtf(scale, ndp, d1, d2): np.PyArray_MultiIter_NEXT(it) - return iy, im, id, ihmsf + return iy_out, im_out, id_out, ihmsf_out From 64e30685b9147943224ad227d65fae2609896ed9 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 10:05:36 +0200 Subject: [PATCH 05/37] cdef and assign all C arguments before use --- cython_numpy/erfa.pyx | 66 +++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/cython_numpy/erfa.pyx b/cython_numpy/erfa.pyx index b0d8030..eb2ef87 100644 --- a/cython_numpy/erfa.pyx +++ b/cython_numpy/erfa.pyx @@ -72,12 +72,30 @@ def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, php cdef np.broadcast it = np.broadcast(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tk, rh, wl, aob_out, zob_out, hob_out, dob_out, rob_out, eo_out) - cdef double _aob - cdef double _zob - cdef double _hob - cdef double _dob - cdef double _rob - cdef double _eo + cdef double _rc + cdef double _dc + cdef double _pr + cdef double _pd + cdef double _px + cdef double _rv + cdef double _utc1 + cdef double _utc2 + cdef double _dut1 + cdef double _elong + cdef double _phi + cdef double _hm + cdef double _xp + cdef double _yp + cdef double _phpa + cdef double _tk + cdef double _rh + cdef double _wl + cdef double *_aob + cdef double *_zob + cdef double *_hob + cdef double *_dob + cdef double *_rob + cdef double *_eo while np.PyArray_MultiIter_NOTDONE(it): @@ -99,15 +117,14 @@ def atco13(rc, dc, pr, pd, px, rv, utc1, utc2, dut1, elong, phi, hm, xp, yp, php _tk = (np.PyArray_MultiIter_DATA(it, 15))[0] _rh = (np.PyArray_MultiIter_DATA(it, 16))[0] _wl = (np.PyArray_MultiIter_DATA(it, 17))[0] + _aob = (np.PyArray_MultiIter_DATA(it, 18)) + _zob = (np.PyArray_MultiIter_DATA(it, 19)) + _hob = (np.PyArray_MultiIter_DATA(it, 20)) + _dob = (np.PyArray_MultiIter_DATA(it, 21)) + _rob = (np.PyArray_MultiIter_DATA(it, 22)) + _eo = (np.PyArray_MultiIter_DATA(it, 23)) - ret = eraAtco13(_rc, _dc, _pr, _pd, _px, _rv, _utc1, _utc2, _dut1, _elong, _phi, _hm, _xp, _yp, _phpa, _tk, _rh, _wl, &_aob, &_zob, &_hob, &_dob, &_rob, &_eo) - - (np.PyArray_MultiIter_DATA(it, 18))[0] = _aob - (np.PyArray_MultiIter_DATA(it, 19))[0] = _zob - (np.PyArray_MultiIter_DATA(it, 20))[0] = _hob - (np.PyArray_MultiIter_DATA(it, 21))[0] = _dob - (np.PyArray_MultiIter_DATA(it, 22))[0] = _rob - (np.PyArray_MultiIter_DATA(it, 23))[0] = _eo + ret = eraAtco13(_rc, _dc, _pr, _pd, _px, _rv, _utc1, _utc2, _dut1, _elong, _phi, _hm, _xp, _yp, _phpa, _tk, _rh, _wl, _aob, _zob, _hob, _dob, _rob, _eo) np.PyArray_MultiIter_NEXT(it) @@ -127,10 +144,12 @@ def d2dtf(scale, ndp, d1, d2): cdef char *_scale cdef int _ndp - cdef int _iy - cdef int _im - cdef int _id - cdef int _ihmsf[4] + cdef double _d1 + cdef double _d2 + cdef int *_iy + cdef int *_im + cdef int *_id + cdef int *_ihmsf while np.PyArray_MultiIter_NOTDONE(it): @@ -138,13 +157,12 @@ def d2dtf(scale, ndp, d1, d2): _ndp = ( np.PyArray_MultiIter_DATA(it, 1))[0] _d1 = (np.PyArray_MultiIter_DATA(it, 2))[0] _d2 = (np.PyArray_MultiIter_DATA(it, 3))[0] + _iy = ( np.PyArray_MultiIter_DATA(it, 4)) + _im = ( np.PyArray_MultiIter_DATA(it, 5)) + _id = ( np.PyArray_MultiIter_DATA(it, 6)) + _ihmsf = ( np.PyArray_MultiIter_DATA(it, 7)) - ret = eraD2dtf(_scale, _ndp, _d1, _d2, &_iy, &_im, &_id, _ihmsf) - - (np.PyArray_MultiIter_DATA(it, 4))[0] = _iy - (np.PyArray_MultiIter_DATA(it, 5))[0] = _im - (np.PyArray_MultiIter_DATA(it, 6))[0] = _id - (np.PyArray_MultiIter_DATA(it, 7))[0:4] = _ihmsf + ret = eraD2dtf(_scale, _ndp, _d1, _d2, _iy, _im, _id, _ihmsf) np.PyArray_MultiIter_NEXT(it) From 0cdfa62a819ea5dc5b4884189f555299aa059563 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Mon, 28 Jul 2014 10:06:11 +0200 Subject: [PATCH 06/37] Use copy constructor array() and broadcast_arrays()... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …instead of empty() and copyto() --- cython_numpy/erfa.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cython_numpy/erfa.pyx b/cython_numpy/erfa.pyx index eb2ef87..f3943e8 100644 --- a/cython_numpy/erfa.pyx +++ b/cython_numpy/erfa.pyx @@ -173,8 +173,7 @@ def d2dtf(scale, ndp, d1, d2): def aper(theta, astrom): shape = np.broadcast(theta, astrom).shape - astrom_out = np.empty(shape, dtype=dt_eraASTROM) - np.copyto(astrom_out, astrom) + astrom_out = np.array(np.broadcast_arrays(theta, astrom)[1], dtype=dt_eraASTROM) cdef np.broadcast it = np.broadcast(theta, astrom_out) From 9c9d296895373e950afafb363feddc1b223be496 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:29:31 +0200 Subject: [PATCH 07/37] Added *.py[cod] to ignore list --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1bb95c0..6a9fce5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /build .project - .pydevproject + +*.py[cod] From 3dbc8f8e5bcec274ecb75749682ec9b038289b4e Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:30:49 +0200 Subject: [PATCH 08/37] Initial commit of code analyzer --- cython_numpy_auto/code_analyzer.py | 152 +++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 cython_numpy_auto/code_analyzer.py diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py new file mode 100644 index 0000000..3489372 --- /dev/null +++ b/cython_numpy_auto/code_analyzer.py @@ -0,0 +1,152 @@ +import os.path +import re + + +__all__ = ['Function'] + + +ctype_to_dtype = {'double' : "np.double", + 'double *' : "np.double", + 'int' : "np.int", + 'int *' : "np.int", + 'int[4]' : "np.dtype([('', 'i')]*4)", + 'eraASTROM *': "dt_eraASTROM", + } + + +class FunctionDoc: + + def __init__(self, doc): + self.doc = doc.replace("**"," ").replace("/*"," ").replace("*/"," ") + self.__input = None + self.__output = None + + @property + def input(self): + if self.__input is None: + self.__input = [] + __input = re.search("Given:\n(.+?) \n", self.doc, re.DOTALL).group(1) + for i in __input.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__input.append(arg_doc) + return self.__input + + @property + def output(self): + if self.__output is None: + self.__output = [] + __output = re.search("Returned:\n(.+?) \n", self.doc, re.DOTALL).group(1) + for i in __output.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__output.append(arg_doc) + return self.__output + + def __repr__(self): + return self.doc + +class ArgumentDoc: + + def __init__(self, doc): + match = re.search("^ ([^ ]+)[ ]+([^ ]+)[ ]+(.+)", doc) + if match is not None: + self.name = match.group(1) + self.type = match.group(2) + self.doc = match.group(3) + else: + self.name = None + self.type = None + self.doc = None + + def __repr__(self): + return " {0:15} {1:15} {2}".format(self.name, self.type, self.doc) + +class Argument: + + def __init__(self, definition, doc): + self.__doc = doc + self.__inout_state = None + self.definition = definition.strip() + if self.definition[-1] == "]": + self.ctype, self.name = self.definition.split(" ",1) + self.name, arr = self.name.split("[") + self.ctype += ("["+arr) + elif "*" in self.definition: + self.ctype, self.name = self.definition.split("*", 1) + self.ctype += "*" + else: + self.ctype, self.name = self.definition.split(" ", 1) + + @property + def inout_state(self): + if self.__inout_state is None: + self.__inout_state = '' + for i in self.__doc.input: + if self.name in i.name.split(','): + self.__inout_state = 'in' + for o in self.__doc.output: + if self.name in o.name.split(','): + if self.__inout_state == 'in': + self.__inout_state = 'inout' + else: + self.__inout_state = 'out' + return self.__inout_state + + @property + def is_in(self): + return self.inout_state == 'in' + + @property + def is_out(self): + return self.inout_state == 'out' + + @property + def is_inout(self): + return self.inout_state == 'inout' + + @property + def ctype_ptr(self): + if self.ctype[-1] == ']': + return self.ctype.split('[')[0]+" *" + elif self.ctype[:6] == 'const ': + return self.ctype[6:] + else: + return self.ctype + + @property + def dtype(self): + return ctype_to_dtype[self.ctype] + + def __repr__(self): + return "Argument('{0}', name='{1}', ctype='{2}', inout_state='{3}')".format(self.definition, self.name, self.ctype, self.inout_state) + +class Function: + + def __init__(self, name, source_path): + self.name = name + self.pyname = name.split('era')[-1].lower() + self.filename = name.split("era")[-1].lower()+".c" + self.filepath = os.path.join(os.path.normpath(source_path), self.filename) + pattern = "\n([^\n]+{0}\([^)]+\)).+?(/\*.+?\*/)".format(name) + p = re.compile(pattern, flags=re.DOTALL|re.MULTILINE) + with open(self.filepath) as f: + search = p.search(f.read()) + self.cfunc = search.group(1) + self.__doc = FunctionDoc(search.group(2)) + self.args = [] + for arg in re.search("\(([^)]+)\)", self.cfunc, flags=re.MULTILINE|re.DOTALL).group(1).split(','): + self.args.append(Argument(arg, self.__doc)) + + def args_by_inout(self, inout_filter, prop=None, join=None): + result = [] + for arg in self.args: + if arg.inout_state in inout_filter.split('|'): + if prop is None: + result.append(arg) + else: + result.append(getattr(arg, prop)) + if join is not None: + return join.join(result) + else: + return result From 7523521c2643ab655b59027b1a8eb5c68bd305b6 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:31:22 +0200 Subject: [PATCH 09/37] Initial commit of cython_generator --- cython_numpy_auto/cython_generator.py | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 cython_numpy_auto/cython_generator.py diff --git a/cython_numpy_auto/cython_generator.py b/cython_numpy_auto/cython_generator.py new file mode 100644 index 0000000..a0d84a8 --- /dev/null +++ b/cython_numpy_auto/cython_generator.py @@ -0,0 +1,62 @@ +from code_analyzer import * + + +def generate(era_func_names, source_path): + + era_funcs = [Function(name, source_path) for name in era_func_names] + + wrapper = [] + wrapper.append("import numpy as np") + wrapper.append("cimport numpy as np") + wrapper.append("") + wrapper.append("np.import_array()") + wrapper.append("") + wrapper.append("__all__ = [{0}]".format(", ".join(["'{0}'".format(func.pyname) for func in era_funcs]+["'dt_eraASTROM'"]))) + wrapper.append("") + wrapper.append("cdef extern from 'erfa.h':") + wrapper.append(" struct eraASTROM:") + wrapper.append(" pass") + for func in era_funcs: + wrapper.append(" {0}".format(func.cfunc)) + wrapper.append("") + wrapper.append("dt_eraASTROM = np.dtype([('pmt','d'),('eb','d',(3,)),('eh','d',(3,)),('em','d'),('v','d',(3,)),('bm1 ','d'),('bpn','d',(3,3)),('along','d'),('phi','d'),('xpl','d'),('ypl','d'),('sphi','d'),('cphi','d'),('diurab','d'),('eral','d'),('refa','d'),('refb','d')], align=True)") + wrapper.append("") + for func in era_funcs: + wrapper.append("#=== {0} ===".format(func.name)) + wrapper.append("") + wrapper.append("def {0}({1}):".format(func.pyname, func.args_by_inout('in|inout','name',', '))) + wrapper.append(" ") + wrapper.append(" shape = np.broadcast({0}).shape".format(func.args_by_inout('in|inout','name',', '))) + for arg in func.args_by_inout('out'): + wrapper.append(" {0}_out = np.empty(shape, dtype={1})".format(arg.name, arg.dtype)) + for arg in func.args_by_inout('inout'): + wrapper.append(" {0}_out = np.array(np.broadcast_arrays({1})[{2}], dtype={3})".format(arg.name, func.args_by_inout('in|inout','name',', '), func.args.index(arg), arg.dtype)) + wrapper.append(" ") + wrapper.append(" cdef np.broadcast it = np.broadcast({0})".format(', '.join([arg.name if arg.is_in else (arg.name+"_out") for arg in func.args_by_inout('in|inout|out')]))) + wrapper.append(" ") + for arg in func.args_by_inout('in|out|inout'): + wrapper.append(" cdef {0} _{1}".format(arg.ctype_ptr, arg.name)) + wrapper.append(" ") + wrapper.append(" while np.PyArray_MultiIter_NOTDONE(it):") + wrapper.append(" ") + for arg in func.args_by_inout('in|out|inout'): + if arg.ctype_ptr[-1] == '*': + wrapper.append(" _{0} = (<{1}>np.PyArray_MultiIter_DATA(it, {2}))".format(arg.name, arg.ctype_ptr, func.args.index(arg))) + else: + wrapper.append(" _{0} = (<{1}*>np.PyArray_MultiIter_DATA(it, {2}))[0]".format(arg.name, arg.ctype_ptr, func.args.index(arg))) + wrapper.append(" ") + wrapper.append(" {0}({1})".format(func.name, ", ".join(["_"+name for name in func.args_by_inout('in|out|inout','name')]))) + wrapper.append(" ") + wrapper.append(" np.PyArray_MultiIter_NEXT(it)") + wrapper.append(" ") + wrapper.append(" return {0}".format(', '.join([arg+"_out" for arg in func.args_by_inout('out|inout','name')]))) + wrapper.append("") + + with open("./erfa.pyx","w") as f: + f.write("\n".join(wrapper)) + + +if __name__ == '__main__': + era_func_names = ["eraAtco13", "eraD2dtf", "eraAper"] + source_path = "../../erfa/src" + generate(era_func_names, source_path) From 2a6093340b1c693d860741da77fc9a48626aeac9 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:32:47 +0200 Subject: [PATCH 10/37] Ignore erfa.pyx and erfa.c in cython_numpy_auto --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 6a9fce5..157f002 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ .pydevproject *.py[cod] + +cython_numpy_auto/erfa.c + +cython_numpy_auto/erfa.pyx From 6e653c169c8d485b08ad7d8a81608e4479d9ce4d Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:33:08 +0200 Subject: [PATCH 11/37] Added cython_numpy_auto extension to setup.py --- setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index acfd3b0..4b7a75a 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,14 @@ include_dirs = [np.get_include(), '/opt/local/include'], library_dirs = ['/opt/local/lib']) +cython_numpy_auto_ext = Extension("erfa.cython_numpy_auto", + ["cython_numpy_auto/erfa.pyx"], + libraries=['erfa'], + include_dirs = [np.get_include(), '/opt/local/include'], + library_dirs = ['/opt/local/lib']) + setup(name = "erfa", cmdclass = { "build_ext": build_ext }, packages = ["erfa"], package_dir = {"erfa":"."}, - ext_modules = [cython_numpy_ext]) + ext_modules = [cython_numpy_ext, cython_numpy_auto_ext]) From bcbe7091ecf49cead69b36f25558c47f61e9e89f Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Tue, 29 Jul 2014 11:39:17 +0200 Subject: [PATCH 12/37] Run test against both cython_numpy and cython_numpy_auto --- tests/test_erfa.py | 63 +++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/tests/test_erfa.py b/tests/test_erfa.py index 9bdd66d..7126c05 100644 --- a/tests/test_erfa.py +++ b/tests/test_erfa.py @@ -1,30 +1,37 @@ +import importlib import numpy as np -import erfa.cython_numpy as erfa -jd = np.linspace(2456855.5, 2456855.5+1.0/24.0/60.0, 60*2+1) -ra = np.linspace(0.0,np.pi*2.0,5) -dec = np.linspace(-np.pi/2.0,+np.pi/2.0,4) - -aob, zob, hob, dob, rob, eo = erfa.atco13(0.0,0.0,0.0,0.0,0.0,0.0,jd,0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) -print(aob.shape) - -aob, zob, hob, dob, rob, eo = erfa.atco13(0.0,0.0,0.0,0.0,0.0,0.0,jd[0],0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) -print(aob.shape) - -aob, zob, hob, dob, rob, eo = erfa.atco13(ra[:,None,None],dec[None,:,None],0.0,0.0,0.0,0.0,jd[None,None,:],0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) -print(aob.shape) - -iy, im, id, ihmsf = erfa.d2dtf("UTC", 3, jd, 0.0) -print(iy.shape, ihmsf.shape, ihmsf.dtype) -print(ihmsf) - -iy, im, id, ihmsf = erfa.d2dtf("UTC", 3, jd[0], 0.0) -print(iy.shape, ihmsf.shape, ihmsf.dtype) -print(ihmsf) - -astrom = np.zeros([2],dtype=erfa.dt_eraASTROM) -theta = np.arange(0,10.0) -print(theta.shape) -print(astrom.shape) -astrom = erfa.aper(theta[:,None], astrom[None,:]) -print(astrom) +for erfa_wrapper_name in ['cython_numpy', 'cython_numpy_auto']: + + erfa_module_name = '.'.join(['erfa', erfa_wrapper_name]) + print("Testing with {0}...".format(erfa_module_name)) + + erfa = importlib.import_module(erfa_module_name) + + jd = np.linspace(2456855.5, 2456855.5+1.0/24.0/60.0, 60*2+1) + ra = np.linspace(0.0,np.pi*2.0,5) + dec = np.linspace(-np.pi/2.0,+np.pi/2.0,4) + + aob, zob, hob, dob, rob, eo = erfa.atco13(0.0,0.0,0.0,0.0,0.0,0.0,jd,0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) + print(aob.shape) + + aob, zob, hob, dob, rob, eo = erfa.atco13(0.0,0.0,0.0,0.0,0.0,0.0,jd[0],0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) + print(aob.shape) + + aob, zob, hob, dob, rob, eo = erfa.atco13(ra[:,None,None],dec[None,:,None],0.0,0.0,0.0,0.0,jd[None,None,:],0.0,0.0,0.0,np.pi/4.0,0.0,0.0,0.0,1014.0,0.0,0.0,0.5) + print(aob.shape) + + iy, im, id, ihmsf = erfa.d2dtf("UTC", 3, jd, 0.0) + print(iy.shape, ihmsf.shape, ihmsf.dtype) + print(ihmsf) + + iy, im, id, ihmsf = erfa.d2dtf("UTC", 3, jd[0], 0.0) + print(iy.shape, ihmsf.shape, ihmsf.dtype) + print(ihmsf) + + astrom = np.zeros([2],dtype=erfa.dt_eraASTROM) + theta = np.arange(0,10.0) + print(theta.shape) + print(astrom.shape) + astrom = erfa.aper(theta[:,None], astrom[None,:]) + print(astrom) From d1dcc3b513e3767dd973c582c96d044f17ec123e Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Wed, 30 Jul 2014 09:53:42 +0200 Subject: [PATCH 13/37] Base all classes on `object` for attribute access. --- cython_numpy_auto/code_analyzer.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 3489372..9d376a4 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -14,7 +14,7 @@ } -class FunctionDoc: +class FunctionDoc(object): def __init__(self, doc): self.doc = doc.replace("**"," ").replace("/*"," ").replace("*/"," ") @@ -46,7 +46,7 @@ def output(self): def __repr__(self): return self.doc -class ArgumentDoc: +class ArgumentDoc(object): def __init__(self, doc): match = re.search("^ ([^ ]+)[ ]+([^ ]+)[ ]+(.+)", doc) @@ -62,7 +62,7 @@ def __init__(self, doc): def __repr__(self): return " {0:15} {1:15} {2}".format(self.name, self.type, self.doc) -class Argument: +class Argument(object): def __init__(self, definition, doc): self.__doc = doc @@ -121,7 +121,7 @@ def dtype(self): def __repr__(self): return "Argument('{0}', name='{1}', ctype='{2}', inout_state='{3}')".format(self.definition, self.name, self.ctype, self.inout_state) -class Function: +class Function(object): def __init__(self, name, source_path): self.name = name From 729d2e0e3f65deda215a7e78a50cfbebdb0c7100 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Wed, 30 Jul 2014 09:54:12 +0200 Subject: [PATCH 14/37] Turn `cython_numpy_auto` into a package --- cython_numpy_auto/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cython_numpy_auto/__init__.py diff --git a/cython_numpy_auto/__init__.py b/cython_numpy_auto/__init__.py new file mode 100644 index 0000000..e69de29 From b6da8b8b53aea480f0bc94d4eeff3b4a90ccfb01 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Wed, 30 Jul 2014 09:55:08 +0200 Subject: [PATCH 15/37] Use jinja2 for code generation --- cython_numpy_auto/cython_generator.py | 67 ++++----------------------- cython_numpy_auto/erfa.pyx.in | 67 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 58 deletions(-) create mode 100644 cython_numpy_auto/erfa.pyx.in diff --git a/cython_numpy_auto/cython_generator.py b/cython_numpy_auto/cython_generator.py index a0d84a8..d8b4f43 100644 --- a/cython_numpy_auto/cython_generator.py +++ b/cython_numpy_auto/cython_generator.py @@ -1,62 +1,13 @@ -from code_analyzer import * +from jinja2 import Environment, PackageLoader +from cython_numpy_auto.code_analyzer import Function +env = Environment(loader=PackageLoader('cython_numpy_auto', '.')) +erfa_pyx_in = env.get_template('erfa.pyx.in') -def generate(era_func_names, source_path): +era_func_names = ["eraAtco13", "eraD2dtf", "eraAper"] +source_path = "../../erfa/src" - era_funcs = [Function(name, source_path) for name in era_func_names] - - wrapper = [] - wrapper.append("import numpy as np") - wrapper.append("cimport numpy as np") - wrapper.append("") - wrapper.append("np.import_array()") - wrapper.append("") - wrapper.append("__all__ = [{0}]".format(", ".join(["'{0}'".format(func.pyname) for func in era_funcs]+["'dt_eraASTROM'"]))) - wrapper.append("") - wrapper.append("cdef extern from 'erfa.h':") - wrapper.append(" struct eraASTROM:") - wrapper.append(" pass") - for func in era_funcs: - wrapper.append(" {0}".format(func.cfunc)) - wrapper.append("") - wrapper.append("dt_eraASTROM = np.dtype([('pmt','d'),('eb','d',(3,)),('eh','d',(3,)),('em','d'),('v','d',(3,)),('bm1 ','d'),('bpn','d',(3,3)),('along','d'),('phi','d'),('xpl','d'),('ypl','d'),('sphi','d'),('cphi','d'),('diurab','d'),('eral','d'),('refa','d'),('refb','d')], align=True)") - wrapper.append("") - for func in era_funcs: - wrapper.append("#=== {0} ===".format(func.name)) - wrapper.append("") - wrapper.append("def {0}({1}):".format(func.pyname, func.args_by_inout('in|inout','name',', '))) - wrapper.append(" ") - wrapper.append(" shape = np.broadcast({0}).shape".format(func.args_by_inout('in|inout','name',', '))) - for arg in func.args_by_inout('out'): - wrapper.append(" {0}_out = np.empty(shape, dtype={1})".format(arg.name, arg.dtype)) - for arg in func.args_by_inout('inout'): - wrapper.append(" {0}_out = np.array(np.broadcast_arrays({1})[{2}], dtype={3})".format(arg.name, func.args_by_inout('in|inout','name',', '), func.args.index(arg), arg.dtype)) - wrapper.append(" ") - wrapper.append(" cdef np.broadcast it = np.broadcast({0})".format(', '.join([arg.name if arg.is_in else (arg.name+"_out") for arg in func.args_by_inout('in|inout|out')]))) - wrapper.append(" ") - for arg in func.args_by_inout('in|out|inout'): - wrapper.append(" cdef {0} _{1}".format(arg.ctype_ptr, arg.name)) - wrapper.append(" ") - wrapper.append(" while np.PyArray_MultiIter_NOTDONE(it):") - wrapper.append(" ") - for arg in func.args_by_inout('in|out|inout'): - if arg.ctype_ptr[-1] == '*': - wrapper.append(" _{0} = (<{1}>np.PyArray_MultiIter_DATA(it, {2}))".format(arg.name, arg.ctype_ptr, func.args.index(arg))) - else: - wrapper.append(" _{0} = (<{1}*>np.PyArray_MultiIter_DATA(it, {2}))[0]".format(arg.name, arg.ctype_ptr, func.args.index(arg))) - wrapper.append(" ") - wrapper.append(" {0}({1})".format(func.name, ", ".join(["_"+name for name in func.args_by_inout('in|out|inout','name')]))) - wrapper.append(" ") - wrapper.append(" np.PyArray_MultiIter_NEXT(it)") - wrapper.append(" ") - wrapper.append(" return {0}".format(', '.join([arg+"_out" for arg in func.args_by_inout('out|inout','name')]))) - wrapper.append("") - - with open("./erfa.pyx","w") as f: - f.write("\n".join(wrapper)) +erfa_pyx = erfa_pyx_in.render(funcs=[Function(name, source_path) for name in era_func_names]) - -if __name__ == '__main__': - era_func_names = ["eraAtco13", "eraD2dtf", "eraAper"] - source_path = "../../erfa/src" - generate(era_func_names, source_path) +with open("erfa.pyx", "w") as f: + f.write(erfa_pyx) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in new file mode 100644 index 0000000..3673a32 --- /dev/null +++ b/cython_numpy_auto/erfa.pyx.in @@ -0,0 +1,67 @@ +import numpy as np +cimport numpy as np + +np.import_array() + +__all__ = ['{{ funcs|map(attribute='name')|join("', '") }}'] + + +cdef extern from "erfa.h": + struct eraASTROM: + pass +{%- for func in funcs %} + {{ func.cfunc }} +{%- endfor %} + +dt_eraASTROM = np.dtype([('pmt','d'), + ('eb','d',(3,)), + ('eh','d',(3,)), + ('em','d'), + ('v','d',(3,)), + ('bm1 ','d'), + ('bpn','d',(3,3)), + ('along','d'), + ('phi','d'), + ('xpl','d'), + ('ypl','d'), + ('sphi','d'), + ('cphi','d'), + ('diurab','d'), + ('eral','d'), + ('refa','d'), + ('refb','d')], align=True) + + +{% for func in funcs %} +def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}): + + shape = np.broadcast({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}).shape + {%- for arg in func.args_by_inout('out') %} + {{ arg.name }}_out = np.empty(shape, dtype={{ arg.dtype }}) + {%- endfor %} + {%- for arg in func.args_by_inout('inout') %} + {{ arg.name }}_out = np.array(np.broadcast_arrays({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }})[{{ func.args.index(arg) }}], dtype={{ arg.dtype }}) + {%- endfor %} + + cdef np.broadcast it = np.broadcast({{ func.args_by_inout('in')|map(attribute='name')|join(', ') }}, {{func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out) + {%- for arg in func.args_by_inout('in|inout|out') %} + cdef {{ arg.ctype_ptr }} _{{ arg.name }} + {%- endfor %} + + while np.PyArray_MultiIter_NOTDONE(it): + + {%- for arg in func.args_by_inout('in|inout|out') %} + {%- if arg.ctype_ptr[-1] == '*' %} + _{{ arg.name }} = (<{{ arg.ctype_ptr }}>np.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }})) + {%- else %} + _{{ arg.name }} = (<{{ arg.ctype_ptr }}*>np.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }}))[0] + {%- endif %} + {%- endfor %} + + {{ func.name }}(_{{ func.args_by_inout('in|out|inout')|map(attribute='name')|join(', _') }}) + + np.PyArray_MultiIter_NEXT(it) + + return {{ func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out + +{% endfor %} From 1ec74bcd06a25eebe340633a2cd4b3f14e9bae73 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:31:43 +0200 Subject: [PATCH 16/37] Add more ctype to dtype conversions --- cython_numpy_auto/code_analyzer.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 9d376a4..4344790 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -5,12 +5,17 @@ __all__ = ['Function'] -ctype_to_dtype = {'double' : "np.double", - 'double *' : "np.double", - 'int' : "np.int", - 'int *' : "np.int", - 'int[4]' : "np.dtype([('', 'i')]*4)", - 'eraASTROM *': "dt_eraASTROM", +ctype_to_dtype = {'double' : "numpy.double", + 'double *' : "numpy.double", + 'int' : "numpy.int", + 'int *' : "numpy.int", + 'int[4]' : "numpy.dtype([('', 'i', (4,))])", + 'double[2]' : "numpy.dtype([('', 'd', (2,))])", + 'double[3]' : "numpy.dtype([('p', 'd', (3,))])", + 'double[2][3]' : "numpy.dtype([('pv', 'd', (2,3))])", + 'double[3][3]' : "numpy.dtype([('r', 'd', (3,3))])", + 'eraASTROM *' : "dt_eraASTROM", + 'char *' : "numpy.dtype('S1')", } From 940753a2d0db12e38726bd308c9a8c4073059b95 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:32:30 +0200 Subject: [PATCH 17/37] Deal with empty input/output sections in doc --- cython_numpy_auto/code_analyzer.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 4344790..9fa218d 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -30,22 +30,26 @@ def __init__(self, doc): def input(self): if self.__input is None: self.__input = [] - __input = re.search("Given:\n(.+?) \n", self.doc, re.DOTALL).group(1) - for i in __input.split("\n"): - arg_doc = ArgumentDoc(i) - if arg_doc.name is not None: - self.__input.append(arg_doc) + result = re.search("Given[^\n]*:\n(.+?) \n", self.doc, re.DOTALL) + if result is not None: + __input = result.group(1) + for i in __input.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__input.append(arg_doc) return self.__input @property def output(self): if self.__output is None: self.__output = [] - __output = re.search("Returned:\n(.+?) \n", self.doc, re.DOTALL).group(1) - for i in __output.split("\n"): - arg_doc = ArgumentDoc(i) - if arg_doc.name is not None: - self.__output.append(arg_doc) + result = re.search("Returned:\n(.+?) \n", self.doc, re.DOTALL) + if result is not None: + __output = result.group(1) + for i in __output.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__output.append(arg_doc) return self.__output def __repr__(self): From 41cbfe12e081e47d6d9487b0a36f5f6953058e0d Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:33:21 +0200 Subject: [PATCH 18/37] Argument documentations have variable leading spaces --- cython_numpy_auto/code_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 9fa218d..69ade73 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -58,7 +58,7 @@ def __repr__(self): class ArgumentDoc(object): def __init__(self, doc): - match = re.search("^ ([^ ]+)[ ]+([^ ]+)[ ]+(.+)", doc) + match = re.search("^ +([^ ]+)[ ]+([^ ]+)[ ]+(.+)", doc) if match is not None: self.name = match.group(1) self.type = match.group(2) From 7506ff08d0c3a3053b3bcc8c07a92fdced7fd522 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:34:00 +0200 Subject: [PATCH 19/37] Deal with multidimensional C arrays --- cython_numpy_auto/code_analyzer.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 69ade73..da1a49e 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -77,15 +77,14 @@ def __init__(self, definition, doc): self.__doc = doc self.__inout_state = None self.definition = definition.strip() - if self.definition[-1] == "]": - self.ctype, self.name = self.definition.split(" ",1) - self.name, arr = self.name.split("[") - self.ctype += ("["+arr) - elif "*" in self.definition: + if "*" in self.definition: self.ctype, self.name = self.definition.split("*", 1) self.ctype += "*" else: - self.ctype, self.name = self.definition.split(" ", 1) + self.ctype, self.name = self.definition.rsplit(" ", 1) + if "[" in self.name: + self.name, arr = self.name.split("[", 1) + self.ctype += ("["+arr) @property def inout_state(self): From 7bbcabb4e44f36000c343855d49217726fedf7d2 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:34:31 +0200 Subject: [PATCH 20/37] Pointer arguments are always of input type --- cython_numpy_auto/code_analyzer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index da1a49e..701985a 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -93,6 +93,8 @@ def inout_state(self): for i in self.__doc.input: if self.name in i.name.split(','): self.__inout_state = 'in' + if "*" in self.ctype_ptr: + self.__inout_state = 'in' for o in self.__doc.output: if self.name in o.name.split(','): if self.__inout_state == 'in': From 922cba1bab5bd2e0b7443888ae7361d346d57f56 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:34:58 +0200 Subject: [PATCH 21/37] Remove dead code: is_in(), is_out(), is_inout() --- cython_numpy_auto/code_analyzer.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 701985a..3c3986b 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -103,18 +103,6 @@ def inout_state(self): self.__inout_state = 'out' return self.__inout_state - @property - def is_in(self): - return self.inout_state == 'in' - - @property - def is_out(self): - return self.inout_state == 'out' - - @property - def is_inout(self): - return self.inout_state == 'inout' - @property def ctype_ptr(self): if self.ctype[-1] == ']': From fa3612b9a9d6128e1fd0690f2adc2b893b53b680 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:35:50 +0200 Subject: [PATCH 22/37] Implement Return() class to use along regular arguments --- cython_numpy_auto/code_analyzer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 3c3986b..e00364d 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -119,6 +119,18 @@ def dtype(self): def __repr__(self): return "Argument('{0}', name='{1}', ctype='{2}', inout_state='{3}')".format(self.definition, self.name, self.ctype, self.inout_state) +class Return(object): + + def __init__(self, ctype, doc): + self.name = 'ret' + self.ctype = ctype + self.inout_state = 'ret' + self.ctype_ptr = ctype + + @property + def dtype(self): + return ctype_to_dtype[self.ctype] + class Function(object): def __init__(self, name, source_path): From 1ebb796c9ad46e28416d03be8c847d19cd3204f8 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:37:12 +0200 Subject: [PATCH 23/37] Spaces between function name and opening parenthesis --- cython_numpy_auto/code_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index e00364d..2e7cc18 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -138,7 +138,7 @@ def __init__(self, name, source_path): self.pyname = name.split('era')[-1].lower() self.filename = name.split("era")[-1].lower()+".c" self.filepath = os.path.join(os.path.normpath(source_path), self.filename) - pattern = "\n([^\n]+{0}\([^)]+\)).+?(/\*.+?\*/)".format(name) + pattern = "\n([^\n]+{0} ?\([^)]+\)).+?(/\*.+?\*/)".format(name) p = re.compile(pattern, flags=re.DOTALL|re.MULTILINE) with open(self.filepath) as f: search = p.search(f.read()) From 3d3521823a013b795ecca51ec84546db32abad3e Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:37:54 +0200 Subject: [PATCH 24/37] Add returned parameter at end of arguments list --- cython_numpy_auto/code_analyzer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 2e7cc18..5306e96 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -147,6 +147,9 @@ def __init__(self, name, source_path): self.args = [] for arg in re.search("\(([^)]+)\)", self.cfunc, flags=re.MULTILINE|re.DOTALL).group(1).split(','): self.args.append(Argument(arg, self.__doc)) + self.ret = re.search("^(.*){0}".format(name), self.cfunc).group(1).strip() + if self.ret == 'double': + self.args.append(Return(self.ret, self.__doc)) def args_by_inout(self, inout_filter, prop=None, join=None): result = [] From 0de22b98a30c1ad5eac4e0e75b2158cdbcd47a66 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:41:07 +0200 Subject: [PATCH 25/37] np instead of numpy collides with c code arguments --- cython_numpy_auto/erfa.pyx.in | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index 3673a32..9b037e0 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -1,7 +1,7 @@ -import numpy as np -cimport numpy as np +import numpy +cimport numpy -np.import_array() +numpy.import_array() __all__ = ['{{ funcs|map(attribute='name')|join("', '") }}'] @@ -13,7 +13,7 @@ cdef extern from "erfa.h": {{ func.cfunc }} {%- endfor %} -dt_eraASTROM = np.dtype([('pmt','d'), +dt_eraASTROM = numpy.dtype([('pmt','d'), ('eb','d',(3,)), ('eh','d',(3,)), ('em','d'), @@ -35,32 +35,32 @@ dt_eraASTROM = np.dtype([('pmt','d'), {% for func in funcs %} def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}): - shape = np.broadcast({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}).shape + shape = numpy.broadcast({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}).shape {%- for arg in func.args_by_inout('out') %} - {{ arg.name }}_out = np.empty(shape, dtype={{ arg.dtype }}) + {{ arg.name }}_out = numpy.empty(shape, dtype={{ arg.dtype }}) {%- endfor %} {%- for arg in func.args_by_inout('inout') %} - {{ arg.name }}_out = np.array(np.broadcast_arrays({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }})[{{ func.args.index(arg) }}], dtype={{ arg.dtype }}) + {{ arg.name }}_out = numpy.array(numpy.broadcast_arrays({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }})[{{ func.args.index(arg) }}], dtype={{ arg.dtype }}) {%- endfor %} - cdef np.broadcast it = np.broadcast({{ func.args_by_inout('in')|map(attribute='name')|join(', ') }}, {{func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out) + cdef numpy.broadcast it = numpy.broadcast({{ func.args_by_inout('in')|map(attribute='name')|join(', ') }}, {{func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out) {%- for arg in func.args_by_inout('in|inout|out') %} cdef {{ arg.ctype_ptr }} _{{ arg.name }} {%- endfor %} - while np.PyArray_MultiIter_NOTDONE(it): + while numpy.PyArray_MultiIter_NOTDONE(it): {%- for arg in func.args_by_inout('in|inout|out') %} {%- if arg.ctype_ptr[-1] == '*' %} - _{{ arg.name }} = (<{{ arg.ctype_ptr }}>np.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }})) + _{{ arg.name }} = (<{{ arg.ctype_ptr }}>numpy.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }})) {%- else %} - _{{ arg.name }} = (<{{ arg.ctype_ptr }}*>np.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }}))[0] + _{{ arg.name }} = (<{{ arg.ctype_ptr }}*>numpy.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }}))[0] {%- endif %} {%- endfor %} {{ func.name }}(_{{ func.args_by_inout('in|out|inout')|map(attribute='name')|join(', _') }}) - np.PyArray_MultiIter_NEXT(it) + numpy.PyArray_MultiIter_NEXT(it) return {{ func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out From 763c08c49a156a30664943d9513564061493f253 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:44:41 +0200 Subject: [PATCH 26/37] Three new jinja filters: prefix, postfix, surround --- cython_numpy_auto/cython_generator.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cython_numpy_auto/cython_generator.py b/cython_numpy_auto/cython_generator.py index d8b4f43..faf36b4 100644 --- a/cython_numpy_auto/cython_generator.py +++ b/cython_numpy_auto/cython_generator.py @@ -2,6 +2,17 @@ from cython_numpy_auto.code_analyzer import Function env = Environment(loader=PackageLoader('cython_numpy_auto', '.')) + +def prefix(a_list, pre): + return [pre+'{0}'.format(an_element) for an_element in a_list] +def postfix(a_list, post): + return ['{0}'.format(an_element)+post for an_element in a_list] +def surround(a_list, pre, post): + return [pre+'{0}'.format(an_element)+post for an_element in a_list] +env.filters['prefix'] = prefix +env.filters['postfix'] = postfix +env.filters['surround'] = surround + erfa_pyx_in = env.get_template('erfa.pyx.in') era_func_names = ["eraAtco13", "eraD2dtf", "eraAper"] From d03c0c1e34beb4ab393b6cead6e8739d243d4307 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:45:50 +0200 Subject: [PATCH 27/37] Process all ERFA functions --- cython_numpy_auto/cython_generator.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cython_numpy_auto/cython_generator.py b/cython_numpy_auto/cython_generator.py index faf36b4..8809d1b 100644 --- a/cython_numpy_auto/cython_generator.py +++ b/cython_numpy_auto/cython_generator.py @@ -1,6 +1,10 @@ +import re from jinja2 import Environment, PackageLoader from cython_numpy_auto.code_analyzer import Function +ERFA_SOURCES = "../../erfa/src" + +#Prepare the jinja2 templating environment env = Environment(loader=PackageLoader('cython_numpy_auto', '.')) def prefix(a_list, pre): @@ -15,10 +19,16 @@ def surround(a_list, pre, post): erfa_pyx_in = env.get_template('erfa.pyx.in') -era_func_names = ["eraAtco13", "eraD2dtf", "eraAper"] -source_path = "../../erfa/src" - -erfa_pyx = erfa_pyx_in.render(funcs=[Function(name, source_path) for name in era_func_names]) -with open("erfa.pyx", "w") as f: - f.write(erfa_pyx) +#Extract all the ERFA function names from erfa.h +with open(ERFA_SOURCES+"/erfa.h", "r") as f: + func_names = re.findall(' (\w+)\(.*?\);', f.read(), flags=re.DOTALL) + funcs = [] + for name in func_names: + print("Parsing {0}...".format(name)) + funcs.append(Function(name, ERFA_SOURCES)) + print("Done!") + #Render the template and save + erfa_pyx = erfa_pyx_in.render(funcs=funcs) + with open("erfa.pyx", "w") as f: + f.write(erfa_pyx) From c9be43ef2567f78c507b7bff590d4d012e999899 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:47:32 +0200 Subject: [PATCH 28/37] Add eraLDBODY structure support --- cython_numpy_auto/erfa.pyx.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index 9b037e0..66b068c 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -9,6 +9,8 @@ __all__ = ['{{ funcs|map(attribute='name')|join("', '") }}'] cdef extern from "erfa.h": struct eraASTROM: pass + struct eraLDBODY: + pass {%- for func in funcs %} {{ func.cfunc }} {%- endfor %} From 56d80cad668c14fd091efd4b96da6973d86642dd Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:51:35 +0200 Subject: [PATCH 29/37] Use prefix, postfix, and surround in template. --- cython_numpy_auto/erfa.pyx.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index 66b068c..be89fc6 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -3,7 +3,7 @@ cimport numpy numpy.import_array() -__all__ = ['{{ funcs|map(attribute='name')|join("', '") }}'] +__all__ = [{{ funcs|map(attribute='name')|surround("'","'")|join(", ") }}] cdef extern from "erfa.h": @@ -45,7 +45,7 @@ def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|jo {{ arg.name }}_out = numpy.array(numpy.broadcast_arrays({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }})[{{ func.args.index(arg) }}], dtype={{ arg.dtype }}) {%- endfor %} - cdef numpy.broadcast it = numpy.broadcast({{ func.args_by_inout('in')|map(attribute='name')|join(', ') }}, {{func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out) + cdef numpy.broadcast it = numpy.broadcast({{ (func.args_by_inout('in')|map(attribute='name')|list + func.args_by_inout('inout|out')|map(attribute='name')|postfix('_out')|list) |join(', ') }}) {%- for arg in func.args_by_inout('in|inout|out') %} cdef {{ arg.ctype_ptr }} _{{ arg.name }} {%- endfor %} @@ -60,10 +60,10 @@ def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|jo {%- endif %} {%- endfor %} - {{ func.name }}(_{{ func.args_by_inout('in|out|inout')|map(attribute='name')|join(', _') }}) + {{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) numpy.PyArray_MultiIter_NEXT(it) - return {{ func.args_by_inout('inout|out')|map(attribute='name')|join('_out, ') }}_out + return {{ func.args_by_inout('inout|out')|map(attribute='name')|postfix('_out')|join(', ') }} {% endfor %} From e3ccefdd5d7628fd29cf19c6b82dbc56bada6ea2 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:52:08 +0200 Subject: [PATCH 30/37] Re-generate extern function signature with pointers --- cython_numpy_auto/erfa.pyx.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index be89fc6..7eddf13 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -12,7 +12,7 @@ cdef extern from "erfa.h": struct eraLDBODY: pass {%- for func in funcs %} - {{ func.cfunc }} + {{ func.ret }} {{ func.name }}({{ func.args_by_inout('in|inout|out')|map(attribute='ctype_ptr')|join(', ') }}) {%- endfor %} dt_eraASTROM = numpy.dtype([('pmt','d'), From e641fd4fedf275e77e97ab6c6b4c558772188f3a Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:55:14 +0200 Subject: [PATCH 31/37] Add returned values --- cython_numpy_auto/erfa.pyx.in | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index 7eddf13..44348e0 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -38,15 +38,15 @@ dt_eraASTROM = numpy.dtype([('pmt','d'), def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}): shape = numpy.broadcast({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}).shape - {%- for arg in func.args_by_inout('out') %} + {%- for arg in func.args_by_inout('out|ret') %} {{ arg.name }}_out = numpy.empty(shape, dtype={{ arg.dtype }}) {%- endfor %} {%- for arg in func.args_by_inout('inout') %} {{ arg.name }}_out = numpy.array(numpy.broadcast_arrays({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }})[{{ func.args.index(arg) }}], dtype={{ arg.dtype }}) {%- endfor %} - cdef numpy.broadcast it = numpy.broadcast({{ (func.args_by_inout('in')|map(attribute='name')|list + func.args_by_inout('inout|out')|map(attribute='name')|postfix('_out')|list) |join(', ') }}) - {%- for arg in func.args_by_inout('in|inout|out') %} + cdef numpy.broadcast it = numpy.broadcast({{ (func.args_by_inout('in')|map(attribute='name')|list + func.args_by_inout('inout|out|ret')|map(attribute='name')|postfix('_out')|list) |join(', ') }}) + {%- for arg in func.args_by_inout('in|inout|out|ret') %} cdef {{ arg.ctype_ptr }} _{{ arg.name }} {%- endfor %} @@ -60,10 +60,14 @@ def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|jo {%- endif %} {%- endfor %} - {{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) + {{ func.args_by_inout('ret')|map(attribute='name')|surround('_',' = ')|join }}{{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) + + {%- for arg in func.args_by_inout('ret') %} + (<{{ arg.ctype_ptr }}*>numpy.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }}))[0] = _{{ arg.name }} + {%- endfor %} numpy.PyArray_MultiIter_NEXT(it) - return {{ func.args_by_inout('inout|out')|map(attribute='name')|postfix('_out')|join(', ') }} + return {{ func.args_by_inout('inout|out|ret')|map(attribute='name')|postfix('_out')|join(', ') }} {% endfor %} From dade3e0d1e537f8f949c372b6fbe95ce57544a5f Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 08:55:50 +0200 Subject: [PATCH 32/37] Functions with no inputs do not need vectorisation --- cython_numpy_auto/erfa.pyx.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index 44348e0..c9831e4 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -37,6 +37,7 @@ dt_eraASTROM = numpy.dtype([('pmt','d'), {% for func in funcs %} def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}): + {%- if func.args_by_inout('in|inout') %} shape = numpy.broadcast({{ func.args_by_inout('in|inout')|map(attribute='name')|join(', ') }}).shape {%- for arg in func.args_by_inout('out|ret') %} {{ arg.name }}_out = numpy.empty(shape, dtype={{ arg.dtype }}) @@ -67,6 +68,13 @@ def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|jo {%- endfor %} numpy.PyArray_MultiIter_NEXT(it) + {%- else %} + {%- for arg in func.args_by_inout('out') %} + {{ arg.name }}_out = numpy.empty(shape, dtype={{ arg.dtype }}) + {%- endfor %} + + {{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) + {%- endif %} return {{ func.args_by_inout('inout|out|ret')|map(attribute='name')|postfix('_out')|join(', ') }} From e16ff9aa70640518f32bf9ae62bbd30776ba0e8d Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 09:35:43 +0200 Subject: [PATCH 33/37] Improvements for no input functions --- cython_numpy_auto/erfa.pyx.in | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/cython_numpy_auto/erfa.pyx.in b/cython_numpy_auto/erfa.pyx.in index c9831e4..3b65992 100644 --- a/cython_numpy_auto/erfa.pyx.in +++ b/cython_numpy_auto/erfa.pyx.in @@ -69,11 +69,29 @@ def {{ func.pyname }}({{ func.args_by_inout('in|inout')|map(attribute='name')|jo numpy.PyArray_MultiIter_NEXT(it) {%- else %} + shape = [] {%- for arg in func.args_by_inout('out') %} {{ arg.name }}_out = numpy.empty(shape, dtype={{ arg.dtype }}) {%- endfor %} - {{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) + cdef numpy.broadcast it = numpy.broadcast({{ (func.args_by_inout('in')|map(attribute='name')|list + func.args_by_inout('inout|out|ret')|map(attribute='name')|postfix('_out')|list) |join(', ') }}) + {%- for arg in func.args_by_inout('out') %} + cdef {{ arg.ctype_ptr }} _{{ arg.name }} + {%- endfor %} + + while numpy.PyArray_MultiIter_NOTDONE(it): + + {%- for arg in func.args_by_inout('in|inout|out') %} + {%- if arg.ctype_ptr[-1] == '*' %} + _{{ arg.name }} = (<{{ arg.ctype_ptr }}>numpy.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }})) + {%- else %} + _{{ arg.name }} = (<{{ arg.ctype_ptr }}*>numpy.PyArray_MultiIter_DATA(it, {{ func.args.index(arg) }}))[0] + {%- endif %} + {%- endfor %} + + {{ func.name }}({{ func.args_by_inout('in|out|inout')|map(attribute='name')|prefix('_')|join(', ') }}) + + numpy.PyArray_MultiIter_NEXT(it) {%- endif %} return {{ func.args_by_inout('inout|out|ret')|map(attribute='name')|postfix('_out')|join(', ') }} From 080325d11df48524249ddc6ae4405566b221e1a0 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 09:36:16 +0200 Subject: [PATCH 34/37] Pointer arguments are actually not necessarily inputs --- cython_numpy_auto/code_analyzer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index 5306e96..fb4f405 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -93,8 +93,6 @@ def inout_state(self): for i in self.__doc.input: if self.name in i.name.split(','): self.__inout_state = 'in' - if "*" in self.ctype_ptr: - self.__inout_state = 'in' for o in self.__doc.output: if self.name in o.name.split(','): if self.__inout_state == 'in': From 9981fb059ed90af7785111cde9e56f9e41349db6 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 10:01:18 +0200 Subject: [PATCH 35/37] Deal with "Given and returned" documentation sections --- cython_numpy_auto/code_analyzer.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index fb4f405..e2555ce 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -30,9 +30,18 @@ def __init__(self, doc): def input(self): if self.__input is None: self.__input = [] - result = re.search("Given[^\n]*:\n(.+?) \n", self.doc, re.DOTALL) + result = re.search("Given([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - __input = result.group(1) + print(result.group(1)) + __input = result.group(2) + for i in __input.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__input.append(arg_doc) + result = re.search("Given and returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) + if result is not None: + print(result.group(1)) + __input = result.group(2) for i in __input.split("\n"): arg_doc = ArgumentDoc(i) if arg_doc.name is not None: @@ -43,9 +52,18 @@ def input(self): def output(self): if self.__output is None: self.__output = [] - result = re.search("Returned:\n(.+?) \n", self.doc, re.DOTALL) + result = re.search("Returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) + if result is not None: + print(result.group(1)) + __output = result.group(2) + for i in __output.split("\n"): + arg_doc = ArgumentDoc(i) + if arg_doc.name is not None: + self.__output.append(arg_doc) + result = re.search("Given and returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - __output = result.group(1) + print(result.group(1)) + __output = result.group(2) for i in __output.split("\n"): arg_doc = ArgumentDoc(i) if arg_doc.name is not None: From a85fcf9bcd5cd494dbc2ce52caf8ef03af57dfa3 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 10:39:31 +0200 Subject: [PATCH 36/37] Remove unwanted print statements --- cython_numpy_auto/code_analyzer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cython_numpy_auto/code_analyzer.py b/cython_numpy_auto/code_analyzer.py index e2555ce..bde0348 100644 --- a/cython_numpy_auto/code_analyzer.py +++ b/cython_numpy_auto/code_analyzer.py @@ -32,7 +32,6 @@ def input(self): self.__input = [] result = re.search("Given([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - print(result.group(1)) __input = result.group(2) for i in __input.split("\n"): arg_doc = ArgumentDoc(i) @@ -40,7 +39,6 @@ def input(self): self.__input.append(arg_doc) result = re.search("Given and returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - print(result.group(1)) __input = result.group(2) for i in __input.split("\n"): arg_doc = ArgumentDoc(i) @@ -54,7 +52,6 @@ def output(self): self.__output = [] result = re.search("Returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - print(result.group(1)) __output = result.group(2) for i in __output.split("\n"): arg_doc = ArgumentDoc(i) @@ -62,7 +59,6 @@ def output(self): self.__output.append(arg_doc) result = re.search("Given and returned([^\n]*):\n(.+?) \n", self.doc, re.DOTALL) if result is not None: - print(result.group(1)) __output = result.group(2) for i in __output.split("\n"): arg_doc = ArgumentDoc(i) From c43c83cd3f1e37d680cda0714a509320be6c6260 Mon Sep 17 00:00:00 2001 From: Julien Woillez Date: Thu, 31 Jul 2014 10:41:02 +0200 Subject: [PATCH 37/37] Identify sections/subsections and process only Astronomy section --- cython_numpy_auto/cython_generator.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cython_numpy_auto/cython_generator.py b/cython_numpy_auto/cython_generator.py index 8809d1b..3b227fe 100644 --- a/cython_numpy_auto/cython_generator.py +++ b/cython_numpy_auto/cython_generator.py @@ -22,12 +22,19 @@ def surround(a_list, pre, post): #Extract all the ERFA function names from erfa.h with open(ERFA_SOURCES+"/erfa.h", "r") as f: - func_names = re.findall(' (\w+)\(.*?\);', f.read(), flags=re.DOTALL) + + erfa_h = f.read() + funcs = [] - for name in func_names: - print("Parsing {0}...".format(name)) - funcs.append(Function(name, ERFA_SOURCES)) - print("Done!") + section_subsection_functions = re.findall('/\* (\w*)/(\w*) \*/\n(.*?)\n\n', erfa_h, flags=re.DOTALL|re.MULTILINE) + for section, subsection, functions in section_subsection_functions: + print("{0}.{1}".format(section, subsection)) + if section == "Astronomy": + func_names = re.findall(' (\w+)\(.*?\);', functions, flags=re.DOTALL) + for name in func_names: + print("{0}.{1}.{2}...".format(section, subsection, name)) + funcs.append(Function(name, ERFA_SOURCES)) + print("Done!") #Render the template and save erfa_pyx = erfa_pyx_in.render(funcs=funcs) with open("erfa.pyx", "w") as f: