demo version
This commit is contained in:
parent
fbb282a801
commit
672d6daa8e
125 changed files with 17918 additions and 1481 deletions
|
@ -0,0 +1,216 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
###############################################################################/
|
||||
# Copyright (c) 2021 Nerian Vision GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
###############################################################################/
|
||||
|
||||
#
|
||||
# This helper script auto-generates pydoc comments (Google-style syntax)
|
||||
# from the Doxygen comments in the specified Nerian headers.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
def print_error(what):
|
||||
sys.stderr.write(what + '\n')
|
||||
|
||||
class RegexMatcher(object):
|
||||
def __init__(self):
|
||||
self.result = None
|
||||
def search(self, regex, where):
|
||||
self.result = re.search(regex, where)
|
||||
return self.result is not None
|
||||
def group(self, i=0):
|
||||
return self.result.group(i)
|
||||
def groups(self):
|
||||
return self.result.groups()
|
||||
|
||||
class DocstringExtractor(object):
|
||||
def __init__(self):
|
||||
self.docstrings = {}
|
||||
pass
|
||||
|
||||
def snake_case(self, fnname):
|
||||
'''Convert mixed case to Python methods' snake case'''
|
||||
fnname_snake = ''
|
||||
for c in fnname:
|
||||
if c.isupper():
|
||||
fnname_snake += '_' + c.lower()
|
||||
else:
|
||||
fnname_snake += c
|
||||
# Some conventional exceptions :)
|
||||
fnname_snake = fnname_snake.replace('r_o_i', 'roi')
|
||||
return fnname_snake
|
||||
|
||||
def beautified_docstring(self, comment, indent=8):
|
||||
ds = ''
|
||||
cs = [l.strip() for l in comment.split('\n')] # if l.strip()!='']
|
||||
# remove leading blank lines
|
||||
reallines = list(filter(lambda x: x>0, [c!='' for c in cs]))
|
||||
if len(reallines):
|
||||
cs = cs[reallines[0]:]
|
||||
#
|
||||
printed_kwarg = False
|
||||
extra_indent = 0
|
||||
for i, c in enumerate(cs):
|
||||
if c.strip() == '':
|
||||
extra_indent = 0
|
||||
next_is_param = False
|
||||
cnew = ''
|
||||
increase_extra_indent = 0
|
||||
for j, w in enumerate(c.split()):
|
||||
if w in ['\\brief', '\\c']:
|
||||
pass
|
||||
elif w in ['\\return']:
|
||||
ds += '\n'
|
||||
ds += ' '*indent + 'Returns:\n'
|
||||
extra_indent = 4
|
||||
increase_extra_indent = 4
|
||||
elif w in ['\\param']:
|
||||
if not printed_kwarg:
|
||||
ds += ' '*indent + 'Args:\n'
|
||||
extra_indent = 4
|
||||
increase_extra_indent = 4
|
||||
printed_kwarg = True
|
||||
next_is_param = True
|
||||
pass
|
||||
elif w.startswith('\\'):
|
||||
cnew += (' ' if len(cnew) else '') + w[1].upper()+w[2:]+': '
|
||||
else:
|
||||
cnew += (' ' if len(cnew) else '') + w
|
||||
if next_is_param:
|
||||
cnew += ':'
|
||||
next_is_param = False
|
||||
ds += ' '*indent + ' '*extra_indent + ("'''" if i==0 else "") + cnew + ("'''\n" if i==len(cs)-1 else "")
|
||||
ds += '\n'
|
||||
extra_indent += increase_extra_indent
|
||||
return ds
|
||||
|
||||
def generate(self, basedir, filename):
|
||||
with open(basedir + '/' + filename, 'r') as f:
|
||||
in_comment = False
|
||||
comment = ''
|
||||
names = []
|
||||
currentname = ''
|
||||
currentargs = ''
|
||||
level = 0
|
||||
restl =''
|
||||
for rawl in [ll.strip() for ll in f.readlines()]:
|
||||
l = restl + rawl
|
||||
had_restl = len(restl) > 0
|
||||
restl = ''
|
||||
apply_comment = False
|
||||
if in_comment:
|
||||
end = l.find('*/')
|
||||
thisline = (l if end<0 else l[:end]).lstrip('*').strip()
|
||||
#if thisline != '':
|
||||
comment += '\n' + thisline
|
||||
if end >= 0:
|
||||
in_comment = False
|
||||
else:
|
||||
start = l.find('/**')
|
||||
if start >= 0:
|
||||
currentname = '' # force finding new name
|
||||
currentargs = ''
|
||||
in_comment = True
|
||||
comment = l[start+3:]
|
||||
else:
|
||||
rem = RegexMatcher()
|
||||
if rem.search(r'(namespace|class|enum)([^:]*).*[{;]', l):
|
||||
if comment != '':
|
||||
cls = rem.group(2).strip().split()[-1]
|
||||
currentname = cls
|
||||
currentargs = ''
|
||||
apply_comment = True
|
||||
elif rem.search(r'[ \t]*(.*)\(', l): # match word and opening paren
|
||||
if currentname == '':
|
||||
cls = rem.group(1).strip().split()[-1]
|
||||
currentname = cls
|
||||
if rem.search(r'[ \t]*([^(]*)\((.*)\).*[{;]', l) and l.count('(') == l.count(')'): #: # match function
|
||||
if l.count('(') == l.count(')'):
|
||||
# reduce argument list (just names, no types or defaults)
|
||||
args_just_names = [(a.split('=')[0].strip().split()[-1] if a.strip()!='' else '') for a in rem.group(2).split(',')]
|
||||
currentargs = '(' + (', '.join(args_just_names)) + ')'
|
||||
if comment != '':
|
||||
apply_comment = True
|
||||
else: # match partial fn or something like it
|
||||
restl = l # save line for next iteration
|
||||
continue # and proceed to next line
|
||||
else:
|
||||
pass
|
||||
if apply_comment:
|
||||
ns = names + [currentname+currentargs]
|
||||
ns = [n for n in ns if n!='']
|
||||
name = '::'.join(ns)
|
||||
if name in self.docstrings and len(ns)>1: # warn, but not for the namespace doc
|
||||
print_error('Note: not overwriting previous docstring for '+name)
|
||||
else:
|
||||
self.docstrings[name] = self.beautified_docstring(comment, indent=8)
|
||||
|
||||
comment = ''
|
||||
for j in range(l.count('{')):
|
||||
level += 1
|
||||
names.append(currentname+currentargs)
|
||||
currentname = ''
|
||||
currentargs = ''
|
||||
for j in range(l.count('}')):
|
||||
level -= 1
|
||||
names = names[:-1]
|
||||
currentname = ''
|
||||
currentargs = ''
|
||||
|
||||
def store_docstrings_to_file(self, filename='', fobj=None):
|
||||
f = open(filename, 'w') if fobj is None else fobj
|
||||
f.write('''
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !! CAUTION !!
|
||||
# !! !!
|
||||
# !! This file is autogenerated from the libvisiontransfer headers !!
|
||||
# !! using autogen_docstrings.py - manual changes are not permanent !!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!''' + '\n\n')
|
||||
f.write('_NERIAN_COMPILED_DOCSTRINGS = {\n')
|
||||
for name, comment in self.docstrings.items():
|
||||
f.write(" '"+ name + "': \\\n")
|
||||
f.write(comment.rstrip('\n') + ',\n')
|
||||
f.write('}\n')
|
||||
f.write("\n# Also add parameter-less versions for convenience (duplicates overwritten)\n")
|
||||
f.write("for __k in list(_NERIAN_COMPILED_DOCSTRINGS):\n")
|
||||
f.write(" if __k.count('('):\n")
|
||||
f.write(" _NERIAN_COMPILED_DOCSTRINGS[__k.split('(')[0]] = _NERIAN_COMPILED_DOCSTRINGS[__k]\n\n")
|
||||
if fobj is not None:
|
||||
f.close()
|
||||
|
||||
if __name__=='__main__':
|
||||
basedir = os.getenv("LIBVISIONTRANSFER_SRCDIR", '../..')
|
||||
if os.path.isdir(basedir):
|
||||
d = DocstringExtractor()
|
||||
for filename in [
|
||||
'visiontransfer/deviceparameters.h',
|
||||
'visiontransfer/imageset.h',
|
||||
'visiontransfer/imageprotocol.h',
|
||||
'visiontransfer/imagetransfer.h',
|
||||
'visiontransfer/asynctransfer.h',
|
||||
'visiontransfer/deviceenumeration.h',
|
||||
'visiontransfer/deviceinfo.h',
|
||||
'visiontransfer/sensordata.h',
|
||||
'visiontransfer/datachannelservice.h',
|
||||
'visiontransfer/reconstruct3d.h',
|
||||
]:
|
||||
d.generate(basedir, filename)
|
||||
|
||||
d.store_docstrings_to_file('visiontransfer_src/visiontransfer_docstrings_autogen.py')
|
||||
else:
|
||||
print("Could not open library base dir, please set a correct LIBVISIONTRANSFER_SRCDIR")
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
###############################################################################/
|
||||
# Copyright (c) 2021 Nerian Vision GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
###############################################################################/
|
||||
|
||||
#
|
||||
# This helper script auto-generates adapters for all current
|
||||
# Nerian stereo device parameters directly from the C++ header file.
|
||||
#
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
class Generator(object):
|
||||
def __init__(self):
|
||||
self.pxdcode = \
|
||||
'''
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !! CAUTION !!
|
||||
# !! !!
|
||||
# !! This file is autogenerated from the libvisiontransfer headers !!
|
||||
# !! using autogen.py - manual changes are not permanent! !!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
cdef extern from "visiontransfer/deviceparameters.h" namespace "visiontransfer":
|
||||
cdef cppclass DeviceParameters:
|
||||
DeviceParameters(const DeviceInfo &) except +'''.split('\n')
|
||||
self.pyxcode = \
|
||||
'''# distutils: language=c++
|
||||
# cython: language_level=3
|
||||
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !! CAUTION !!
|
||||
# !! !!
|
||||
# !! This file is autogenerated from the libvisiontransfer headers !!
|
||||
# !! using autogen.py - manual changes are not permanent! !!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
from libcpp.string cimport string
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp cimport bool
|
||||
from cython cimport view
|
||||
|
||||
cdef class DeviceParameters:
|
||||
'''.split('\n')
|
||||
|
||||
self.pyxcode2 = \
|
||||
'''
|
||||
cdef cpp.DeviceParameters* c_obj
|
||||
|
||||
def __cinit__(self, DeviceInfo device_info):
|
||||
self.c_obj = new cpp.DeviceParameters(device_info.c_obj)
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.c_obj
|
||||
'''.split('\n')
|
||||
|
||||
def add_pxd(self, ret, fnname, argstr):
|
||||
args = [p.strip().split() for p in argstr.split(',')]
|
||||
# remove default arguments in pxd (present in pyx)
|
||||
for a in args:
|
||||
if len(a)>1:
|
||||
a[1] = a[1].split('=')[0]
|
||||
self.pxdcode.append(' '*8 + ret + ' ' + fnname + ' ('+(', '.join((a[0]+' '+a[1]) for a in args if len(a)>1))+') except +')
|
||||
|
||||
def add_pyx(self, ret, fnname, argstr, comment):
|
||||
# Generate function name reference also used by doc extractor
|
||||
args_just_names = [(a.split('=')[0].strip().split()[-1] if a.strip()!='' else '') for a in argstr.split(',')]
|
||||
currentname = 'visiontransfer::DeviceParameters::' + fnname + '(' + (', '.join(args_just_names)) + ')'
|
||||
fnname_snake = self.snake_case(fnname)
|
||||
args = [p.strip().split() for p in argstr.split(',')]
|
||||
for i in range(len(args)):
|
||||
if len(args[i])>0:
|
||||
if args[i][0] in ['int', 'float', 'double', 'bool', 'int&', 'float&', 'double&', 'bool&']:
|
||||
pass
|
||||
else:
|
||||
args[i][0] = "cpp." + str(args[i][0])
|
||||
if fnname.startswith('set'):
|
||||
argstr = ', '.join(' '.join(a) for a in args if len(a)>0)
|
||||
self.pyxcode.append(' '*4 + 'def '+ fnname_snake + '(self' + (', ' if len(argstr) else '') + argstr + '):')
|
||||
self.pyxcode.append(' '*8 + '_SUBSTITUTE_DOCSTRING_FOR_("' + currentname + '")')
|
||||
self.pyxcode.append(' '*8 + 'self.c_obj.'+ fnname + '(' + ', '.join(a[1].split('=')[0] for a in args if len(a)>1) + ')')
|
||||
self.pyxcode.append(' '*0) # extra newline to visually separate blocks
|
||||
pass
|
||||
else:
|
||||
argstr = '' #', '.join(' '.join(a) for a in args if len(a)>0)
|
||||
newargstr_defaults = ', '.join(a[1] for a in args if len(a)>0)
|
||||
newargstr_nodefaults = ', '.join(a[1].split('=')[0] for a in args if len(a)>0)
|
||||
if all(' '.join(a).find('&')<0 for a in args): #len(args)==0 or len(args[0])==0:
|
||||
if ret in ['int', 'float', 'double', 'bool', 'int&', 'float&', 'double&', 'bool&']:
|
||||
ret = ''
|
||||
ret_post = ''
|
||||
else:
|
||||
ret += '('
|
||||
ret_post = ')'
|
||||
self.pyxcode.append(' '*4 + 'def '+ fnname_snake + '(self' + (', ' if len(newargstr_defaults) else '') + newargstr_defaults + '):')
|
||||
self.pyxcode.append(' '*8 + '_SUBSTITUTE_DOCSTRING_FOR_("' + currentname + '")')
|
||||
self.pyxcode.append(' '*8 + 'return '+ret+'self.c_obj.'+ fnname + '(' + newargstr_nodefaults + ')' + ret_post)
|
||||
else:
|
||||
self.pyxcode.append(' '*4 + 'def '+ fnname_snake + '(self' + (', ' if len(argstr) else '') + argstr + '):')
|
||||
self.pyxcode.append(' '*8 + '_SUBSTITUTE_DOCSTRING_FOR_("' + currentname + '")')
|
||||
for a in args:
|
||||
rawtype = a[0].replace('&', '')
|
||||
var = a[1] if a[1].find('=')>0 else (a[1]+' = 0')
|
||||
self.pyxcode.append(' '*8 + 'cdef '+rawtype+' '+var)
|
||||
self.pyxcode.append(' '*8 + 'self.c_obj.'+ fnname + '(' + newargstr_nodefaults + ')')
|
||||
self.pyxcode.append(' '*8 + 'return '+newargstr_nodefaults)
|
||||
self.pyxcode.append(' '*0) # extra newline to visually separate blocks
|
||||
|
||||
def snake_case(self, fnname):
|
||||
'''Convert mixed case to Python methods' snake case'''
|
||||
fnname_snake = ''
|
||||
for c in fnname:
|
||||
if c.isupper():
|
||||
fnname_snake += '_' + c.lower()
|
||||
else:
|
||||
fnname_snake += c
|
||||
# Some conventional exceptions :)
|
||||
fnname_snake = fnname_snake.replace('r_o_i', 'roi')
|
||||
return fnname_snake
|
||||
|
||||
def generate(self, basedir):
|
||||
with open(basedir + '/visiontransfer/deviceparameters.h', 'r') as f:
|
||||
in_comment = False
|
||||
comment = ''
|
||||
level = 0
|
||||
for l in [ll.strip() for ll in f.readlines()]:
|
||||
if in_comment:
|
||||
end = l.find('*/')
|
||||
thisline = (l if end<0 else l[:end]).lstrip('*').strip()
|
||||
if thisline != '':
|
||||
comment += '\n' + thisline
|
||||
if end >= 0:
|
||||
in_comment = False
|
||||
else:
|
||||
start = l.find('/**')
|
||||
if start >= 0:
|
||||
in_comment = True
|
||||
comment = l[start+3:]
|
||||
else:
|
||||
if level==1 and l.find(' DeviceParameters {') >= 0:
|
||||
# insert class docstring
|
||||
self.pyxcode.append(' '*4 + '_SUBSTITUTE_DOCSTRING_FOR_("visiontransfer::DeviceParameters")')
|
||||
self.pyxcode.extend(self.pyxcode2)
|
||||
self.pyxcode2 = []
|
||||
comment = ''
|
||||
elif level==2 and l.find('(') >= 0 and l.find('{') > 0 and (l.find('get') > 0 or l.find('set') > 0):
|
||||
ret = l.split()[0]
|
||||
fnname = l.split()[1].split('(')[0]
|
||||
args = l.split('(')[1].split(')')[0]
|
||||
self.add_pxd(ret, fnname, args)
|
||||
self.add_pyx(ret, fnname, args, comment)
|
||||
comment = ''
|
||||
else:
|
||||
pass
|
||||
level += l.count('{')
|
||||
level -= l.count('}')
|
||||
|
||||
if __name__=='__main__':
|
||||
basedir = os.getenv("LIBVISIONTRANSFER_SRCDIR", '../..')
|
||||
if os.path.isdir(basedir):
|
||||
g = Generator()
|
||||
g.generate(basedir)
|
||||
pathlib.Path("visiontransfer").mkdir(parents=True, exist_ok=True)
|
||||
with open('visiontransfer/visiontransfer_parameters_cpp_autogen.pxd', 'w') as f:
|
||||
f.write('\n'.join(g.pxdcode))
|
||||
with open('visiontransfer/visiontransfer_parameters_autogen.pyx.in', 'w') as f:
|
||||
f.write('\n'.join(g.pyxcode))
|
||||
else:
|
||||
print("Could not open library base dir, please set a correct LIBVISIONTRANSFER_SRCDIR")
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
###############################################################################/
|
||||
# Copyright (c) 2021 Nerian Vision GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
###############################################################################/
|
||||
|
||||
#
|
||||
# This helper script constructs commented Cython .pxd/.pyx source files
|
||||
# from their .in templates, utilizing the docstrings generated earlier.
|
||||
#
|
||||
|
||||
import re
|
||||
|
||||
from visiontransfer_src.visiontransfer_docstrings_autogen import _NERIAN_COMPILED_DOCSTRINGS
|
||||
|
||||
def get_docstring(what):
|
||||
if what is None:
|
||||
sys.stderr.write('IMPLEMENT_ME: missing docstring link')
|
||||
doc = 'IMPLEMENT_ME: missing docstring link'
|
||||
elif what in _NERIAN_COMPILED_DOCSTRINGS:
|
||||
doc = _NERIAN_COMPILED_DOCSTRINGS[what]
|
||||
else:
|
||||
doc = '(No extra documentation for ' + what + ')'
|
||||
#partial_matches = [k for k in _NERIAN_COMPILED_DOCSTRINGS.keys() if k.startswith(what.split('(')[0])]
|
||||
#doc += '\nCandidates:'
|
||||
#for p in partial_matches:
|
||||
# doc += '\n "'+p+'"'
|
||||
return doc
|
||||
|
||||
def process_infile_to_outfile(infilename, outfilename):
|
||||
with open(infilename, 'r') as infile:
|
||||
with open(outfilename, 'w') as outfile:
|
||||
outfile.write( \
|
||||
'''# distutils: language=c++
|
||||
# cython: language_level=3
|
||||
|
||||
########################################
|
||||
## Autogenerated file. Do not change! ##
|
||||
## Work on its .in template instead ##
|
||||
## (found inside visiontransfer_src). ##
|
||||
########################################
|
||||
|
||||
''')
|
||||
# looking for docstring substitution sites, the C++ docs are translated
|
||||
# to docstring-like docs, and optionally prepended by a note. The same
|
||||
# indentation that the macro has is used for the entire docstring.
|
||||
# Syntax: _SUBSTITUTE_DOCSTRING_FOR_("CppNamespace::CppClassOrFn"[, "Python note"])
|
||||
for line in infile.readlines():
|
||||
if line.find('_SUBSTITUTE_DOCSTRING_FOR_(') >= 0:
|
||||
toks = line.split('"')
|
||||
what = toks[1]
|
||||
notelines = [] if len(toks)<4 else ([''] + list(toks[3].split('\n')) + ['']) # extra Python-only note
|
||||
m = re.match(r'([ \t]*)', line)
|
||||
whitespace = m.group(1) if m else ''
|
||||
whitespace_len = len(whitespace) # common indent
|
||||
clines = get_docstring(what).split('\n')
|
||||
alllines = notelines + clines
|
||||
for i, cl in enumerate(alllines):
|
||||
if i==0:
|
||||
printline = whitespace + ("'''" if i==0 else '') + cl + ("'''" if i==len(alllines)-1 else '')
|
||||
else:
|
||||
printline = cl + ("'''" if i==len(alllines)-1 else '')
|
||||
outfile.write(printline + '\n')
|
||||
else:
|
||||
outfile.write(line)
|
||||
|
||||
if __name__=='__main__':
|
||||
process_infile_to_outfile('visiontransfer_src/visiontransfer.pyx.in', 'visiontransfer/visiontransfer.pyx')
|
||||
process_infile_to_outfile('visiontransfer_src/visiontransfer_cpp.pxd.in', 'visiontransfer/visiontransfer_cpp.pxd')
|
||||
process_infile_to_outfile('visiontransfer/visiontransfer_parameters_autogen.pyx.in', 'visiontransfer/visiontransfer_parameters_autogen.pyx')
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue