gen.py 81 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270
  1. #!/usr/bin/env python3
  2. """
  3. Copyright (C) 2020 by
  4. The Salk Institute for Biological Studies
  5. Use of this source code is governed by an MIT-style
  6. license that can be found in the LICENSE file or at
  7. https://opensource.org/licenses/MIT.
  8. """
  9. # TODO: change 'items' to 'attributes'?
  10. # TODO: cleanup const/nonconst handling
  11. # TODO: unify superclass vs superclasses -
  12. # w superclasses, the objects are inherited manually now and it should be made automatic
  13. # or maybe just rename it..
  14. # TODO: print unset constants as UNSET, not diffusion_constant_2d=3.40282e+38
  15. import sys
  16. import os
  17. import yaml
  18. import re
  19. from datetime import datetime
  20. from copy import copy
  21. from pyexpat import model
  22. VERBOSE = False # may be overridden by argument -v
  23. from constants import *
  24. import doc
  25. # --- some global data ---
  26. g_enums = set()
  27. # ------------------------
  28. def rename_cpp_reserved_id(name):
  29. if (name == 'union'):
  30. return 'union_'
  31. else:
  32. return name
  33. def get_underscored(class_name):
  34. return re.sub(r'(?<!^)(?=[A-Z])', '_', class_name).lower()
  35. def get_gen_header_guard_name(class_name):
  36. return GEN_GUARD_PREFIX + get_underscored(class_name).upper() + GUARD_SUFFIX
  37. def get_api_header_guard_name(class_name):
  38. return API_GUARD_PREFIX + get_underscored(class_name).upper() + GUARD_SUFFIX
  39. def get_gen_class_file_name(class_name, extension):
  40. return GEN_PREFIX + get_underscored(class_name) + '.' + extension
  41. def get_gen_class_file_name_w_dir(class_name, extension):
  42. # using '/' intentionally to generate the same output on Windows and Linux/Mac
  43. return TARGET_DIRECTORY + '/' + get_gen_class_file_name(class_name, extension)
  44. def get_api_class_file_name(class_name, extension):
  45. return get_underscored(class_name) + '.' + extension
  46. def get_api_class_file_name_w_dir(class_name, extension):
  47. if class_name == "Config":
  48. # config.h is be called api_config.h due to include collisions with MSVC
  49. class_name = 'ApiConfig'
  50. return API_DIRECTORY + '/' + get_api_class_file_name(class_name, extension)
  51. def get_api_class_file_name_w_work_dir(class_name, extension):
  52. return WORK_DIRECTORY + '/' + get_api_class_file_name(class_name, extension)
  53. def get_as_shared_ptr(class_name):
  54. return SHARED_PTR + '<' + class_name + '>'
  55. def get_copy_function_name(class_name):
  56. return COPY_NAME + '_' + get_underscored(class_name)
  57. def get_deepcopy_function_name(class_name):
  58. return DEEPCOPY_NAME + '_' + get_underscored(class_name)
  59. def is_yaml_list_type(t):
  60. return t.startswith(YAML_TYPE_LIST)
  61. def is_yaml_dict_type(t):
  62. return t.startswith(YAML_TYPE_DICT)
  63. def is_yaml_function_type(t):
  64. return t.startswith(YAML_TYPE_FUNCTION)
  65. # rename inner to underlying?
  66. def get_inner_list_type(t):
  67. if is_yaml_list_type(t):
  68. return t[len(YAML_TYPE_LIST)+1:-1]
  69. else:
  70. return t
  71. def get_inner_dict_key_type(t):
  72. if is_yaml_dict_type(t):
  73. return t.replace('[', ',').replace(']', ',').split(',')[1].strip()
  74. else:
  75. return t
  76. def get_inner_dict_value_type(t):
  77. if is_yaml_dict_type(t):
  78. return t.replace('[', ',').replace(']', ',').split(',')[2].strip()
  79. else:
  80. return t
  81. def get_inner_function_type(t):
  82. # callbacks pass back only one shared_ptr argument
  83. if is_yaml_function_type(t):
  84. return t.split('<')[2].split('>')[0].strip()
  85. else:
  86. return t
  87. def get_first_inner_type(t):
  88. if is_yaml_list_type(t):
  89. return get_inner_list_type(t)
  90. elif is_yaml_dict_type(t):
  91. return get_inner_dict_key_type(t)
  92. elif is_yaml_function_type(t):
  93. return get_inner_function_type(t)
  94. else:
  95. return t
  96. # returns true also for enums
  97. def is_base_yaml_type(t):
  98. return \
  99. t == YAML_TYPE_FLOAT or t == YAML_TYPE_STR or t == YAML_TYPE_INT or t == YAML_TYPE_UINT64 or t == YAML_TYPE_UINT32 or \
  100. t == YAML_TYPE_BOOL or t == YAML_TYPE_VEC2 or t == YAML_TYPE_VEC3 or t == YAML_TYPE_IVEC3 or \
  101. t == YAML_TYPE_PY_OBJECT or \
  102. (is_yaml_function_type(t) and is_base_yaml_type(get_inner_function_type(t))) or \
  103. (is_yaml_list_type(t) and is_base_yaml_type(get_inner_list_type(t))) or \
  104. (is_yaml_dict_type(t) and is_base_yaml_type(get_inner_dict_key_type(t)) and is_base_yaml_type(get_inner_dict_value_type(t)))
  105. def is_yaml_ptr_type(t):
  106. if t == YAML_TYPE_PY_OBJECT:
  107. return False
  108. else:
  109. return t[-1] == '*'
  110. def yaml_type_to_cpp_type(t, w_namespace=False):
  111. assert len(t) >= 1
  112. if t == YAML_TYPE_FLOAT:
  113. return CPP_TYPE_DOUBLE
  114. elif t == YAML_TYPE_STR:
  115. return CPP_TYPE_STR
  116. elif t == YAML_TYPE_INT:
  117. return CPP_TYPE_INT
  118. elif t == YAML_TYPE_UINT64:
  119. return CPP_TYPE_UINT64
  120. elif t == YAML_TYPE_UINT32:
  121. return CPP_TYPE_UINT32
  122. elif t == YAML_TYPE_BOOL:
  123. return CPP_TYPE_BOOL
  124. elif t == YAML_TYPE_VEC2:
  125. ns = '' if not w_namespace else 'MCell::'
  126. return ns + CPP_TYPE_VEC2
  127. elif t == YAML_TYPE_VEC3:
  128. ns = '' if not w_namespace else 'MCell::'
  129. return ns + CPP_TYPE_VEC3
  130. elif t == YAML_TYPE_IVEC3:
  131. ns = '' if not w_namespace else 'MCell::'
  132. return ns + CPP_TYPE_IVEC3
  133. elif is_yaml_list_type(t):
  134. assert len(t) > 7
  135. inner_type = yaml_type_to_cpp_type(get_inner_list_type(t), w_namespace)
  136. return CPP_VECTOR_TYPE + '<' + inner_type + '>'
  137. elif is_yaml_dict_type(t):
  138. key_type = yaml_type_to_cpp_type(get_inner_dict_key_type(t), w_namespace)
  139. value_type = yaml_type_to_cpp_type(get_inner_dict_value_type(t), w_namespace)
  140. return CPP_MAP_TYPE + '<' + key_type + ', ' + value_type + '>'
  141. else:
  142. ns = '' if not w_namespace else 'MCell::API::'
  143. if is_yaml_ptr_type(t):
  144. return SHARED_PTR + '<' + ns + t[0:-1] + '>'
  145. else:
  146. return ns + t # standard ttype
  147. def get_cpp_bool_string(val):
  148. if val == 'True':
  149. return 'true'
  150. elif val == 'False':
  151. return 'false'
  152. else:
  153. assert false
  154. def yaml_type_to_pybind_type(t):
  155. assert len(t) >= 1
  156. if t == YAML_TYPE_FLOAT:
  157. return 'float_'
  158. elif t == YAML_TYPE_STR:
  159. return YAML_TYPE_STR
  160. elif t == YAML_TYPE_BOOL:
  161. return CPP_TYPE_BOOL
  162. elif t == YAML_TYPE_INT:
  163. return CPP_TYPE_INT + '_'
  164. elif t == YAML_TYPE_UINT64:
  165. return CPP_TYPE_INT + '_'
  166. elif t == YAML_TYPE_UINT32:
  167. return CPP_TYPE_INT + '_'
  168. elif t == YAML_TYPE_SPECIES:
  169. return PYBIND_TYPE_OBJECT
  170. else:
  171. assert False, "Unsupported constant type " + t
  172. def yaml_type_to_py_type(t):
  173. assert len(t) >= 1
  174. if t == YAML_TYPE_UINT64 or t == YAML_TYPE_UINT32:
  175. return YAML_TYPE_INT # not sure what should be the name
  176. elif is_yaml_function_type(t):
  177. return "Callable, # " + t
  178. elif t == YAML_TYPE_PY_OBJECT:
  179. return "Any, # " + t
  180. elif is_yaml_list_type(t) and get_inner_list_type(t) == YAML_TYPE_UINT64:
  181. return t.replace(YAML_TYPE_UINT64, YAML_TYPE_INT)
  182. elif is_yaml_list_type(t) and get_inner_list_type(t) == YAML_TYPE_UINT32:
  183. return t.replace(YAML_TYPE_UINT32, YAML_TYPE_INT)
  184. else:
  185. return t.replace('*', '')
  186. def is_cpp_ptr_type(cpp_type):
  187. return cpp_type.startswith(SHARED_PTR)
  188. def is_cpp_ref_type(cpp_type):
  189. not_reference = \
  190. cpp_type in CPP_NONREFERENCE_TYPES or \
  191. is_cpp_ptr_type(cpp_type) or \
  192. cpp_type.startswith(CPP_VECTOR_TYPE) or \
  193. cpp_type in g_enums or \
  194. is_yaml_function_type(cpp_type) or \
  195. cpp_type == YAML_TYPE_PY_OBJECT
  196. return not not_reference
  197. def is_cpp_vector_type(cpp_type):
  198. return'std::vector' in cpp_type
  199. def get_type_as_ref_param(attr):
  200. assert KEY_TYPE in attr
  201. yaml_type = attr[KEY_TYPE]
  202. cpp_type = yaml_type_to_cpp_type(yaml_type)
  203. res = cpp_type
  204. if is_cpp_ref_type(cpp_type):
  205. res += '&'
  206. return res
  207. def get_default_or_unset_value(attr):
  208. assert KEY_TYPE in attr
  209. t = attr[KEY_TYPE]
  210. if KEY_DEFAULT in attr:
  211. default_value = attr[KEY_DEFAULT]
  212. if default_value != UNSET_VALUE and default_value != EMPTY_ARRAY:
  213. res = str(default_value)
  214. # might need to convert enum.value into enum::value
  215. if not is_base_yaml_type(t):
  216. res = res.replace('.', '::')
  217. elif t == YAML_TYPE_BOOL:
  218. res = get_cpp_bool_string(res)
  219. elif t == YAML_TYPE_STR:
  220. # default strings must be quoted (not the 'unset' one because the UNSET_STR is a constant)
  221. res = '"' + res + '"'
  222. return res
  223. if t == YAML_TYPE_FLOAT:
  224. return UNSET_VALUE_FLOAT
  225. elif t == YAML_TYPE_STR:
  226. return UNSET_VALUE_STR
  227. elif t == YAML_TYPE_INT:
  228. return UNSET_VALUE_INT
  229. elif t == YAML_TYPE_UINT64:
  230. return UNSET_VALUE_UINT64
  231. elif t == YAML_TYPE_UINT32:
  232. return UNSET_VALUE_UINT32
  233. elif t == YAML_TYPE_BOOL:
  234. assert False, "There is no unset value for bool - for " + attr[KEY_NAME]
  235. return "error"
  236. elif t == YAML_TYPE_VEC2:
  237. return UNSET_VALUE_VEC2
  238. elif t == YAML_TYPE_VEC3:
  239. return UNSET_VALUE_VEC3
  240. elif t == YAML_TYPE_IVEC3:
  241. return UNSET_VALUE_IVEC3
  242. elif t == YAML_TYPE_ORIENTATION:
  243. return UNSET_VALUE_ORIENTATION
  244. elif is_yaml_list_type(t):
  245. return yaml_type_to_cpp_type(t) + '()'
  246. elif is_yaml_dict_type(t):
  247. return yaml_type_to_cpp_type(t) + '()'
  248. else:
  249. return UNSET_VALUE_PTR
  250. def get_default_or_unset_value_py(attr):
  251. assert KEY_TYPE in attr
  252. t = attr[KEY_TYPE]
  253. if KEY_DEFAULT in attr:
  254. default_value = attr[KEY_DEFAULT]
  255. if default_value == "":
  256. return "''"
  257. elif default_value != UNSET_VALUE and default_value != EMPTY_ARRAY:
  258. return str(default_value)
  259. # might need to convert enum.value into enum::value
  260. #if not is_base_yaml_type(t):
  261. # res = res.replace('.', '::')
  262. #elif t == YAML_TYPE_BOOL:
  263. # res = get_cpp_bool_string(res)
  264. return PY_NONE
  265. def has_item_w_name(items, item_name):
  266. for item in items:
  267. if item_name == item[KEY_NAME]:
  268. return True
  269. return False
  270. def is_container_class(name):
  271. return \
  272. name == CLASS_NAME_MODEL or \
  273. name == CLASS_NAME_SUBSYSTEM or \
  274. name == CLASS_NAME_INSTANTIATION or \
  275. name == CLASS_NAME_OBSERVABLES
  276. def is_container_class_no_model(name):
  277. return is_container_class(name) and not name == CLASS_NAME_MODEL
  278. def write_generated_notice(f):
  279. now = datetime.now()
  280. # date printing is disabled during the development phase to minimize changes in files
  281. #date_time = now.strftime("%m/%d/%Y, %H:%M")
  282. #f.write('// This file was generated automatically on ' + date_time + ' from ' + '\'' + input_file_name + '\'\n\n')
  283. def write_ctor_decl(f, class_def, class_name, append_backslash, indent_and_fix_rst_chars, only_inherited, with_args=True):
  284. items = class_def[KEY_ITEMS] if KEY_ITEMS in class_def else []
  285. backshlash = '\\' if append_backslash else ''
  286. f.write(indent_and_fix_rst_chars + class_name + '( ' + backshlash + '\n')
  287. inherited_items = [ attr for attr in items if is_inherited(attr) ]
  288. if only_inherited:
  289. # override also the original items list
  290. items = inherited_items
  291. # ctor parameters
  292. if with_args:
  293. num_items = len(items)
  294. for i in range(num_items):
  295. attr = items[i]
  296. if not only_inherited and attr_not_in_ctor(attr):
  297. continue
  298. assert KEY_NAME in attr
  299. name = attr[KEY_NAME]
  300. const_spec = 'const ' if not is_yaml_ptr_type(attr[KEY_TYPE]) else ''
  301. f.write(indent_and_fix_rst_chars + ' ' + const_spec + get_type_as_ref_param(attr) + ' ' + name + '_')
  302. if KEY_DEFAULT in attr:
  303. f.write(' = ' + get_default_or_unset_value(attr))
  304. if i != num_items - 1:
  305. f.write(',')
  306. f.write(' ' + backshlash + '\n')
  307. f.write(indent_and_fix_rst_chars + ') ')
  308. if has_superclass_other_than_base(class_def):
  309. # call superclass ctor
  310. # only one, therefore all inherited attributes are its arguments
  311. if only_inherited:
  312. # we are generating ctor for the superclass of the Gen class, e.g. for GenSpecies
  313. # and we need to initialize Complex
  314. superclass_name = class_def[KEY_SUPERCLASS]
  315. else:
  316. # we are generating ctor for the superclass, e.g. for Species and we need to initialize
  317. # GenSpecies
  318. superclass_name = GEN_CLASS_PREFIX + class_name
  319. f.write(' : ' + superclass_name + '(')
  320. num_inherited_items = len(inherited_items)
  321. for i in range(num_inherited_items):
  322. f.write(inherited_items[i][KEY_NAME] + '_')
  323. if i != num_inherited_items - 1:
  324. f.write(',')
  325. f.write(') ')
  326. def needs_default_ctor(class_def, only_inherited):
  327. if KEY_ITEMS not in class_def:
  328. return False
  329. items = class_def[KEY_ITEMS]
  330. if only_inherited:
  331. items = [ attr for attr in items if is_inherited(attr) ]
  332. if items:
  333. all_attrs_initialized = True
  334. for item in items:
  335. if not KEY_DEFAULT in item:
  336. all_attrs_initialized = False
  337. return not all_attrs_initialized
  338. else:
  339. return False
  340. def write_ctor_define(f, class_def, class_name):
  341. with_args = True
  342. if KEY_SUPERCLASS in class_def and class_def[KEY_SUPERCLASS] == BASE_INTROSPECTION_CLASS:
  343. with_args = False
  344. suffix = CTOR_SUFFIX if with_args else CTOR_NOARGS_SUFFIX
  345. f.write('#define ' + get_underscored(class_name).upper() + suffix + '() \\\n')
  346. write_ctor_decl(f, class_def, class_name, append_backslash=True, indent_and_fix_rst_chars=' ', only_inherited=False, with_args=with_args)
  347. f.write('{ \\\n')
  348. # initialization code
  349. if not is_container_class_no_model(class_name):
  350. f.write(' ' + CLASS_NAME_ATTR + ' = "' + class_name + '"; \\\n')
  351. items = class_def[KEY_ITEMS] if KEY_ITEMS in class_def else []
  352. num_items = len(items)
  353. for i in range(num_items):
  354. if attr_not_in_ctor(items[i]):
  355. continue
  356. assert KEY_NAME in items[i]
  357. attr_name = items[i][KEY_NAME]
  358. if with_args:
  359. f.write(' ' + attr_name + ' = ' + attr_name + '_; \\\n')
  360. else:
  361. f.write(' ' + attr_name + ' = ' + get_default_or_unset_value(items[i]) + '; \\\n')
  362. if not is_container_class_no_model(class_name):
  363. f.write(' ' + CTOR_POSTPROCESS + '(); \\\n')
  364. f.write(' ' + CHECK_SEMANTICS + '(); \\\n')
  365. f.write(' } \\\n')
  366. # also generate empty ctor if needed
  367. f.write(' ' + class_name + '(' + DEFAULT_CTOR_ARG_TYPE + ')')
  368. if has_single_superclass(class_def):
  369. f.write(' : \\\n'
  370. ' ' + GEN_CLASS_PREFIX + class_name + '(' + DEFAULT_CTOR_ARG_TYPE + '()) ')
  371. f.write('{ \\\n')
  372. if has_single_superclass(class_def):
  373. f.write(' ' + SET_ALL_DEFAULT_OR_UNSET_DECL + '; \\\n')
  374. f.write(' ' + SET_ALL_CUSTOM_TO_DEFAULT_DECL + '; \\\n')
  375. f.write(' }\n\n');
  376. def write_ctor_for_superclass(f, class_def, class_name):
  377. write_ctor_decl(f, class_def, GEN_CLASS_PREFIX + class_name, append_backslash=False, indent_and_fix_rst_chars=' ', only_inherited=True)
  378. f.write(' {\n')
  379. f.write(' }\n')
  380. def write_attr_with_get_set(f, class_def, attr):
  381. assert KEY_NAME in attr, KEY_NAME + " is not set in " + str(attr)
  382. assert KEY_TYPE in attr, KEY_TYPE + " is not set in " + str(attr)
  383. name = attr[KEY_NAME]
  384. # skip attribute 'name'
  385. if name == ATTR_NAME_NAME:
  386. return False
  387. yaml_type = attr[KEY_TYPE]
  388. cpp_type = yaml_type_to_cpp_type(yaml_type)
  389. #decl_const = 'const ' if is_cpp_ptr_type(cpp_type) else ''
  390. decl_type = cpp_type
  391. # decl
  392. f.write(' ' + decl_type + ' ' + name + ';\n')
  393. # setter
  394. arg_type_const = 'const ' if not is_cpp_ptr_type(cpp_type) else ''
  395. f.write(' virtual void set_' + name + '(' + arg_type_const + get_type_as_ref_param(attr) + ' new_' + name + '_) {\n')
  396. # TODO: allow some values to be set even after initialization
  397. if has_single_superclass(class_def):
  398. f.write(' if (initialized) {\n')
  399. f.write(' throw RuntimeError("Value \'' + name + '\' of object with name " + name + " (class " + class_name + ") "\n')
  400. f.write(' "cannot be set after model was initialized.");\n')
  401. f.write(' }\n')
  402. if has_single_superclass(class_def):
  403. f.write(' ' + CACHED_DATA_ARE_UPTODATE_ATTR + ' = false;\n');
  404. f.write(' ' + name + ' = new_' + name + '_;\n')
  405. f.write(' }\n')
  406. # getter
  407. ret_type_ref = ''
  408. ret_type_const = 'const ' if is_cpp_ref_type(cpp_type) else ''
  409. method_const = 'const '
  410. # vectors are always returned as a non-const reference
  411. if is_cpp_vector_type(cpp_type):
  412. ret_type_ref = '&'
  413. ret_type_const = ''
  414. method_const = ''
  415. ret_type = get_type_as_ref_param(attr)
  416. f.write(' virtual ' + ret_type_const + ret_type + ret_type_ref + ' get_' + name + '() ' + method_const +'{\n')
  417. """
  418. # original approach to protect already initialized classes but this behavior woudl probably be more confusing
  419. if has_single_superclass(class_def) and is_cpp_vector_type(cpp_type):
  420. f.write(' if (initialized) {\n')
  421. f.write(' // any changes after initialization are ignored\n')
  422. f.write(' static ' + ret_type + ' copy = ' + name + ';\n')
  423. f.write(' return copy;\n')
  424. f.write(' }\n')
  425. """
  426. if has_single_superclass(class_def):
  427. f.write(' ' + CACHED_DATA_ARE_UPTODATE_ATTR + ' = false; // arrays and other data can be modified through getters\n');
  428. f.write(' return ' + name + ';\n')
  429. f.write(' }\n')
  430. return True
  431. def write_method_signature(f, method):
  432. if KEY_RETURN_TYPE in method:
  433. f.write(yaml_type_to_cpp_type(method[KEY_RETURN_TYPE]) + ' ')
  434. else:
  435. f.write('void ')
  436. assert KEY_NAME in method
  437. f.write(rename_cpp_reserved_id(method[KEY_NAME]) + '(')
  438. if KEY_PARAMS in method:
  439. params = method[KEY_PARAMS]
  440. params_cnt = len(params)
  441. for i in range(params_cnt):
  442. p = params[i]
  443. assert KEY_NAME in p
  444. assert KEY_TYPE in p
  445. t = get_type_as_ref_param(p)
  446. const_spec = 'const ' if not is_yaml_ptr_type(p[KEY_TYPE]) and p[KEY_TYPE] != YAML_TYPE_PY_OBJECT else ''
  447. f.write(const_spec + t + ' ' + p[KEY_NAME])
  448. if KEY_DEFAULT in p:
  449. f.write(' = ' + get_default_or_unset_value(p))
  450. if i != params_cnt - 1:
  451. f.write(', ')
  452. f.write(')')
  453. if KEY_IS_CONST in method and method[KEY_IS_CONST]:
  454. f.write(' const')
  455. def write_method_declaration(f, method):
  456. f.write(' virtual ')
  457. write_method_signature(f, method)
  458. f.write(' = 0;\n')
  459. def is_inherited(attr_or_method_def):
  460. if KEY_INHERITED in attr_or_method_def:
  461. return attr_or_method_def[KEY_INHERITED]
  462. else:
  463. return False
  464. def attr_not_in_ctor(attr_or_method_def):
  465. if KEY_NOT_AS_CTOR_ARG in attr_or_method_def:
  466. return attr_or_method_def[KEY_NOT_AS_CTOR_ARG]
  467. else:
  468. return False
  469. def has_single_superclass(class_def):
  470. if KEY_SUPERCLASS in class_def:
  471. return True
  472. else:
  473. return False
  474. def has_superclass_other_than_base(class_def):
  475. # TODO: also deal with superclasses?
  476. if has_single_superclass(class_def):
  477. return \
  478. class_def[KEY_SUPERCLASS] != BASE_DATA_CLASS and \
  479. class_def[KEY_SUPERCLASS] != BASE_INTROSPECTION_CLASS
  480. else:
  481. return False
  482. def write_gen_class(f, class_def, class_name, decls):
  483. gen_class_name = GEN_CLASS_PREFIX + class_name
  484. f.write('class ' + gen_class_name)
  485. if has_single_superclass(class_def):
  486. f.write(': public ' + class_def[KEY_SUPERCLASS])
  487. elif KEY_SUPERCLASSES in class_def:
  488. scs = class_def[KEY_SUPERCLASSES]
  489. assert scs
  490. f.write(': ')
  491. for i in range(len(scs)):
  492. f.write('public ' + scs[i])
  493. if i + 1 != len(scs):
  494. f.write(', ')
  495. elif is_container_class_no_model(class_name):
  496. f.write(': public ' + BASE_EXPORT_CLASS)
  497. f.write( ' {\n')
  498. f.write('public:\n')
  499. initialization_ctor_generated = False
  500. if has_superclass_other_than_base(class_def):
  501. write_ctor_for_superclass(f, class_def, class_name)
  502. initialization_ctor_generated = True
  503. # we need a 'default' ctor (with custom arg type) all the time,
  504. # however, there may not be a default ctor with no argument so we must define it
  505. # as well
  506. if needs_default_ctor(class_def, only_inherited=True) or not initialization_ctor_generated:
  507. f.write(' ' + GEN_CLASS_PREFIX + class_name + '() ')
  508. if has_superclass_other_than_base(class_def):
  509. superclass_name = class_def[KEY_SUPERCLASS]
  510. f.write(': ' + superclass_name + '(' + DEFAULT_CTOR_ARG_TYPE + '()) {\n')
  511. else:
  512. f.write('{\n')
  513. f.write(' }\n')
  514. f.write(' ' + GEN_CLASS_PREFIX + class_name + '(' + DEFAULT_CTOR_ARG_TYPE + ') {\n')
  515. f.write(' }\n')
  516. if not has_single_superclass(class_def):
  517. # generate virtual destructor
  518. f.write(' virtual ~' + gen_class_name + '() {}\n')
  519. if has_single_superclass(class_def):
  520. f.write(' ' + RET_CTOR_POSTPROCESS + ' ' + CTOR_POSTPROCESS + '() ' + KEYWORD_OVERRIDE + ' {}\n')
  521. f.write(' ' + RET_TYPE_CHECK_SEMANTICS + ' ' + DECL_CHECK_SEMANTICS + ' ' + KEYWORD_OVERRIDE + ';\n')
  522. f.write(' void ' + DECL_SET_INITIALIZED + ' ' + KEYWORD_OVERRIDE + ';\n')
  523. f.write(' ' + RET_TYPE_SET_ALL_DEFAULT_OR_UNSET + ' ' + SET_ALL_DEFAULT_OR_UNSET_DECL + ' ' + KEYWORD_OVERRIDE + ';\n\n')
  524. f.write(' ' + get_as_shared_ptr(class_name) + ' ' + get_copy_function_name(class_name) + '() const;\n')
  525. f.write(' ' + get_as_shared_ptr(class_name) + ' ' + get_deepcopy_function_name(class_name) + '(py::dict = py::dict()) const;\n')
  526. f.write(' virtual bool __eq__(const ' + class_name + '& other) const;\n')
  527. f.write(' virtual bool eq_nonarray_attributes(const ' + class_name + '& other, const bool ignore_name = false) const;\n')
  528. f.write(' bool operator == (const ' + class_name + '& other) const { return __eq__(other);}\n')
  529. f.write(' bool operator != (const ' + class_name + '& other) const { return !__eq__(other);}\n')
  530. override = KEYWORD_OVERRIDE if has_single_superclass(class_def) else ''
  531. f.write(' ' + RET_TYPE_TO_STR + ' ' + DECL_TO_STR_W_DEFAULT + ' ' + override + ';\n\n')
  532. if decls:
  533. f.write(decls + '\n\n')
  534. f.write(' // --- attributes ---\n')
  535. items = class_def[KEY_ITEMS]
  536. for attr in items:
  537. if not is_inherited(attr):
  538. written = write_attr_with_get_set(f, class_def, attr)
  539. if written :
  540. f.write('\n')
  541. f.write(' // --- methods ---\n')
  542. methods = class_def[KEY_METHODS]
  543. for m in methods:
  544. if not is_inherited(m):
  545. write_method_declaration(f, m)
  546. f.write('}; // ' + GEN_CLASS_PREFIX + class_name + '\n\n')
  547. def write_define_binding_decl(f, class_name, ret_type_void = False):
  548. if ret_type_void:
  549. f.write('void ')
  550. else:
  551. f.write('py::class_<' + class_name + '> ')
  552. f.write('define_pybinding_' + class_name + '(py::module& m)')
  553. def remove_ptr_mark(t):
  554. assert len(t) > 1
  555. if t[-1] == '*':
  556. return t[0:-1]
  557. else:
  558. return t
  559. def get_all_used_nonbase_types(class_def):
  560. types = set()
  561. for items in class_def[KEY_ITEMS]:
  562. assert KEY_TYPE in items
  563. t = items[KEY_TYPE]
  564. if not is_base_yaml_type(t):
  565. types.add( t )
  566. for method in class_def[KEY_METHODS]:
  567. if KEY_RETURN_TYPE in method:
  568. t = method[KEY_RETURN_TYPE]
  569. if not is_base_yaml_type(t):
  570. types.add( t )
  571. if KEY_PARAMS in method:
  572. for param in method[KEY_PARAMS]:
  573. assert KEY_TYPE in param
  574. t = param[KEY_TYPE]
  575. if not is_base_yaml_type(t):
  576. types.add( t )
  577. return types
  578. def get_all_used_compound_types_no_decorations(class_def):
  579. types = get_all_used_nonbase_types(class_def)
  580. cleaned_up_types = set()
  581. for t in types:
  582. # mus be set otherwise we get duplicates
  583. cleaned_up_types.add(remove_ptr_mark( get_first_inner_type( remove_ptr_mark(t) ) ))
  584. sorted_types_no_enums = [ t for t in cleaned_up_types if t not in g_enums ]
  585. sorted_types_no_enums.sort()
  586. return sorted_types_no_enums
  587. def write_required_includes(f, class_def):
  588. types = get_all_used_nonbase_types(class_def)
  589. inner_types = set()
  590. for t in types:
  591. # must be set otherwise we get duplicates
  592. inner_types.add( get_first_inner_type(t) )
  593. types_no_ptrs = [ t for t in inner_types if not is_yaml_ptr_type(t) ]
  594. sorted_types_no_enums = [ t for t in types_no_ptrs if t not in g_enums ]
  595. sorted_types_no_enums.sort()
  596. for t in sorted_types_no_enums:
  597. f.write('#include "' + get_api_class_file_name_w_dir(t, EXT_H) + '"\n')
  598. if KEY_SUPERCLASSES in class_def:
  599. scs = class_def[KEY_SUPERCLASSES]
  600. for sc in scs:
  601. f.write('#include "' + get_api_class_file_name_w_dir(sc, EXT_H) + '"\n')
  602. def write_forward_decls(f, class_def, class_name):
  603. # first we need to collect all types that we will need
  604. types = get_all_used_compound_types_no_decorations(class_def)
  605. if class_name not in types and class_def[KEY_TYPE] != VALUE_SUBMODULE:
  606. f.write('class ' + class_name + ';\n')
  607. for t in types:
  608. f.write('class ' + t + ';\n')
  609. f.write('class PythonExportContext;\n')
  610. f.write('\n')
  611. def generate_class_header(class_name, class_def, decls):
  612. with open(get_gen_class_file_name_w_dir(class_name, EXT_H), 'w') as f:
  613. f.write(COPYRIGHT)
  614. write_generated_notice(f)
  615. def_type = class_def[KEY_TYPE]
  616. guard = get_gen_header_guard_name(class_name);
  617. f.write('#ifndef ' + guard + '\n')
  618. f.write('#define ' + guard + '\n\n')
  619. f.write(INCLUDE_API_COMMON_H + '\n')
  620. if def_type == VALUE_CLASS:
  621. if KEY_SUPERCLASS in class_def:
  622. if class_def[KEY_SUPERCLASS] == BASE_DATA_CLASS:
  623. f.write(INCLUDE_API_BASE_DATA_CLASS_H + '\n')
  624. elif class_def[KEY_SUPERCLASS] == BASE_INTROSPECTION_CLASS:
  625. f.write(INCLUDE_API_BASE_INTROSPECTION_CLASS_H + '\n')
  626. elif is_container_class_no_model(class_name):
  627. f.write(INCLUDE_API_BASE_EXPORT_CLASS_H + '\n')
  628. if has_superclass_other_than_base(class_def):
  629. f.write('#include "' + get_api_class_file_name_w_dir(class_def[KEY_SUPERCLASS], EXT_H) + '"\n\n')
  630. write_required_includes(f, class_def)
  631. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  632. write_forward_decls(f, class_def, class_name)
  633. if has_single_superclass(class_def) or is_container_class_no_model(class_name):
  634. write_ctor_define(f, class_def, class_name)
  635. if def_type == VALUE_CLASS:
  636. write_gen_class(f, class_def, class_name, decls)
  637. f.write('class ' + class_name + ';\n')
  638. elif def_type == VALUE_SUBMODULE:
  639. assert decls == ''
  640. f.write('namespace ' + class_name + ' {\n\n')
  641. # submodules have only functions
  642. methods = class_def[KEY_METHODS]
  643. for m in methods:
  644. write_method_signature(f, m)
  645. f.write(";\n")
  646. f.write('\n} // namespace ' + class_name + '\n\n')
  647. write_define_binding_decl(f, class_name, def_type == VALUE_SUBMODULE)
  648. f.write(';\n')
  649. f.write(NAMESPACES_END + '\n\n')
  650. f.write('#endif // ' + guard + '\n')
  651. def generate_class_template(class_name, class_def):
  652. with open(get_api_class_file_name_w_work_dir(class_name, EXT_H), 'w') as f:
  653. f.write(COPYRIGHT)
  654. write_generated_notice(f)
  655. guard = get_api_header_guard_name(class_name);
  656. f.write('#ifndef ' + guard + '\n')
  657. f.write('#define ' + guard + '\n\n')
  658. f.write('#include "' + get_gen_class_file_name_w_dir(class_name, EXT_H) + '"\n')
  659. f.write(INCLUDE_API_COMMON_H + '\n')
  660. if has_superclass_other_than_base(class_def):
  661. f.write('#include "' + get_api_class_file_name_w_dir(class_def[KEY_SUPERCLASS], EXT_H) + '"\n\n')
  662. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  663. f.write('class ' + class_name + ': public ' + GEN_CLASS_PREFIX + class_name + ' {\n')
  664. f.write('public:\n')
  665. f.write(' ' + get_underscored(class_name).upper() + CTOR_SUFFIX + '()\n\n')
  666. if has_single_superclass(class_def):
  667. f.write(' // defined in generated file\n')
  668. f.write(' ' + RET_TYPE_EXPORT_TO_PYTHON + ' ' + DECL_EXPORT_TO_PYTHON + ' ' + KEYWORD_OVERRIDE + ';\n\n')
  669. f.write('};\n\n')
  670. f.write(NAMESPACES_END + '\n\n')
  671. f.write('#endif // ' + guard + '\n')
  672. def write_is_set_check(f, name, is_list):
  673. f.write(' if (!is_set(' + name + ')) {\n')
  674. f.write(' throw ValueError("Parameter \'' + name + '\' must be set')
  675. if is_list:
  676. f.write(' and the value must not be an empty list')
  677. f.write('.");\n')
  678. f.write(' }\n')
  679. def write_check_semantics_implemetation(f, class_name, items):
  680. f.write(RET_TYPE_CHECK_SEMANTICS + ' ' + GEN_CLASS_PREFIX + class_name + '::' + DECL_CHECK_SEMANTICS + ' {\n')
  681. for attr in items:
  682. if KEY_DEFAULT not in attr:
  683. write_is_set_check(f, attr[KEY_NAME], is_yaml_list_type(attr[KEY_TYPE]))
  684. f.write('}\n\n')
  685. def write_to_str_implementation(f, class_name, items, based_on_base_superclass):
  686. f.write(RET_TYPE_TO_STR + ' ' + GEN_CLASS_PREFIX + class_name + '::' + DECL_TO_STR + ' {\n')
  687. f.write(' std::stringstream ss;\n')
  688. if based_on_base_superclass:
  689. f.write(' ss << get_object_name()')
  690. else:
  691. f.write(' ss << "' + class_name + '"')
  692. if items:
  693. f.write(' << ": " <<\n')
  694. last_print_nl = False
  695. num_attrs = len(items)
  696. for i in range(num_attrs):
  697. name = items[i][KEY_NAME]
  698. t = items[i][KEY_TYPE]
  699. print_nl = False
  700. starting_nl = '"' if last_print_nl else '"\\n" << ind + " " << "'
  701. if is_yaml_list_type(t):
  702. underlying_type = get_first_inner_type(t)
  703. if is_yaml_ptr_type(underlying_type):
  704. f.write(' ' + starting_nl + name + '=" << ' + VEC_PTR_TO_STR + '(' + name + ', all_details, ind + " ")')
  705. print_nl = True
  706. else:
  707. f.write(' "' + name + '=" << ')
  708. f.write(VEC_NONPTR_TO_STR + '(' + name + ', all_details, ind + " ")')
  709. elif not is_base_yaml_type(t) and t not in g_enums:
  710. if is_yaml_ptr_type(t):
  711. f.write(' ' + starting_nl + name + '=" << "(" << ((' + name + ' != nullptr) ? ' + name + '->to_str(all_details, ind + " ") : "null" ) << ")"')
  712. else:
  713. f.write(' ' + starting_nl + name + '=" << "(" << ' + name + '.to_str(all_details, ind + " ") << ")"')
  714. print_nl = True
  715. else:
  716. f.write(' "' + name + '=" << ')
  717. f.write(name)
  718. if i != num_attrs - 1:
  719. f.write(' << ", " <<')
  720. if print_nl:
  721. f.write(' "\\n" << ind + " " <<\n')
  722. else:
  723. f.write('\n')
  724. last_print_nl = print_nl
  725. f.write(';\n')
  726. f.write(' return ss.str();\n')
  727. f.write('}\n\n')
  728. def write_vec_export(f, return_only_name, gen_class_name, item):
  729. item_name = item[KEY_NAME];
  730. PARENT_NAME = 'parent_name'
  731. name_w_args = \
  732. EXPORT_VEC_PREFIX + item_name + '(' + EXPORT_TO_PYTHON_ARGS + ', const std::string& ' + PARENT_NAME + ')'
  733. decl = ' virtual ' + RET_TYPE_EXPORT_TO_PYTHON + ' ' + name_w_args + ';\n'
  734. f.write(RET_TYPE_EXPORT_TO_PYTHON + " " + gen_class_name + "::" + name_w_args + " {\n")
  735. if return_only_name:
  736. f.write(' // prints vector into \'out\' and returns name of the generated list\n')
  737. else:
  738. f.write(' // does not print the array itself to \'out\' and returns the whole list\n')
  739. f.write(' std::stringstream ss;\n')
  740. out = ' ss << '
  741. if return_only_name:
  742. f.write(' std::string ' + EXPORTED_NAME + ';\n' +
  743. ' if (' + PARENT_NAME + ' != ""){\n' +
  744. ' ' + EXPORTED_NAME + ' = ' + PARENT_NAME + '+ "_' + item_name + '";\n' +
  745. ' }\n' +
  746. ' else {\n' +
  747. ' ' + EXPORTED_NAME + ' = "' + item_name + '";\n' +
  748. ' }\n\n')
  749. f.write(out + EXPORTED_NAME + ' << " = [\\n";\n')
  750. spacing = ' '
  751. else:
  752. f.write(out + '"[";\n')
  753. spacing = ' '
  754. type = item[KEY_TYPE]
  755. assert is_yaml_list_type(type)
  756. inner_type = get_inner_list_type(type)
  757. f.write(
  758. ' for (size_t i = 0; i < ' + item_name + '.size(); i++) {\n' +
  759. ' const auto& item = ' + item_name + '[i];\n' +
  760. ' if (i == 0) {\n' +
  761. ' ' + out + '"' + spacing + '";\n' +
  762. ' }\n' +
  763. ' else if (i % 16 == 0) {\n' +
  764. ' ' + out + '"\\n ";\n' +
  765. ' }\n'
  766. )
  767. if is_yaml_list_type(inner_type):
  768. inner2_type = get_inner_list_type(inner_type)
  769. # special case for 2D arrays, they hold int or float
  770. f.write(
  771. ' ' + out + '"[";\n' +
  772. ' for (const auto& value: item) {\n'
  773. )
  774. if inner2_type == YAML_TYPE_FLOAT:
  775. f.write(' ' + out + F_TO_STR + '(value) << ", ";\n')
  776. else:
  777. f.write(' ' + out + 'value << ", ";\n')
  778. f.write(
  779. ' }\n' +
  780. ' ' + out + '"], ";\n'
  781. )
  782. elif not is_base_yaml_type(inner_type) and inner_type not in g_enums:
  783. # array of API objects
  784. f.write(
  785. ' if (!item->skip_python_export()) {\n' +
  786. ' std::string name = item->export_to_python(out, ctx);\n' +
  787. ' ' + out + 'name << ", ";\n' +
  788. ' }\n'
  789. )
  790. elif inner_type == YAML_TYPE_FLOAT:
  791. f.write(' ' + out + F_TO_STR + '(item) << ", ";\n')
  792. elif inner_type == YAML_TYPE_STR:
  793. f.write(' ' + out + '"\'" << item << "\', ";\n')
  794. else:
  795. # array of simple type
  796. f.write(' ' + out + 'item << ", ";\n')
  797. f.write(' }\n')
  798. if return_only_name:
  799. f.write(out + '"\\n]\\n\\n";\n')
  800. f.write(' out << ss.str();\n')
  801. f.write(' return ' + EXPORTED_NAME + ';\n')
  802. else:
  803. f.write(out + '"]";\n')
  804. f.write(' return ss.str();\n')
  805. f.write('}\n\n')
  806. return decl
  807. def write_export_to_python_implementation(f, class_name, class_def):
  808. items = class_def[KEY_ITEMS]
  809. # declaration
  810. if KEY_SUPERCLASS in class_def and class_def[KEY_SUPERCLASS] == BASE_DATA_CLASS:
  811. override = ' ' + KEYWORD_OVERRIDE
  812. virtual = ''
  813. else:
  814. override = ''
  815. virtual = KEYWORD_VIRTUAL + ' '
  816. gen_class_name = GEN_CLASS_PREFIX + class_name
  817. decl_main = ' ' + virtual + RET_TYPE_EXPORT_TO_PYTHON + ' ' + DECL_EXPORT_TO_PYTHON + override + ';\n'
  818. f.write(RET_TYPE_EXPORT_TO_PYTHON + " " + gen_class_name + "::" + DECL_EXPORT_TO_PYTHON + " {\n")
  819. export_vecs_to_define = []
  820. if class_name == CLASS_NAME_MODEL:
  821. f.write('# if 0 // not to be used\n')
  822. name_underscored = get_underscored(class_name)
  823. if not is_container_class(class_name):
  824. f.write(
  825. ' if (!export_even_if_already_exported() && ' + CTX + '.already_exported(this)) {\n' +
  826. ' return ' + CTX + '.get_exported_name(this);\n' +
  827. ' }\n'
  828. )
  829. # name generation
  830. f.write(' std::string ' + EXPORTED_NAME + ' = ')
  831. if not has_item_w_name(items, ATTR_NAME_NAME):
  832. f.write('"' + name_underscored + '_" + ' +
  833. 'std::to_string(' + CTX + '.postinc_counter("' + name_underscored + '"));\n')
  834. else:
  835. f.write('std::string("' + name_underscored + '") + "_" + ' +
  836. '(is_set(name) ? ' +
  837. 'fix_id(name) : ' +
  838. 'std::to_string(' + CTX + '.postinc_counter("' + name_underscored + '")));\n')
  839. f.write(
  840. ' if (!export_even_if_already_exported()) {\n' +
  841. ' ' + CTX + '.add_exported(this, ' + EXPORTED_NAME + ');\n'
  842. ' }\n\n'
  843. )
  844. else:
  845. f.write(' std::string ' + EXPORTED_NAME + ' = "' + name_underscored + '";\n\n')
  846. STR_EXPORT = 'str_export'
  847. f.write(' bool ' + STR_EXPORT + ' = export_as_string_without_newlines();\n')
  848. NL = 'nl';
  849. f.write(' std::string ' + NL + ' = "";\n')
  850. IND = 'ind';
  851. f.write(' std::string ' + IND + ' = " ";\n')
  852. f.write(' std::stringstream ss;\n')
  853. out = ' ss << '
  854. f.write(' if (!' + STR_EXPORT + ') {\n')
  855. f.write(' ' + NL + ' = "\\n";\n')
  856. f.write(' ' + IND + ' = " ";\n')
  857. f.write(' ' + out + EXPORTED_NAME + ' << " = ";\n')
  858. f.write(' }\n')
  859. f.write(out + '"' + M_DOT + class_name + '(" << ' + NL + ';\n')
  860. processed_items = set()
  861. # we would like to export inherited fields first
  862. sorted_items = []
  863. for item in items:
  864. if KEY_INHERITED in item:
  865. sorted_items.append(item)
  866. for item in items:
  867. if KEY_INHERITED not in item:
  868. sorted_items.append(item)
  869. for item in sorted_items:
  870. type = item[KEY_TYPE]
  871. name = item[KEY_NAME]
  872. if name in processed_items:
  873. continue
  874. processed_items.add(name)
  875. export_condition = ''
  876. if is_yaml_list_type(type):
  877. export_condition = ' && !skip_vectors_export()'
  878. if KEY_DEFAULT in item:
  879. if is_yaml_ptr_type(type):
  880. f.write(' if (is_set(' + name + ')) {\n')
  881. else:
  882. f.write(' if (' + name + ' != ' + get_default_or_unset_value(item) + export_condition + ') {\n')
  883. f.write(' ')
  884. f.write(out + IND + ' << "' + name + ' = " << ')
  885. if is_yaml_list_type(type):
  886. f.write(EXPORT_VEC_PREFIX + name + '(out, ' + CTX + ', ' + EXPORTED_NAME + ') << "," << ' + NL + ';\n')
  887. export_vecs_to_define.append(item)
  888. elif is_yaml_ptr_type(type):
  889. f.write(name + '->export_to_python(out, ' + CTX + ') << "," << ' + NL + ';\n')
  890. elif not is_base_yaml_type(type) and type not in g_enums:
  891. f.write(name + '.export_to_python(out, ' + CTX + ') << "," << ' + NL + ';\n')
  892. elif type == YAML_TYPE_STR:
  893. f.write('"\'" << ' + name + ' << "\'" << "," << ' + NL + ';\n')
  894. elif type == YAML_TYPE_VEC3:
  895. # also other ivec*/vec2 types should be handled like this but it was not needed so far
  896. f.write('"' + M_DOT + CPP_TYPE_VEC3 + '(" << ' +
  897. F_TO_STR + '(' + name + '.x) << ", " << ' +
  898. F_TO_STR + '(' + name + '.y) << ", " << ' +
  899. F_TO_STR + '(' + name + '.z)' +
  900. '<< ")," << ' + NL + ';\n')
  901. elif type == YAML_TYPE_VEC2:
  902. # also other ivec*/vec2 types should be handled like this but it was not needed so far
  903. f.write('"' + M_DOT + CPP_TYPE_VEC2 + '(" << ' +
  904. F_TO_STR + '(' + name + '.u) << ", " << ' +
  905. F_TO_STR + '(' + name + '.v)' +
  906. '<< ")," << ' + NL + ';\n')
  907. elif type == YAML_TYPE_FLOAT:
  908. f.write(F_TO_STR + '(' + name + ') << "," << ' + NL + ';\n')
  909. else:
  910. # using operators << for enums
  911. f.write(name + ' << "," << ' + NL + ';\n')
  912. if KEY_DEFAULT in item:
  913. f.write(' }\n')
  914. f.write(out + '")" << ' + NL + ' << ' + NL + ';\n')
  915. f.write(' if (!' + STR_EXPORT + ') {\n')
  916. f.write(' out << ss.str();\n')
  917. f.write(' return ' + EXPORTED_NAME + ';\n')
  918. f.write(' }\n')
  919. f.write(' else {\n')
  920. f.write(' return ss.str();\n')
  921. f.write(' }\n')
  922. if class_name == CLASS_NAME_MODEL:
  923. f.write('#else // # if 0\n')
  924. f.write(' assert(false);\n')
  925. f.write(' return "";\n')
  926. f.write('#endif\n')
  927. f.write('}\n\n')
  928. # also define export vec methods
  929. decls_export_vec = ''
  930. for item in export_vecs_to_define:
  931. decls_export_vec += write_vec_export(f, is_container_class(class_name), gen_class_name, item)
  932. # return string that contains declaration of export methods, this will be later used in
  933. # .h file generation
  934. return decl_main + decls_export_vec
  935. def write_operator_equal_body(f, class_name, class_def, skip_arrays_and_name=False):
  936. items = class_def[KEY_ITEMS]
  937. f.write(' return\n')
  938. if not items:
  939. f.write('true ;\n')
  940. num_attrs = len(items)
  941. for i in range(num_attrs):
  942. name = items[i][KEY_NAME]
  943. t = items[i][KEY_TYPE]
  944. if is_yaml_ptr_type(t):
  945. f.write(
  946. ' (\n'
  947. ' (is_set(' + name + ')) ?\n'
  948. ' (is_set(other.' + name + ') ?\n'
  949. ' (' + name + '->__eq__(*other.' + name + ')) : \n'
  950. ' false\n'
  951. ' ) :\n'
  952. ' (is_set(other.' + name + ') ?\n'
  953. ' false :\n'
  954. ' true\n'
  955. ' )\n'
  956. ' ) '
  957. )
  958. elif is_yaml_list_type(t):
  959. if not skip_arrays_and_name:
  960. if is_yaml_ptr_type(get_inner_list_type(t)):
  961. f.write(' vec_ptr_eq(' + name + ', other.' + name + ')')
  962. else:
  963. f.write(' ' + name + ' == other.' + name)
  964. else:
  965. f.write(' true /*' + name + '*/')
  966. else:
  967. if skip_arrays_and_name and name == KEY_NAME:
  968. f.write(' (ignore_name || ' + name + ' == other.' + name + ')')
  969. else:
  970. f.write(' ' + name + ' == other.' + name)
  971. if i != num_attrs - 1:
  972. f.write(' &&')
  973. else:
  974. f.write(';')
  975. f.write('\n')
  976. def write_copy_implementation(f, class_name, class_def, deepcopy):
  977. if deepcopy:
  978. func_name = get_deepcopy_function_name(class_name)
  979. func_args = 'py::dict'
  980. else:
  981. func_name = get_copy_function_name(class_name)
  982. func_args = ''
  983. f.write(get_as_shared_ptr(class_name) + ' ' + GEN_CLASS_PREFIX + class_name + '::' + func_name + '(' + func_args + ') const {\n')
  984. # for some reason res(DefaultCtorArgType()) is not accepted by gcc...
  985. f.write(' ' + SHARED_PTR + '<' + class_name + '> res = ' + MAKE_SHARED + '<' + class_name + '>(' + DEFAULT_CTOR_ARG_TYPE +'());\n')
  986. items = class_def[KEY_ITEMS]
  987. if has_single_superclass(class_def):
  988. f.write(' res->' + CLASS_NAME_ATTR + ' = ' + CLASS_NAME_ATTR + ';\n')
  989. # TODO - deepcopy - for
  990. for item in items:
  991. name = item[KEY_NAME]
  992. if not deepcopy:
  993. # simply use assign operator
  994. f.write(' res->' + name + ' = ' + name + ';\n')
  995. else:
  996. # pointers must be deepcopied
  997. # also vectors containing pointers, cannot use aux functions for copying vectors
  998. # because name of the deepcpy function is different for each class
  999. t = item[KEY_TYPE]
  1000. # check for 2D lists
  1001. innermost_list_ptr_type = None
  1002. if is_yaml_list_type(t):
  1003. inner = get_inner_list_type(t)
  1004. if is_yaml_ptr_type(inner):
  1005. innermost_list_ptr_type = inner
  1006. elif is_yaml_list_type(inner):
  1007. inner2 = get_inner_list_type(inner)
  1008. if is_yaml_ptr_type(inner2):
  1009. assert False, "2D lists containing pointers are not supported for deepcopy yet"
  1010. if innermost_list_ptr_type:
  1011. base_t = remove_ptr_mark(innermost_list_ptr_type)
  1012. f.write(' for (const auto& item: ' + name + ') {\n')
  1013. f.write(' res->' + name + '.push_back((' + IS_SET + '(item)) ? ' +
  1014. 'item->' + get_deepcopy_function_name(base_t) + '() : '
  1015. 'nullptr);\n')
  1016. f.write(' }\n')
  1017. elif is_yaml_dict_type(t):
  1018. assert False, "Dict type is not supported for deepcopy yet"
  1019. elif is_yaml_ptr_type(t):
  1020. base_t = remove_ptr_mark(t)
  1021. f.write(' res->' + name + ' = ' + IS_SET + '(' + name + ') ? ' +
  1022. name + '->' + get_deepcopy_function_name(base_t) + '() : '
  1023. 'nullptr;\n')
  1024. else:
  1025. f.write(' res->' + name + ' = ' + name + ';\n')
  1026. f.write('\n')
  1027. f.write(' return res;\n')
  1028. f.write('}\n\n')
  1029. def write_operator_equal_implementation(f, class_name, class_def):
  1030. # originally was operator== used, but this causes too cryptic compilation errors,
  1031. # so it was better to use python-style naming,
  1032. # also the __eq__ is only defined for the 'Gen' classes, so defining operator ==
  1033. # might have been more confusing
  1034. gen_class_name = GEN_CLASS_PREFIX + class_name
  1035. f.write('bool ' + gen_class_name + '::__eq__(const ' + class_name + '& other) const {\n')
  1036. write_operator_equal_body(f, class_name, class_def)
  1037. f.write('}\n\n')
  1038. f.write('bool ' + gen_class_name + '::eq_nonarray_attributes(const ' + class_name + '& other, const bool ignore_name) const {\n')
  1039. write_operator_equal_body(f, class_name, class_def, skip_arrays_and_name=True)
  1040. f.write('}\n\n')
  1041. def write_set_initialized_implemetation(f, class_name, items):
  1042. # originally was operator== used, but this causes too cryptic compilation errors,
  1043. # so it was better to use python-style naming,
  1044. # also the __eq__ is aonly defined for the 'Gen' classes, so defining operator ==
  1045. # might have been more confusing
  1046. gen_class_name = GEN_CLASS_PREFIX + class_name
  1047. f.write('void ' + gen_class_name + '::' + DECL_SET_INITIALIZED + ' {\n')
  1048. num_attrs = len(items)
  1049. for i in range(num_attrs):
  1050. name = items[i][KEY_NAME]
  1051. t = items[i][KEY_TYPE]
  1052. if is_yaml_ptr_type(t):
  1053. f.write(' if (is_set(' + name + ')) {\n')
  1054. f.write(' ' + name + '->set_initialized();\n')
  1055. f.write(' }\n')
  1056. elif is_yaml_list_type(t) and is_yaml_ptr_type(get_inner_list_type(t)):
  1057. f.write(' vec_set_initialized(' + name + ');\n')
  1058. f.write(' initialized = true;\n')
  1059. f.write('}\n\n')
  1060. def is_overloaded(method, class_def):
  1061. name = method[KEY_NAME]
  1062. count = 0
  1063. for m in class_def[KEY_METHODS]:
  1064. if m[KEY_NAME] == name:
  1065. count += 1
  1066. assert count >= 1
  1067. return count >= 2
  1068. def get_method_overload_cast(method):
  1069. res = 'py::overload_cast<'
  1070. if KEY_PARAMS in method:
  1071. params = method[KEY_PARAMS]
  1072. params_cnt = len(params)
  1073. for i in range(params_cnt):
  1074. p = params[i]
  1075. assert KEY_TYPE in p
  1076. t = get_type_as_ref_param(p)
  1077. res += 'const ' + t
  1078. if i + 1 != params_cnt:
  1079. res += ', '
  1080. res += '>'
  1081. return res
  1082. def create_doc_str(yaml_doc, w_quotes=True):
  1083. if not yaml_doc:
  1084. return ""
  1085. res = yaml_doc.replace('"', '\\"')
  1086. res = res.replace('\n', '\\n')
  1087. res = res.replace('\\:', ':')
  1088. if w_quotes:
  1089. return '"' + res + '"'
  1090. else:
  1091. return res
  1092. def write_pybind11_method_bindings(f, class_name, method, class_def):
  1093. assert KEY_NAME in method
  1094. name = rename_cpp_reserved_id(method[KEY_NAME])
  1095. full_method_name = '&' + class_name + '::' + name
  1096. if is_overloaded(method, class_def):
  1097. # overloaded method must be extra decorated with argument types for pybind11
  1098. full_method_name = get_method_overload_cast(method) + '(' + full_method_name + ')'
  1099. f.write(' .def("' + name + '", ' + full_method_name)
  1100. params_doc = ''
  1101. if KEY_PARAMS in method:
  1102. params = method[KEY_PARAMS]
  1103. params_cnt = len(params)
  1104. for i in range(params_cnt):
  1105. f.write(', ')
  1106. p = params[i]
  1107. assert KEY_NAME in p
  1108. param_name = p[KEY_NAME]
  1109. f.write('py::arg("' + param_name + '")')
  1110. if KEY_DEFAULT in p:
  1111. f.write(' = ' + get_default_or_unset_value(p))
  1112. params_doc += '- ' + param_name
  1113. if KEY_DOC in p:
  1114. params_doc += ': ' + create_doc_str(p[KEY_DOC], False) + '\\n'
  1115. params_doc += '\\n'
  1116. if KEY_DOC in method or params_doc:
  1117. f.write(', "')
  1118. if KEY_DOC in method:
  1119. f.write(create_doc_str(method[KEY_DOC], False))
  1120. if params_doc:
  1121. params_doc = '\\n' + params_doc
  1122. f.write(params_doc + '"')
  1123. f.write(')\n')
  1124. def write_pybind11_bindings(f, class_name, class_def):
  1125. def_type = class_def[KEY_TYPE]
  1126. items = class_def[KEY_ITEMS]
  1127. write_define_binding_decl(f, class_name, def_type != VALUE_CLASS)
  1128. f.write(' {\n')
  1129. superclass = ''
  1130. if has_superclass_other_than_base(class_def):
  1131. superclass = class_def[KEY_SUPERCLASS] + ', '
  1132. ctor_has_args = True
  1133. if KEY_SUPERCLASS in class_def and class_def[KEY_SUPERCLASS] == BASE_INTROSPECTION_CLASS:
  1134. ctor_has_args = False
  1135. if def_type == VALUE_CLASS:
  1136. f.write(' return py::class_<' + class_name + ', ' + superclass + \
  1137. SHARED_PTR + '<' + class_name + '>>(m, "' + class_name + '"')
  1138. if KEY_DOC in class_def:
  1139. f.write(', ' + create_doc_str(class_def[KEY_DOC]))
  1140. f.write(')\n')
  1141. f.write(' .def(\n')
  1142. f.write(' py::init<\n')
  1143. num_items = len(items)
  1144. if ctor_has_args:
  1145. if has_single_superclass(class_def) or is_container_class_no_model(class_name):
  1146. # init operands
  1147. for i in range(num_items):
  1148. attr = items[i]
  1149. if attr_not_in_ctor(attr):
  1150. continue
  1151. const_spec = 'const ' if not is_yaml_ptr_type(attr[KEY_TYPE]) else ''
  1152. f.write(' ' + const_spec + get_type_as_ref_param(attr))
  1153. if i != num_items - 1:
  1154. f.write(',\n')
  1155. if num_items != 0:
  1156. f.write('\n')
  1157. f.write(' >()')
  1158. if ctor_has_args:
  1159. # init argument names and default values
  1160. if has_single_superclass(class_def) or is_container_class_no_model(class_name):
  1161. if num_items != 0:
  1162. f.write(',')
  1163. f.write('\n')
  1164. for i in range(num_items):
  1165. attr = items[i]
  1166. if attr_not_in_ctor(attr):
  1167. continue
  1168. name = attr[KEY_NAME]
  1169. f.write(' py::arg("' + name + '")')
  1170. if KEY_DEFAULT in attr:
  1171. f.write(' = ' + get_default_or_unset_value(attr))
  1172. if i != num_items - 1:
  1173. f.write(',\n')
  1174. f.write('\n')
  1175. f.write(' )\n')
  1176. # common methods
  1177. if has_single_superclass(class_def):
  1178. f.write(' .def("check_semantics", &' + class_name + '::check_semantics)\n')
  1179. f.write(' .def("__copy__", &' + class_name + '::' + get_copy_function_name(class_name) + ')\n')
  1180. f.write(' .def("__deepcopy__", &' + class_name + '::' + get_deepcopy_function_name(class_name) + ', py::arg("memo"))\n')
  1181. f.write(' .def("__str__", &' + class_name + '::to_str, py::arg("all_details") = false, py::arg("ind") = std::string(""))\n')
  1182. # keeping the default __repr__ implementation for better error messages
  1183. #f.write(' .def("__repr__", &' + class_name + '::to_str, py::arg("ind") = std::string(""))\n')
  1184. f.write(' .def("__eq__", &' + class_name + '::__eq__, py::arg("other"))\n')
  1185. else:
  1186. f.write(' m.def_submodule("' + class_name + '")\n')
  1187. # declared methods
  1188. for m in class_def[KEY_METHODS]:
  1189. if not has_single_superclass(class_def) or not is_inherited(m):
  1190. write_pybind11_method_bindings(f, class_name, m, class_def)
  1191. if def_type == VALUE_CLASS:
  1192. # dump needs to be always implemented
  1193. f.write(' .def("dump", &' + class_name + '::dump)\n')
  1194. # properties
  1195. for i in range(num_items):
  1196. if not has_single_superclass(class_def) or not is_inherited(items[i]):
  1197. name = items[i][KEY_NAME]
  1198. f.write(' .def_property("' + name + '", &' + class_name + '::get_' + name + ', &' + class_name + '::set_' + name)
  1199. if is_yaml_list_type(items[i][KEY_TYPE]):
  1200. f.write(', py::return_value_policy::reference')
  1201. if KEY_DOC in items[i]:
  1202. f.write(', ' + create_doc_str(items[i][KEY_DOC]))
  1203. f.write(')\n')
  1204. f.write(' ;\n')
  1205. f.write('}\n\n')
  1206. def write_used_classes_includes(f, class_def):
  1207. types = get_all_used_compound_types_no_decorations(class_def)
  1208. for t in types:
  1209. f.write('#include "' + get_api_class_file_name_w_dir(t, EXT_H) + '"\n')
  1210. def write_set_all_default_or_unset(f, class_name, class_def):
  1211. f.write(
  1212. RET_TYPE_SET_ALL_DEFAULT_OR_UNSET + ' ' + GEN_CLASS_PREFIX + class_name + '::' +
  1213. SET_ALL_DEFAULT_OR_UNSET_DECL + ' {' + '\n'
  1214. )
  1215. # we do not need to call the derived methods because all members were inherited
  1216. f.write(' ' + CLASS_NAME_ATTR + ' = "' + class_name + '";\n')
  1217. items = class_def[KEY_ITEMS]
  1218. num_items = len(items)
  1219. for i in range(num_items):
  1220. assert KEY_NAME in items[i]
  1221. attr_name = items[i][KEY_NAME]
  1222. f.write(' ' + attr_name + ' = ' + get_default_or_unset_value(items[i]) + ';\n')
  1223. f.write('}\n\n')
  1224. def generate_class_implementation_and_bindings(class_name, class_def):
  1225. # returns a list of methods to be declared
  1226. decls = ''
  1227. with open(get_gen_class_file_name_w_dir(class_name, EXT_CPP), 'w') as f:
  1228. def_type = class_def[KEY_TYPE]
  1229. f.write(COPYRIGHT)
  1230. write_generated_notice(f)
  1231. f.write('#include <sstream>\n')
  1232. f.write('#include "api/pybind11_stl_include.h"\n')
  1233. f.write(INCLUDE_API_PYTHON_EXPORT_UTILS_H + '\n')
  1234. # includes for our class
  1235. f.write('#include "' + get_gen_class_file_name(class_name, EXT_H) + '"\n')
  1236. if def_type == VALUE_CLASS:
  1237. f.write('#include "' + get_api_class_file_name_w_dir(class_name, EXT_H) + '"\n')
  1238. # we also need includes for every type that we used
  1239. write_used_classes_includes(f, class_def)
  1240. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  1241. if def_type == VALUE_CLASS:
  1242. items = class_def[KEY_ITEMS]
  1243. if has_single_superclass(class_def):
  1244. write_check_semantics_implemetation(f, class_name, items)
  1245. write_set_initialized_implemetation(f, class_name, items)
  1246. write_set_all_default_or_unset(f, class_name, class_def)
  1247. write_copy_implementation(f, class_name, class_def, False)
  1248. write_copy_implementation(f, class_name, class_def, True)
  1249. write_operator_equal_implementation(f, class_name, class_def)
  1250. write_to_str_implementation(f, class_name, items, has_single_superclass(class_def))
  1251. write_pybind11_bindings(f, class_name, class_def)
  1252. # we do not need export for introspected classes
  1253. if is_container_class(class_name) or \
  1254. (KEY_SUPERCLASS in class_def and class_def[KEY_SUPERCLASS] != BASE_INTROSPECTION_CLASS):
  1255. decls += write_export_to_python_implementation(f, class_name, class_def);
  1256. f.write(NAMESPACES_END + '\n\n')
  1257. return decls
  1258. # class Model provides the same methods as Subsystem and Instantiation,
  1259. # this function copies the definition for pybind11 API generation
  1260. def inherit_from_superclasses(data_classes, class_name, class_def):
  1261. res = class_def.copy()
  1262. superclass_names = []
  1263. if has_superclass_other_than_base(class_def):
  1264. superclass_names.append(class_def[KEY_SUPERCLASS])
  1265. if KEY_SUPERCLASSES in class_def:
  1266. superclass_names += class_def[KEY_SUPERCLASSES]
  1267. for sc_name in superclass_names:
  1268. assert sc_name in data_classes
  1269. superclass_def = data_classes[sc_name]
  1270. assert not has_superclass_other_than_base(superclass_def), "Only one level of inheritance"
  1271. # we are not checking any duplicates
  1272. if KEY_METHODS in superclass_def:
  1273. for method in superclass_def[KEY_METHODS]:
  1274. method_copy = copy(method)
  1275. method_copy[KEY_INHERITED] = True
  1276. res[KEY_METHODS].append(method_copy)
  1277. class_attr_names = set()
  1278. if KEY_ITEMS in class_def:
  1279. for attr in class_def[KEY_ITEMS]:
  1280. class_attr_names.add(attr[KEY_NAME])
  1281. if KEY_ITEMS in superclass_def:
  1282. for attr in superclass_def[KEY_ITEMS]:
  1283. attr_copy = copy(attr)
  1284. attr_copy[KEY_INHERITED] = True
  1285. res[KEY_ITEMS].append(attr_copy)
  1286. # do not inherit attribute if the attribute with the same name already exists
  1287. if attr[KEY_NAME] in class_attr_names:
  1288. attr_copy[KEY_NOT_AS_CTOR_ARG] = True
  1289. return res
  1290. def generate_class_files(data_classes, class_name, class_def):
  1291. # we need items and methods to be present
  1292. if KEY_ITEMS not in class_def:
  1293. class_def[KEY_ITEMS] = []
  1294. if KEY_METHODS not in class_def:
  1295. class_def[KEY_METHODS] = []
  1296. class_def_w_inheritances = inherit_from_superclasses(data_classes, class_name, class_def)
  1297. decls = generate_class_implementation_and_bindings(class_name, class_def_w_inheritances)
  1298. generate_class_header(class_name, class_def_w_inheritances, decls)
  1299. generate_class_template(class_name, class_def_w_inheritances)
  1300. def write_constant_def(f, constant_def):
  1301. assert KEY_NAME in constant_def
  1302. assert KEY_TYPE in constant_def
  1303. assert KEY_VALUE in constant_def
  1304. name = constant_def[KEY_NAME]
  1305. t = constant_def[KEY_TYPE]
  1306. value = constant_def[KEY_VALUE]
  1307. # complex types are defined manually in globals.h
  1308. if is_base_yaml_type(t):
  1309. q = '"' if t == YAML_TYPE_STR else ''
  1310. f.write('const ' + yaml_type_to_cpp_type(t) + ' ' + name + ' = ' + q + str(value) + q + ';\n')
  1311. def write_enum_def(f, enum_def):
  1312. assert KEY_NAME in enum_def
  1313. assert KEY_VALUES in enum_def, "Enum must have at least one value"
  1314. name = enum_def[KEY_NAME]
  1315. values = enum_def[KEY_VALUES]
  1316. f.write('\nenum class ' + name + ' {\n')
  1317. num = len(values)
  1318. for i in range(num):
  1319. v = values[i]
  1320. assert KEY_NAME in v
  1321. assert KEY_VALUE in v
  1322. assert type(v[KEY_VALUE]) == int
  1323. f.write(' ' + v[KEY_NAME] + ' = ' + str(v[KEY_VALUE]))
  1324. if i + 1 != num:
  1325. f.write(',')
  1326. f.write('\n')
  1327. f.write('};\n\n')
  1328. f.write('\nstatic inline std::ostream& operator << (std::ostream& out, const ' + name + ' v) {\n')
  1329. f.write(' switch (v) {\n');
  1330. for i in range(num):
  1331. v = values[i]
  1332. # dumping in Python style '.' that will be most probably more common as API
  1333. #f.write(' case ' + name + '::' + v[KEY_NAME] + ': out << "' + name + '.' + v[KEY_NAME] + ' (' + str(v[KEY_VALUE]) + ')"; break;\n')
  1334. # dumping as Python code
  1335. f.write(' case ' + name + '::' + v[KEY_NAME] + ': out << "' + M_DOT + name + '.' + v[KEY_NAME] + '"; break;\n')
  1336. f.write(' }\n')
  1337. f.write(' return out;\n')
  1338. f.write('};\n\n')
  1339. def generate_constants_header(constants_items, enums_items):
  1340. with open(os.path.join(TARGET_DIRECTORY, GEN_CONSTANTS_H), 'w') as f:
  1341. f.write(COPYRIGHT)
  1342. write_generated_notice(f)
  1343. guard = 'API_GEN_CONSTANTS';
  1344. f.write('#ifndef ' + guard + '\n')
  1345. f.write('#define ' + guard + '\n\n')
  1346. f.write('#include <string>\n')
  1347. f.write('#include <climits>\n')
  1348. f.write('#include <cfloat>\n')
  1349. f.write('#include "api/globals.h"\n')
  1350. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  1351. for constant_def in constants_items:
  1352. write_constant_def(f, constant_def)
  1353. for enum_def in enums_items:
  1354. write_enum_def(f, enum_def)
  1355. f.write('\n' + DECL_DEFINE_PYBINDIND_CONSTANTS + ';\n')
  1356. f.write(DECL_DEFINE_PYBINDIND_ENUMS + ';\n\n')
  1357. f.write(NAMESPACES_END + '\n\n')
  1358. f.write('#endif // ' + guard + '\n\n')
  1359. def write_constant_binding(f, constant_def):
  1360. assert KEY_NAME in constant_def
  1361. assert KEY_TYPE in constant_def
  1362. assert KEY_VALUE in constant_def
  1363. name = constant_def[KEY_NAME]
  1364. t = constant_def[KEY_TYPE]
  1365. value = constant_def[KEY_VALUE]
  1366. #q = '"' if t == YAML_TYPE_STR else ''
  1367. pybind_type = yaml_type_to_pybind_type(t)
  1368. if pybind_type == PYBIND_TYPE_OBJECT:
  1369. f.write(' m.attr("' + name + '") = py::' + PYBIND_TYPE_OBJECT + '(' + PY_CAST + '(' + name + '));\n')
  1370. else:
  1371. f.write(' m.attr("' + name + '") = py::' + pybind_type + '(' + name + ');\n')
  1372. def write_enum_binding(f, enum_def):
  1373. assert KEY_NAME in enum_def
  1374. assert KEY_VALUES in enum_def, "Enum must have at least one value"
  1375. name = enum_def[KEY_NAME]
  1376. values = enum_def[KEY_VALUES]
  1377. num = len(values)
  1378. f.write(' py::enum_<' + name + '>(m, "' + name + '", py::arithmetic()')
  1379. # Pybind11 does nto allow help for enum members so we put all that information into
  1380. # the header
  1381. members_doc = ''
  1382. for i in range(num):
  1383. v = values[i]
  1384. assert KEY_NAME in v
  1385. members_doc += '- ' + v[KEY_NAME]
  1386. if KEY_DOC in v:
  1387. members_doc += ': ' + create_doc_str(v[KEY_DOC], False)
  1388. else:
  1389. members_doc += '\\n'
  1390. members_doc += '\\n'
  1391. if KEY_DOC in enum_def or members_doc:
  1392. f.write(', "')
  1393. if KEY_DOC in enum_def:
  1394. f.write(create_doc_str(enum_def[KEY_DOC], False) + '\\n')
  1395. f.write(members_doc + '"')
  1396. f.write(')\n')
  1397. for i in range(num):
  1398. v = values[i]
  1399. assert KEY_NAME in v
  1400. f.write(' .value("' + v[KEY_NAME] + '", ' + name + '::' + v[KEY_NAME] + ')\n')
  1401. f.write(' .export_values();\n')
  1402. def generate_constants_implementation(constants_items, enums_items):
  1403. with open(os.path.join(TARGET_DIRECTORY, GEN_CONSTANTS_CPP), 'w') as f:
  1404. f.write(COPYRIGHT)
  1405. write_generated_notice(f)
  1406. f.write(INCLUDE_API_COMMON_H + '\n')
  1407. # includes for used classes
  1408. used_classes = set()
  1409. for constant_def in constants_items:
  1410. t = constant_def[KEY_TYPE]
  1411. pybind_type = yaml_type_to_pybind_type(t)
  1412. if pybind_type == PYBIND_TYPE_OBJECT:
  1413. used_classes.add(t)
  1414. for cls in used_classes:
  1415. f.write('#include "' + get_api_class_file_name_w_dir(cls, EXT_H) + '"\n')
  1416. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  1417. f.write(DECL_DEFINE_PYBINDIND_CONSTANTS + ' {\n')
  1418. for constant_def in constants_items:
  1419. write_constant_binding(f, constant_def)
  1420. f.write('}\n\n')
  1421. f.write(DECL_DEFINE_PYBINDIND_ENUMS + ' {\n')
  1422. for enum_def in enums_items:
  1423. write_enum_binding(f, enum_def)
  1424. f.write('}\n\n')
  1425. f.write(NAMESPACES_END + '\n\n')
  1426. def generate_constants_and_enums(constants_items, enums_items):
  1427. generate_constants_header(constants_items, enums_items)
  1428. generate_constants_implementation(constants_items, enums_items)
  1429. def set_global_enums(data_classes):
  1430. global g_enums
  1431. res = set()
  1432. enum_defs = data_classes[KEY_ENUMS] if KEY_ENUMS in data_classes else []
  1433. for enum in enum_defs:
  1434. assert KEY_NAME in enum
  1435. res.add(enum[KEY_NAME])
  1436. g_enums = res
  1437. def capitalize_first(s):
  1438. if s[0].islower():
  1439. return s[0].upper() + s[1:]
  1440. else:
  1441. return s
  1442. def generate_vector_bindings(data_classes):
  1443. # collect all attributes that use List
  1444. list_types = set()
  1445. used_types = set()
  1446. for key, value in data_classes.items():
  1447. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1448. # items
  1449. if KEY_ITEMS in value:
  1450. for item in value[KEY_ITEMS]:
  1451. if is_yaml_list_type(item[KEY_TYPE]):
  1452. t = item[KEY_TYPE]
  1453. list_types.add(t)
  1454. inner = get_inner_list_type(t)
  1455. if is_yaml_list_type(inner):
  1456. inner = get_inner_list_type(inner)
  1457. used_types.add(inner)
  1458. sorted_list_types = sorted(list_types)
  1459. sorted_used_types = sorted(used_types)
  1460. # generate gen_make_opaque.h
  1461. with open(os.path.join(TARGET_DIRECTORY, GEN_VECTORS_MAKE_OPAQUE_H), 'w') as f:
  1462. f.write(COPYRIGHT)
  1463. guard = 'GEN_VECTORS_MAKE_OPAQUE_H'
  1464. f.write('#ifndef ' + guard + '\n')
  1465. f.write('#define ' + guard + '\n\n')
  1466. f.write('#include <vector>\n')
  1467. f.write('#include <memory>\n')
  1468. f.write('#include "pybind11/include/pybind11/pybind11.h"\n')
  1469. f.write('#include "defines.h"\n\n')
  1470. f.write(NAMESPACES_BEGIN + '\n\n')
  1471. for t in sorted_used_types:
  1472. if is_base_yaml_type(t):
  1473. continue
  1474. assert is_yaml_ptr_type(t)
  1475. f.write('class ' + t[:-1] + ';\n')
  1476. f.write('\n' + NAMESPACES_END + '\n\n')
  1477. for t in sorted_list_types:
  1478. f.write(PYBIND11_MAKE_OPAQUE + '(' + yaml_type_to_cpp_type(t, True) + ');\n')
  1479. f.write('\n#endif // ' + guard + '\n')
  1480. # generate gen_bind_vector.cpp
  1481. with open(os.path.join(TARGET_DIRECTORY, GEN_VECTORS_BIND_CPP), 'w') as f:
  1482. f.write(COPYRIGHT + '\n')
  1483. f.write('#include "api/pybind11_stl_include.h"\n')
  1484. f.write('#include "pybind11/include/pybind11/stl_bind.h"\n')
  1485. f.write('#include "pybind11/include/pybind11/numpy.h"\n\n')
  1486. f.write('namespace py = pybind11;\n\n')
  1487. for t in sorted_used_types:
  1488. if is_base_yaml_type(t):
  1489. continue
  1490. assert is_yaml_ptr_type(t)
  1491. f.write('#include "' + get_api_class_file_name_w_dir(t[:-1], EXT_H) + '"\n')
  1492. f.write('\n')
  1493. f.write(NAMESPACES_BEGIN + '\n\n')
  1494. f.write('void ' + GEN_VECTORS_BIND + '{\n')
  1495. ind = ' '
  1496. for t in sorted_list_types:
  1497. cpp_t = yaml_type_to_cpp_type(t, True)
  1498. name = 'Vector'
  1499. inner = get_inner_list_type(t)
  1500. if is_yaml_list_type(inner):
  1501. inner2 = get_inner_list_type(inner)
  1502. name += name + capitalize_first(inner2)
  1503. else:
  1504. name += capitalize_first(inner)
  1505. if name[-1] == '*':
  1506. name = name[:-1]
  1507. f.write(ind + PY_BIND_VECTOR + '<' + cpp_t + '>(m,"' + name + '");\n')
  1508. f.write(ind + PY_IMPLICITLY_CONVERTIBLE + '<py::list, ' + cpp_t + '>();\n')
  1509. f.write(ind + PY_IMPLICITLY_CONVERTIBLE + '<py::tuple, ' + cpp_t + '>();\n')
  1510. # special case for numpy conversions
  1511. if name == 'VectorFloat':
  1512. f.write(ind + PY_IMPLICITLY_CONVERTIBLE + '<py::array_t<double>, ' + cpp_t + '>();\n')
  1513. elif name == 'VectorInt':
  1514. f.write(ind + PY_IMPLICITLY_CONVERTIBLE + '<py::array_t<int>, ' + cpp_t + '>();\n')
  1515. f.write('\n')
  1516. f.write('}\n')
  1517. f.write('\n' + NAMESPACES_END + '\n\n')
  1518. def generate_data_classes(data_classes):
  1519. generate_constants_and_enums(
  1520. data_classes[KEY_CONSTANTS] if KEY_CONSTANTS in data_classes else [],
  1521. data_classes[KEY_ENUMS] if KEY_ENUMS in data_classes else [])
  1522. set_global_enums(data_classes)
  1523. # std::vector/list needs special handling because we need it to be opaque
  1524. # so that the user can modify the internal value
  1525. generate_vector_bindings(data_classes)
  1526. for key, value in data_classes.items():
  1527. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1528. # set default definition type
  1529. if not KEY_TYPE in value:
  1530. value[KEY_TYPE] = VALUE_CLASS
  1531. if VERBOSE:
  1532. if value[KEY_TYPE] == VALUE_CLASS:
  1533. print("Generating class " + key)
  1534. elif value[KEY_TYPE] == VALUE_SUBMODULE:
  1535. print("Generating submodule " + key)
  1536. else:
  1537. assert False, "Unknown definition type"
  1538. generate_class_files(data_classes, key, value)
  1539. def collect_all_names(data_classes):
  1540. all_class_names = set()
  1541. all_item_param_names = set()
  1542. all_enum_value_names = set()
  1543. all_const_value_names = set()
  1544. for key, value in data_classes.items():
  1545. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1546. all_class_names.add(key)
  1547. # items
  1548. if KEY_ITEMS in value:
  1549. for item in value[KEY_ITEMS]:
  1550. all_item_param_names.add(item[KEY_NAME])
  1551. # methods
  1552. if KEY_METHODS in value:
  1553. for method in value[KEY_METHODS]:
  1554. all_item_param_names.add(method[KEY_NAME])
  1555. if KEY_PARAMS in method:
  1556. for param in method[KEY_PARAMS]:
  1557. all_item_param_names.add(param[KEY_NAME])
  1558. elif key == KEY_ENUMS:
  1559. # enum names are in g_enums
  1560. for enum in value:
  1561. if KEY_VALUES in enum:
  1562. for enum_item in enum[KEY_VALUES]:
  1563. all_enum_value_names.add(enum_item[KEY_NAME])
  1564. elif key == KEY_CONSTANTS:
  1565. for const in value:
  1566. all_const_value_names.add(const[KEY_NAME])
  1567. else:
  1568. print("Error: unexpected top level key " + key)
  1569. sys.exit(1)
  1570. all_class_names = list(all_class_names)
  1571. all_class_names.sort(key=str.casefold)
  1572. all_item_param_names_list = list(all_item_param_names)
  1573. all_item_param_names_list.sort(key=str.casefold)
  1574. all_enum_value_names_list = list(all_enum_value_names)
  1575. all_enum_value_names_list.sort(key=str.casefold)
  1576. all_const_value_names_list = list(all_const_value_names)
  1577. all_const_value_names_list.sort(key=str.casefold)
  1578. return all_class_names, all_item_param_names_list, all_enum_value_names_list, all_const_value_names_list
  1579. def write_name_def(f, name, extra_prefix=''):
  1580. upper_name = get_underscored(name).upper()
  1581. f.write('const char* const ' + NAME_PREFIX + extra_prefix + upper_name + ' = "' + name + '";\n')
  1582. def write_name_def_verbatim(f, name, extra_prefix=''):
  1583. f.write('const char* const ' + NAME_PREFIX + extra_prefix + name + ' = "' + name + '";\n')
  1584. # this function generates definitions for converter so that we can use constant strings
  1585. def generate_names_header(data_classes):
  1586. all_class_names, all_item_param_names_list, all_enum_value_names_list, all_const_value_names_list = collect_all_names(data_classes)
  1587. with open(os.path.join(TARGET_DIRECTORY, GEN_NAMES_H), 'w') as f:
  1588. f.write(COPYRIGHT)
  1589. write_generated_notice(f)
  1590. guard = 'API_GEN_NAMES';
  1591. f.write('#ifndef ' + guard + '\n')
  1592. f.write('#define ' + guard + '\n\n')
  1593. f.write('\n' + NAMESPACES_BEGIN + '\n\n')
  1594. for name in all_class_names:
  1595. write_name_def(f, name, CLASS_PREFIX)
  1596. f.write('\n')
  1597. for name in all_item_param_names_list:
  1598. write_name_def(f, name)
  1599. f.write('\n')
  1600. all_enums_list = list(g_enums)
  1601. all_enums_list.sort(key=str.casefold)
  1602. for name in all_enums_list:
  1603. write_name_def(f, name, ENUM_PREFIX)
  1604. f.write('\n')
  1605. for name in all_enum_value_names_list:
  1606. write_name_def_verbatim(f, name, ENUM_VALUE_PREFIX)
  1607. f.write('\n')
  1608. for name in all_const_value_names_list:
  1609. write_name_def_verbatim(f, name, CONSTANT_VALUE_PREFIX)
  1610. f.write('\n')
  1611. f.write(NAMESPACES_END + '\n\n')
  1612. f.write('#endif // ' + guard + '\n\n')
  1613. def generate_pyi_class(f, name, class_def):
  1614. f.write('class ' + name + '():\n')
  1615. f.write(' def __init__(\n')
  1616. param_ind = ' '
  1617. f.write(param_ind + 'self,\n')
  1618. # ctor
  1619. if KEY_ITEMS in class_def:
  1620. generated_params = set()
  1621. num_items = len(class_def[KEY_ITEMS])
  1622. for i in range(0, num_items):
  1623. item = class_def[KEY_ITEMS][i]
  1624. name = item[KEY_NAME]
  1625. if name in generated_params:
  1626. continue
  1627. generated_params.add(name)
  1628. f.write(param_ind + name)
  1629. # self-referencing classes must use string type reference
  1630. # https://www.python.org/dev/peps/pep-0484/#forward-references
  1631. t = yaml_type_to_py_type(item[KEY_TYPE])
  1632. q = '\'' if t == name else ''
  1633. f.write(' : ' + q + t + q)
  1634. if KEY_DEFAULT in item:
  1635. f.write(' = ' + get_default_or_unset_value_py(item))
  1636. if i + 1 != num_items:
  1637. f.write(',')
  1638. f.write('\n')
  1639. f.write(' ):\n')
  1640. # class members
  1641. if KEY_ITEMS in class_def and class_def[KEY_ITEMS]:
  1642. num_items = len(class_def[KEY_ITEMS])
  1643. for i in range(0, num_items):
  1644. member_name = class_def[KEY_ITEMS][i][KEY_NAME]
  1645. f.write(' self.' + member_name + ' = ' + member_name + '\n')
  1646. else:
  1647. f.write(' pass')
  1648. f.write('\n\n')
  1649. if KEY_METHODS in class_def:
  1650. # for now, we just simply print the first variant oif there are multiple methods with the same name
  1651. printed_methods = set()
  1652. for method in class_def[KEY_METHODS]:
  1653. method_name = method[KEY_NAME]
  1654. if method_name in printed_methods:
  1655. continue
  1656. printed_methods.add(method_name)
  1657. f.write(' def ' + method[KEY_NAME] + '(\n')
  1658. f.write(param_ind + 'self,\n')
  1659. if KEY_PARAMS in method:
  1660. num_params = len(method[KEY_PARAMS])
  1661. for i in range(0, num_params):
  1662. param = method[KEY_PARAMS][i]
  1663. f.write(param_ind + param[KEY_NAME])
  1664. t = yaml_type_to_py_type(param[KEY_TYPE])
  1665. q = '\'' if t == name else ''
  1666. f.write(' : ' + q + t + q)
  1667. if KEY_DEFAULT in param:
  1668. f.write(' = ' + get_default_or_unset_value_py(param))
  1669. if i + 1 != num_params:
  1670. f.write(',')
  1671. f.write('\n')
  1672. f.write(' )')
  1673. if KEY_RETURN_TYPE in method:
  1674. f.write(' -> \'' + yaml_type_to_py_type(method[KEY_RETURN_TYPE]) + '\'')
  1675. else:
  1676. f.write(' -> ' + PY_NONE)
  1677. f.write(':\n')
  1678. f.write(' pass\n\n')
  1679. def generate_pyi_file(data_classes):
  1680. with open(os.path.join(TARGET_DIRECTORY, MCELL_PYI), 'w') as f:
  1681. f.write('from typing import List, Dict, Callable, Any\n')
  1682. f.write('from enum import Enum\n\n')
  1683. f.write('INT32_MAX = 2147483647 # do not use this constant in your code\n\n')
  1684. f.write('FLT_MAX = 3.40282346638528859812e+38 # do not use this constant in your code\n\n')
  1685. f.write('# "forward" declarations to make the type hints valid\n')
  1686. for key, value in sorted(data_classes.items()):
  1687. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1688. f.write('class ' + key + '():\n pass\n')
  1689. f.write('\n')
  1690. species_def = ''
  1691. # Vec3
  1692. f.write(
  1693. 'class Vec3():\n'
  1694. ' def __init__(self, x : float = 0, y : float = 0, z : float = 0):\n'
  1695. ' self.x = x\n'
  1696. ' self.y = y\n'
  1697. ' self.z = z\n'
  1698. '\n'
  1699. 'class Vec2():\n'
  1700. ' def __init__(self, u : float = 0, v : float = 0):\n'
  1701. ' self.u = u\n'
  1702. ' self.v = v\n'
  1703. '\n'
  1704. )
  1705. # generate enums first, then constants
  1706. enums = data_classes[KEY_ENUMS]
  1707. for enum in enums:
  1708. f.write('class ' + enum[KEY_NAME] + '(Enum):\n')
  1709. for enum_item in enum[KEY_VALUES]:
  1710. f.write(' ' + enum_item[KEY_NAME] + ' = ' + str(enum_item[KEY_VALUE]) + '\n')
  1711. f.write('\n')
  1712. f.write('\n\n')
  1713. constants = data_classes[KEY_CONSTANTS]
  1714. for const in constants:
  1715. if const[KEY_TYPE] == YAML_TYPE_SPECIES:
  1716. # Species constants are a bit special
  1717. ctor_param = get_underscored(const[KEY_VALUE]).upper()
  1718. species_def += const[KEY_NAME] + ' = ' + YAML_TYPE_SPECIES + '(\'' + ctor_param + '\')\n'
  1719. elif const[KEY_TYPE] == YAML_TYPE_STR:
  1720. f.write(const[KEY_NAME] + ' = \'' + str(const[KEY_VALUE]) + '\'\n')
  1721. else:
  1722. f.write(const[KEY_NAME] + ' = ' + str(const[KEY_VALUE]) + '\n')
  1723. f.write('\n\n')
  1724. for key, value in sorted(data_classes.items()):
  1725. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1726. generate_pyi_class(f, key, value)
  1727. # we need to define the Species contants after they were defined
  1728. f.write(species_def)
  1729. def indent_and_fix_rst_chars(text, indent):
  1730. return text.replace('\n', '\n' + indent).replace('*', '\\*').replace('@', '\\@')
  1731. def load_and_generate_data_classes():
  1732. # create work directory
  1733. work_dir = os.path.join('..', 'work')
  1734. if not os.path.exists(work_dir):
  1735. os.mkdir(work_dir)
  1736. data_classes = {}
  1737. for cat in CATEGORIES:
  1738. input_file = cat + '.yaml'
  1739. with open(input_file) as file:
  1740. # The FullLoader parameter handles the conversion from YAML
  1741. # scalar values to Python the dictionary format
  1742. loaded_data = yaml.load(file, Loader=yaml.FullLoader)
  1743. if loaded_data:
  1744. # set information about the original file name
  1745. for key,value in loaded_data.items():
  1746. if key != KEY_CONSTANTS and key != KEY_ENUMS:
  1747. value[KEY_CATEGORY] = cat
  1748. data_classes.update(loaded_data)
  1749. else:
  1750. print("File " + input_file + " is empty (or could not be loaded as yaml), ignoring it")
  1751. if VERBOSE:
  1752. print("Loaded " + input_file)
  1753. if VERBOSE:
  1754. print(data_classes)
  1755. assert type(data_classes) == dict
  1756. generate_data_classes(data_classes)
  1757. generate_names_header(data_classes)
  1758. generate_pyi_file(data_classes)
  1759. doc.generate_documentation(data_classes)
  1760. if __name__ == '__main__':
  1761. if len(sys.argv) == 2 and sys.argv[1] == '-v':
  1762. VERBOSE = True
  1763. load_and_generate_data_classes()