modelverse.py 24 KB

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