modelverse.py 25 KB

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