modelverse.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. import urllib
  2. import urllib2
  3. import json
  4. import random
  5. from urllib2 import URLError
  6. import sys
  7. import time
  8. COMPILER_PATH = "interface/HUTN"
  9. MODE_UNCONNECTED = 0
  10. MODE_UNAUTHORIZED = 1
  11. MODE_MODELLING = 2
  12. MODE_MODIFY = 3
  13. MODE_DIALOG = 4
  14. MODE_MANUAL = 5
  15. # Bind to the compiler (might have to update path manually!)
  16. sys.path.append(COMPILER_PATH)
  17. from hutn_compiler.compiler import main as do_compile
  18. # Exceptions
  19. class ModelverseException(Exception):
  20. pass
  21. class UnknownError(ModelverseException):
  22. pass
  23. class UnknownIdentifier(ModelverseException):
  24. pass
  25. class UnknownType(ModelverseException):
  26. pass
  27. class NotAnAssociation(ModelverseException):
  28. pass
  29. class UnsupportedValue(ModelverseException):
  30. pass
  31. class CompilationError(ModelverseException):
  32. pass
  33. class NoSuchAttribute(ModelverseException):
  34. pass
  35. class UnknownModel(ModelverseException):
  36. pass
  37. class ConnectionError(ModelverseException):
  38. pass
  39. class ModelExists(ModelverseException):
  40. pass
  41. class PermissionDenied(ModelverseException):
  42. pass
  43. class InvalidMode(ModelverseException):
  44. pass
  45. class InterfaceMismatch(ModelverseException):
  46. pass
  47. # Helper functions and configuration: do not use yourself!
  48. taskname = random.random()
  49. address = None
  50. last_output = None
  51. mode = MODE_UNCONNECTED
  52. prev_mode = None
  53. def _input(value):
  54. # Ugly json encoding of primitives
  55. if isinstance(value, type([])):
  56. value = json.dumps(value)
  57. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": taskname}))).read()
  58. else:
  59. value = json.dumps(value)
  60. #print("Set input: " + str(value))
  61. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  62. def _input_raw(value, taskname):
  63. # Ugly json encoding of primitives
  64. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  65. def _compile_AL(code):
  66. # Compile an action language file and send the compiled code
  67. code_fragments = code.split("\n")
  68. code_fragments = [i for i in code_fragments if i.strip() != ""]
  69. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  70. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  71. code_fragments = [i[initial_tabs:] for i in code_fragments]
  72. code_fragments.append("")
  73. code = "\n".join(code_fragments)
  74. with open("__constraint.alc", "w") as f:
  75. f.write(code)
  76. f.flush()
  77. return do_compile("__constraint.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
  78. def _compile_model(code):
  79. # Compile a model and send the compiled graph
  80. # First change multiple spaces to a tab
  81. code_fragments = code.split("\n")
  82. code_fragments = [i for i in code_fragments if i.strip() != ""]
  83. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  84. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  85. code_fragments = [i[initial_tabs:] for i in code_fragments]
  86. code_fragments.append("")
  87. code = "\n".join(code_fragments)
  88. with open("__model.mvc", "w") as f:
  89. f.write(code)
  90. f.flush()
  91. return do_compile("__model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M") + ["exit"]
  92. def _output(expected=None):
  93. try:
  94. global last_output
  95. last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read())
  96. except:
  97. raise UnknownError()
  98. if expected is not None and last_output != expected:
  99. raise InterfaceMismatch(_last_output(), expected)
  100. return last_output
  101. def _last_output():
  102. return last_output
  103. # Raise common exceptions
  104. def _handle_output(requested=None, split=None):
  105. value = _output()
  106. if value.startswith("Model exists: "):
  107. raise ModelExists(value.split(": ", 1)[1])
  108. elif value.startswith("Permission denied"):
  109. raise PermissionDenied(value.split(": ", 1)[1])
  110. elif value.startswith("Model not found: "):
  111. raise UnknownModel(value.split(": ", 1)[1])
  112. elif value.startswith("Element not found: "):
  113. raise UnknownIdentifier(value.split(": ", 1)[1])
  114. elif value.startswith("Element exists: "):
  115. raise ElementExists(value.split(": ", 1)[1])
  116. elif value.startswith("Attribute not found: "):
  117. raise NoSuchAttribute(value.split(": ", 1)[1])
  118. elif requested is not None and value.startswith(requested):
  119. if split is None:
  120. return value
  121. else:
  122. splitted = value.strip().split(split, 1)
  123. if len(splitted) == 1:
  124. return ""
  125. else:
  126. return splitted[1].rstrip()
  127. else:
  128. raise InterfaceMismatch(value)
  129. # Main MvC operations
  130. def init(address_param="http://127.0.0.1:8001", timeout=20.0):
  131. """Starts up the connection to the Modelverse."""
  132. # return None
  133. # raises ConnectionError
  134. # raises UnknownError
  135. # raises InvalidMode
  136. global mode
  137. if mode != MODE_UNCONNECTED:
  138. raise InvalidMode()
  139. global address
  140. address = address_param
  141. start_time = time.time()
  142. while 1:
  143. try:
  144. _input_raw('"%s"' % taskname, "task_manager")
  145. mode = MODE_UNAUTHORIZED
  146. break
  147. except URLError as e:
  148. if time.time() - start_time > timeout:
  149. raise ConnectionError(e.reason)
  150. else:
  151. time.sleep(0.1)
  152. def login(username, password):
  153. """Log in a user, if user doesn't exist, it is created."""
  154. # return None
  155. # raises UnknownError
  156. # raises PermissionDenied
  157. # raises InterfaceMismatch
  158. global mode
  159. if mode != MODE_UNAUTHORIZED:
  160. raise InvalidMode()
  161. _output("Log on as which user?")
  162. _input(username)
  163. if _output() == "Password for existing user?":
  164. _input(password)
  165. if _output() == "Welcome to the Model Management Interface v2.0!":
  166. _output("Use the 'help' command for a list of possible commands")
  167. _input("quiet")
  168. mode = MODE_MODELLING
  169. elif _last_output() == "Wrong password!":
  170. raise PermissionDenied()
  171. else:
  172. raise InterfaceMismatch(_last_output())
  173. elif _last_output() == "This is a new user: please give password!":
  174. _input(password)
  175. _output("Please repeat the password")
  176. _input(password)
  177. if _output() == "Passwords match!":
  178. _output("Welcome to the Model Management Interface v2.0!")
  179. _output("Use the 'help' command for a list of possible commands")
  180. _input("quiet")
  181. mode = MODE_MODELLING
  182. elif _last_output() == "Not the same password!":
  183. # We just sent the same password, so it should be identical, unless the interface changed
  184. raise InterfaceMismatch(_last_output())
  185. else:
  186. raise InterfaceMismatch(_last_output())
  187. else:
  188. raise InterfaceMismatch(_last_output())
  189. def model_add(model_name, metamodel_name, model_code=None):
  190. """Instantiate a new model."""
  191. # return None
  192. # raises UnknownModel
  193. # raises ModelExists
  194. # raises UnknownError
  195. # raises PermissionDenied
  196. # raises CompilationError
  197. if mode != MODE_MODELLING:
  198. raise InvalidMode()
  199. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  200. if model_code is not None:
  201. try:
  202. compiled = _compile_model(model_code)
  203. except:
  204. raise CompilationError()
  205. else:
  206. compiled = ["exit"]
  207. _input(["model_add", metamodel_name, model_name])
  208. _handle_output("Waiting for model constructors...")
  209. _input(compiled)
  210. _output("Success")
  211. def model_modify(model_name):
  212. """Modify an existing model."""
  213. # return is_write
  214. # raises UnknownModel
  215. # raises PermissionDenied
  216. # raises UnknownError
  217. global mode
  218. global prev_mode
  219. if mode == MODE_MANUAL:
  220. prev_mode = MODE_MANUAL
  221. mode = MODE_MODIFY
  222. return None
  223. if mode != MODE_MODELLING:
  224. raise InvalidMode()
  225. prev_mode = MODE_MODELLING
  226. _input(["model_modify", model_name])
  227. _handle_output("Success")
  228. # Mode has changed
  229. mode = MODE_MODIFY
  230. _output("Model loaded, ready for commands!")
  231. if ("r/w" in _output()):
  232. write = True
  233. else:
  234. write = False
  235. _output("Use 'help' command for a list of possible commands")
  236. return write
  237. def model_list():
  238. """List all models."""
  239. # return [(model1, metamodel1), (model2, metamodel2), ...]
  240. # raises UnknownError
  241. if mode != MODE_MODELLING:
  242. raise InvalidMode()
  243. _input("model_list")
  244. output = _handle_output("Success: ", split=" ")
  245. if output == "":
  246. return set([])
  247. lst = set([])
  248. value = output.strip().split("\n")
  249. for v in value:
  250. m, mm = v.split(":")
  251. m = m.strip()
  252. mm = mm.strip()
  253. lst.add((m, mm))
  254. return lst
  255. def model_list_full():
  256. """List full information on all models."""
  257. # return [(model1, metamodel1, owner1, group1, permissions1), (model2, metamodel2, owner2, group2, permissions2), ...]
  258. # raises UnknownError
  259. if mode != MODE_MODELLING:
  260. raise InvalidMode()
  261. _input("model_list_full")
  262. output = _handle_output("Success: ", split=" ")
  263. if output == "":
  264. return set([])
  265. lst = set([])
  266. value = output.strip().split("\n")
  267. for v in value:
  268. m, mm = v.split(":")
  269. m = m.strip()
  270. mm = mm.strip()
  271. perm, own, grp, m = m.split(" ")
  272. lst.add((m, mm, own, grp, perm))
  273. return lst
  274. def verify(model):
  275. """Verify if a model conforms to its metamodel."""
  276. # return "verification_result"
  277. # raises UnknownError
  278. # raises UnknownModel
  279. if mode != MODE_MODELLING:
  280. raise InvalidMode()
  281. _input(["verify", model])
  282. return _handle_output("Success: ", split=" ")
  283. def model_overwrite(model_name, new_model=None):
  284. """Upload a new model and overwrite an existing model."""
  285. # return None
  286. # raises UnknownModel
  287. # raises PermissionDenied
  288. # raises CompilationError
  289. # raises UnknownError
  290. if mode != MODE_MODELLING:
  291. raise InvalidMode()
  292. if new_model is not None:
  293. try:
  294. compiled = _compile_model(new_model)
  295. except Exception as e:
  296. raise CompilationError(e)
  297. else:
  298. compiled = ["exit"]
  299. _input(["model_overwrite", model_name])
  300. _handle_output("Waiting for model constructors...")
  301. _input(compiled)
  302. _output("Success")
  303. def user_logout():
  304. """Log out the current user and break the connection."""
  305. # return None
  306. # raises UnknownError
  307. global mode
  308. if mode != MODE_MODELLING:
  309. raise InvalidMode()
  310. _input("exit")
  311. mode = MODE_UNCONNECTED
  312. def user_delete():
  313. """Removes the current user and break the connection."""
  314. # return None
  315. # raises UnknownError
  316. global mode
  317. if mode != MODE_MODELLING:
  318. raise InvalidMode()
  319. _input("self-destruct")
  320. mode = MODE_UNCONNECTED
  321. def model_render(model, mapper):
  322. """Fetch a rendered verion of a model."""
  323. # return JSON_representation
  324. # raises UnknownError
  325. # raises UnknownIdentifier
  326. # raises InterfaceMismatch
  327. global mode
  328. if mode != MODE_MODELLING:
  329. raise InvalidMode()
  330. _input(["model_render", model, mapper])
  331. return _handle_output("Success: ", split=" ")
  332. def transformation_between(source, target):
  333. global mode
  334. if mode != MODE_MODELLING:
  335. raise InvalidMode()
  336. _input(["transformation_between", source, target])
  337. output = _handle_output("Success: ", split=" ")
  338. if output == "":
  339. return set([])
  340. lst = set([v for v in output.split("\n")])
  341. def transformation_add_MT_language(metamodels, RAMified_name):
  342. """Create a new Model Transformation language out of a set of metamodels."""
  343. global mode
  344. if mode != MODE_MODELLING:
  345. raise InvalidMode()
  346. _input(["transformation_add_MT_language"] + metamodels + ["", RAMified_name])
  347. _handle_output("Success")
  348. def transformation_add_MT(RAMified_metamodel, source_metamodels, target_metamodels, operation_name, code):
  349. """Create a new model transformation."""
  350. global mode
  351. if mode != MODE_MODELLING:
  352. raise InvalidMode()
  353. try:
  354. compiled = _compile_model(code)
  355. except Exception as e:
  356. raise CompilationError(e)
  357. _input(["transformation_add_MT", RAMified_metamodel] + source_metamodels + [""] + target_metamodels + ["", operation_name])
  358. _handle_output("Waiting for model constructors...")
  359. _input(compiled)
  360. _handle_output("Success")
  361. def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code):
  362. """Create a new action language model, which can be executed."""
  363. global mode
  364. if mode != MODE_MODELLING:
  365. raise InvalidMode()
  366. try:
  367. compiled = _compile_AL(code)
  368. except Exception as e:
  369. raise CompilationError(e)
  370. _input(["transformation_add_AL"] + source_metamodels + [""] + target_metamodels + [""] + [operation_name])
  371. _handle_output("Waiting for code constructors...")
  372. _input(compiled)
  373. _output("Success")
  374. def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name):
  375. """Create a new manual model operation."""
  376. global mode
  377. if mode != MODE_MODELLING:
  378. raise InvalidMode()
  379. _input(["transformation_add_MANUAL"] + source_metamodels + [""] + target_metamodels + [""] + [operation_name])
  380. _handle_output("Success")
  381. def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  382. """Execute an existing model operation."""
  383. global mode
  384. if mode != MODE_MODELLING:
  385. raise InvalidMode()
  386. mv_dict_rep = []
  387. for key, value in input_models_dict.items():
  388. mv_dict_rep += [key, value]
  389. mv_dict_rep += [""]
  390. for key, value in output_models_dict.items():
  391. mv_dict_rep += [key, value]
  392. mv_dict_rep += [""]
  393. _input(["transformation_execute", operation_name] + mv_dict_rep)
  394. _handle_output("Success: ready for AL execution")
  395. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  396. while _output() not in ["Success", "Failure"]:
  397. mode = MODE_DIALOG
  398. reply = callback(_last_output())
  399. mode = MODE_MODELLING
  400. if reply is not None:
  401. _input(reply)
  402. # Got termination message, so we are done!
  403. if _last_output() == "Success":
  404. return True
  405. else:
  406. return False
  407. def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  408. """Execute an existing model operation."""
  409. global mode
  410. if mode != MODE_MODELLING:
  411. raise InvalidMode()
  412. mv_dict_rep = []
  413. for key, value in input_models_dict.items():
  414. mv_dict_rep += [key, value]
  415. mv_dict_rep += [""]
  416. for key, value in output_models_dict.items():
  417. mv_dict_rep += [key, value]
  418. mv_dict_rep += [""]
  419. _input(["transformation_execute", operation_name] + mv_dict_rep)
  420. _handle_output("Success: ready for MANUAL execution")
  421. # Skip over the begin of mini_modify
  422. _output()
  423. _output()
  424. _output()
  425. _output()
  426. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  427. mode = MODE_MANUAL
  428. callback()
  429. # Finished, so leave
  430. _input("exit")
  431. mode = MODE_MODELLING
  432. # Got termination message, so we are done!
  433. if _output() == "Success":
  434. return True
  435. else:
  436. return False
  437. def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  438. """Execute an existing model operation."""
  439. global mode
  440. if mode != MODE_MODELLING:
  441. raise InvalidMode()
  442. mv_dict_rep = []
  443. for key, value in input_models_dict.items():
  444. mv_dict_rep += [key, value]
  445. mv_dict_rep += [""]
  446. for key, value in output_models_dict.items():
  447. mv_dict_rep += [key, value]
  448. mv_dict_rep += [""]
  449. _input(["transformation_execute", operation_name] + mv_dict_rep)
  450. _handle_output("Success: ready for MT execution")
  451. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  452. while _output() not in ["Success", "Failure"]:
  453. reply = callback(_last_output())
  454. if reply is not None:
  455. _input(reply)
  456. # Got termination message, so we are done!
  457. if _last_output() == "Success":
  458. return True
  459. else:
  460. return False
  461. def transformation_list():
  462. """List existing model operations."""
  463. global mode
  464. if mode != MODE_MODELLING:
  465. raise InvalidMode()
  466. _input("transformation_list")
  467. output = _handle_output("Success: ", split=" ")
  468. if output == "":
  469. return set([])
  470. lst = set([])
  471. value = output.strip().split("\n")
  472. for v in value:
  473. t, m = v.strip().split(" ", 1)
  474. t = t[1:-1].strip()
  475. m = m.strip().split(":")[0].strip()
  476. lst.add((t, m))
  477. return lst
  478. def transformation_detail():
  479. """List full details of a a model operation."""
  480. raise NotImplementedError()
  481. def transformation_RAMify(metamodel_name, RAMified_metamodel_name):
  482. """Ramify an existing metamodel."""
  483. global mode
  484. if mode != MODE_MODELLING:
  485. raise InvalidMode()
  486. _input(["transformation_RAMify", metamodel_name, RAMified_metamodel_name])
  487. _handle_output("Success")
  488. def process_execute():
  489. """Execute a process model."""
  490. raise NotImplementedError()
  491. def permission_modify():
  492. """Modify permissions of a model."""
  493. raise NotImplementedError()
  494. def permission_owner():
  495. """Modify the owning user of a model."""
  496. raise NotImplementedError()
  497. def permission_group():
  498. """Modify the owning group of a model."""
  499. raise NotImplementedError()
  500. def group_create():
  501. """Create a new group."""
  502. raise NotImplementedError()
  503. def group_delete():
  504. """Delete a group of which you are an owner."""
  505. raise NotImplementedError()
  506. def group_owner_add():
  507. """Add a new owning user to a group you own."""
  508. raise NotImplementedError()
  509. def group_owner_delete():
  510. """Delete an owning user to a group you own."""
  511. raise NotImplementedError()
  512. def group_join():
  513. """Add a new user to a group you own."""
  514. raise NotImplementedError()
  515. def group_kick():
  516. """Delete a user from a group you own."""
  517. raise NotImplementedError()
  518. def group_list():
  519. """List existing groups."""
  520. raise NotImplementedError()
  521. def admin_promote():
  522. """Promote a user to admin status."""
  523. raise NotImplementedError()
  524. def admin_demote():
  525. """Demote a user from admin status."""
  526. raise NotImplementedError()
  527. # Actual operations on the model
  528. def element_list(model_name):
  529. """Return a list of all IDs and the type of the element"""
  530. # return [(name1, type1), (name2, type2), ...]
  531. # raises UnknownError
  532. model_modify(model_name)
  533. if mode != MODE_MODIFY:
  534. raise InvalidMode()
  535. try:
  536. _input("list_full")
  537. lst = set([])
  538. output = _handle_output("Success: ", split=" ")
  539. if output == "":
  540. return set([])
  541. for v in output.split("\n"):
  542. m, mm = v.split(":")
  543. m = m.strip()
  544. mm = mm.strip()
  545. lst.add((m, mm))
  546. return lst
  547. finally:
  548. model_exit()
  549. def types(model_name):
  550. """Return a list of all types usable in the model"""
  551. # return [type1, type2, ...]
  552. # raises UnknownError
  553. model_modify(model_name)
  554. if mode != MODE_MODIFY:
  555. raise InvalidMode()
  556. try:
  557. _input("types")
  558. lst = set([])
  559. output = _handle_output("Success: ", split=" ")
  560. if output == "":
  561. return set([])
  562. for v in output.split("\n"):
  563. m, mm = v.split(":")
  564. m = m.strip()
  565. lst.add(m)
  566. return lst
  567. finally:
  568. model_exit()
  569. def types_full(model_name):
  570. """Return a list of full types usable in the model"""
  571. # return [(type1, typetype1), (type2, typetype2), ...]
  572. # raises UnknownError
  573. model_modify(model_name)
  574. if mode != MODE_MODIFY:
  575. raise InvalidMode()
  576. try:
  577. _input("types")
  578. lst = set([])
  579. output = _handle_output("Success: ", split=" ")
  580. if output == "":
  581. return set([])
  582. for v in output.split("\n"):
  583. m, mm = v.split(":")
  584. m = m.strip()
  585. mm = mm.strip()
  586. lst.add((m, mm))
  587. return lst
  588. finally:
  589. model_exit()
  590. def read(model_name, ID):
  591. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  592. # return (type, (source, target))
  593. # raises UnknownError
  594. # raises UnknownIdentifier
  595. model_modify(model_name)
  596. if mode != MODE_MODIFY:
  597. raise InvalidMode()
  598. try:
  599. _input(["read", ID])
  600. output = _handle_output("Success: ", split=" ")
  601. v = output.split("\n")
  602. t = v[1].split(":")[1].strip()
  603. if (not v[0].startswith("Source:")):
  604. rval = (t, None)
  605. else:
  606. src = v[0].split(":")[1].strip()
  607. trg = v[1].split(":")[1].strip()
  608. rval = (t, (src, trg))
  609. return rval
  610. finally:
  611. model_exit()
  612. def read_attrs(model_name, ID):
  613. """Return a dictionary of attribute value pairs"""
  614. # return {attr1: value1, attr2: value2, ...}
  615. # raises UnknownError
  616. # raises UnknownIdentifier
  617. model_modify(model_name)
  618. if mode != MODE_MODIFY:
  619. raise InvalidMode()
  620. try:
  621. _input(["read", ID])
  622. output = _handle_output("Success: ", split=" ")
  623. v = output.split("\n")
  624. searching = True
  625. rval = {}
  626. for r in v:
  627. if searching:
  628. if r == "Attributes:":
  629. # Start working on attributes
  630. searching = False
  631. else:
  632. key, value = r.split(":", 1)
  633. _, value = value.split("=", 1)
  634. key = json.loads(key.strip())
  635. value = value.strip()
  636. if value == "None":
  637. value = None
  638. elif value == "True":
  639. value = True
  640. elif value == "False":
  641. value = False
  642. else:
  643. value = json.loads(value)
  644. rval[key] = value
  645. return rval
  646. finally:
  647. model_exit()
  648. def instantiate(model_name, typename, edge=None, ID=""):
  649. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  650. # return instantiated_ID
  651. # raises UnknownError
  652. # raises UnknownType
  653. # raises UnknownIdentifier
  654. # raises NotAnEdge
  655. model_modify(model_name)
  656. if mode != MODE_MODIFY:
  657. raise InvalidMode()
  658. try:
  659. if edge is None:
  660. _input(["instantiate_node", typename, ID])
  661. else:
  662. _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
  663. return _handle_output("Success: ", split=" ")
  664. finally:
  665. model_exit()
  666. def delete_element(model_name, ID):
  667. """Delete the element with the given ID"""
  668. # return None
  669. # raises UnknownError
  670. # raises UnknownIdentifier
  671. model_modify(model_name)
  672. if mode != MODE_MODIFY:
  673. raise InvalidMode()
  674. try:
  675. _input(["delete", ID])
  676. _handle_output("Success")
  677. finally:
  678. model_exit()
  679. def attr_assign(model_name, ID, attr, value):
  680. """Assign a value to an attribute"""
  681. # return None
  682. # raises UnknownError
  683. # raises UnknownIdentifier
  684. # raises NoSuchAttribute
  685. # raises UnsupportedValue
  686. model_modify(model_name)
  687. if mode != MODE_MODIFY:
  688. raise InvalidMode()
  689. try:
  690. _input(["attr_add", ID, attr, value])
  691. _handle_output("Success")
  692. finally:
  693. model_exit()
  694. def attr_assign_code(model_name, ID, attr, code):
  695. """Assign a piece of Action Language code to the attribute"""
  696. # return None
  697. # raises UnknownError
  698. # raises UnknownIdentifier
  699. # raises NoSuchAttribute
  700. # raises UnsupportedValue
  701. try:
  702. compiled = _compile_AL(code)
  703. except Exception as e:
  704. raise CompilationError(e)
  705. model_modify(model_name)
  706. if mode != MODE_MODIFY:
  707. raise InvalidMode()
  708. try:
  709. _input(["attr_add", ID, attr])
  710. _handle_output("Waiting for code constructors...")
  711. _input(compiled)
  712. _output("Success")
  713. finally:
  714. model_exit()
  715. def attr_delete(model_name, ID, attr):
  716. """Remove an attribute."""
  717. model_modify(model_name)
  718. if mode != MODE_MODIFY:
  719. raise InvalidMode()
  720. try:
  721. _input(["attr_del", ID, attr])
  722. _handle_output("Success")
  723. finally:
  724. model_exit()
  725. def read_outgoing(model_name, ID, typename):
  726. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  727. # return [name1, name2, ...]
  728. # raises UnknownError
  729. # raises UnknownIdentifier
  730. model_modify(model_name)
  731. if mode != MODE_MODIFY:
  732. raise InvalidMode()
  733. try:
  734. _input(["read_outgoing", ID, typename])
  735. output = _handle_output("Success: ", split=" ")
  736. if output == "":
  737. return set([])
  738. else:
  739. return set(output.split("\n"))
  740. finally:
  741. model_exit()
  742. def read_incoming(model_name, ID, typename):
  743. """Returns a list of all incoming associations of a specific type ("" = all)"""
  744. # return [name1, name2, ...]
  745. # raises UnknownError
  746. # raises UnknownIdentifier
  747. # raises UnknownType
  748. model_modify(model_name)
  749. if mode != MODE_MODIFY:
  750. raise InvalidMode()
  751. try:
  752. _input(["read_incoming", ID, typename])
  753. output = _handle_output("Success: ", split=" ")
  754. if output == "":
  755. return set([])
  756. else:
  757. return set(output.split("\n"))
  758. finally:
  759. model_exit()
  760. def read_association_source(model_name, ID):
  761. """Returns the source of an association."""
  762. # returns name
  763. # raises UnknownError
  764. # raises UnknownIdentifier
  765. # raises NotAnAssociation
  766. model_modify(model_name)
  767. if mode != MODE_MODIFY:
  768. raise InvalidMode()
  769. try:
  770. _input(["read_association_source", ID])
  771. return _handle_output("Success: ", split=" ")
  772. finally:
  773. model_exit()
  774. def read_association_destination(model_name, ID):
  775. """Returns the destination of an association."""
  776. # returns name
  777. # raises UnknownError
  778. # raises UnknownIdentifier
  779. # raises NotAnAssociation
  780. model_modify(model_name)
  781. if mode != MODE_MODIFY:
  782. raise InvalidMode()
  783. try:
  784. _input(["read_association_destination", ID])
  785. return _handle_output("Success: ", split=" ")
  786. finally:
  787. model_exit()
  788. def model_exit():
  789. """Leave model modify mode."""
  790. # return None
  791. # raises UnknownError
  792. global mode
  793. global prev_mode
  794. if prev_mode == MODE_MANUAL:
  795. mode = MODE_MANUAL
  796. return
  797. if mode != MODE_MODIFY:
  798. raise InvalidMode()
  799. _input("exit")
  800. _output("Success")
  801. mode = MODE_MODELLING