-
Notifications
You must be signed in to change notification settings - Fork 2
Auto-generated cython_numpy wrapper #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 12 commits
627719c
b815ba7
f1b7ce9
3209705
64e3068
0cdfa62
9c9d296
3dbc8f8
7523521
2a60933
6e653c1
bcbe709
d1dcc3b
729d2e0
b6da8b8
1ec74bc
940753a
41cbfe1
7506ff0
7bbcabb
922cba1
fa3612b
1ebb796
3d35218
0de22b9
763c08c
d03c0c1
c9be43e
56d80ca
e3ccefd
e641fd4
dade3e0
e16ff9a
080325d
9981fb0
a85fcf9
c43c83c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,10 @@ | ||
| /build | ||
|
|
||
| .project | ||
|
|
||
| .pydevproject | ||
|
|
||
| *.py[cod] | ||
|
|
||
| cython_numpy_auto/erfa.c | ||
|
|
||
| cython_numpy_auto/erfa.pyx |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm following along correctly, this means that it's opening the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. The reason for parsing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah -- having gone and looked at the SOFA/ERFA code, I see what you're saying now, and I see why that's the only real alternative. Not to sound like a curmudgeon, but SOFA is totally doing it wrong. The .h files should include the documentation, not the .c files. At the end of the day, it probably matters little, because I suspect we would generate the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tend to agree with you on the documentation location. We could also relocate the documentation when deriving ERFA from SOFA. This could help those that will use or develop with ERFA directly. Will open a separate issue against ERFA.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You already did! :) |
||
| 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)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm impressed that we're able to get away with regexs to parse the argument types here -- I suppose that's fine given the regularity of ERFA. We could take this one step further and actually find what's in the headers and generate functions for everything -- and use something like pycparser to get that done. But this is probably fine for now.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the |
||
|
|
||
| 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 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this is now the generated file? It should probably be moved so it doesn't clobber the manually written one already in the repo at this location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, no. I have just improved the
cython_numpyapproach independently from the auto-generation. One of the goal was to add support foraper, one of the tricky wrapper as it has an inout parameter, and also uses this eraASTROM structure.If you want to see the auto-generated wrapper, your have to run
cython_generator.pyin thepython_numpy_autoexperiment.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I see, that's fine.