dapi.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. '''*****************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
  4. This file is part of AToMPM.
  5. AToMPM is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU Lesser General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or (at your option) any later
  8. version.
  9. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  11. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  14. *****************************************************************************'''
  15. import copy, subprocess, traceback, os
  16. from pytcore.core.himesis import HConstants as HC
  17. from utils import Utilities as utils
  18. '''
  19. implements the DesignerAPI functions used in pattern/attribute constraints
  20. and actions
  21. NOTE: the current username is required (and remembered) to enable file I/O
  22. within his own directory... the current backend aswid is required (and
  23. remembered) to enable httpreqs to backend from rule code
  24. NOTE: cloned from mmmk.js constraint/action/accessor DesignerAPI... same
  25. comments and thoughts apply... noteworthy differences are
  26. . instead of atompm ids, API functions now take __pLabels as node
  27. identifiers
  28. . 'sys_*()' functions enable system calls and file i/o
  29. . 'session_*()' functions provide a clean and efficient way to
  30. remember information across rules
  31. . the 'isConnectionType()' function '''
  32. class DesignerAPI :
  33. def __init__(self,username,aswid,mtwid) :
  34. self._username = username
  35. self._aswid = aswid
  36. self._mtwid = mtwid
  37. def _aswPrintReq(self,msg):
  38. utils.httpReq( 'PUT',
  39. '127.0.0.1:8124',
  40. '/GET/console?wid='+self._aswid,
  41. {'text':msg})
  42. def _printToDevCon(self,msg):
  43. self._aswPrintReq(msg)
  44. '''
  45. configure this instance of DesignerAPI
  46. 1. set the Himesis graph that will be used to satisfy getAttr/setAttr/...
  47. function calls... this graph represents a (partially) matched LHS, NAC
  48. or RHS
  49. 2. set the type of the calling function... legal values are
  50. patternCondition
  51. patternAction
  52. attrCondition
  53. attrAction
  54. 3. set the pattern-label -> graph-index mapping
  55. 4. set reference to exception holder
  56. 5. set the __pLabel of the owner of the calling function, if any
  57. 6. set the attribute that owns the calling function, if any
  58. 7. set reference to journal (to write changes to), if any
  59. 8. extend the pattern-label -> graph-index map w/ $atompmId -> graph-index
  60. mappings... this enables access to all graph nodes (not just matched
  61. ones, that are incidently the only ones w/ associated __pLabels)
  62. TBI: self._pl2gi should be given a name that reflects step 8. and errors
  63. pertaining to missing/incorrect pLabel parameters should be updated
  64. to account for fact that specified pLabels may not be pLabels (i.e.,
  65. they could be of the form '$atompmId:*' '''
  66. def configure(self,graph,type,pl2gi,ex,pLabel=None,attr=None,journal=None) :
  67. self._graph = graph
  68. self._type = type
  69. self._pl2gi = pl2gi
  70. self._ex = ex
  71. self._pLabel = pLabel
  72. self._attr = attr
  73. self._journal = journal
  74. matched = self._pl2gi.values()
  75. for v in self._graph.vs :
  76. if v.index not in matched :
  77. self._pl2gi['$atompmId:'+str(v['$atompmId'])] = v.index
  78. '''
  79. wrapper around raise()... to be properly reported to the client,
  80. exceptions can not simply be raised... this would just cause them to be
  81. captured by their ExecutionContext who might return a generic error (not
  82. the one it caught)... instead, we store exceptions, if any, in self._ex
  83. for the caller to do with them what he wants... '''
  84. def __raise(self,msg) :
  85. self._ex['$err'] = msg
  86. raise RuntimeError(msg)
  87. # ***************************** API functions ***************************** #
  88. def _getAttr(self,attr=None,pLabel=None) :
  89. if pLabel == None :
  90. if self._type.startswith('pattern') :
  91. self.__raise(\
  92. 'getAttr() requires a __pLabel in pattern conditions/actions')
  93. pLabel = self._pLabel
  94. elif not self._type.startswith('pattern') :
  95. self.__raise(\
  96. 'getAttr() only accepts a __pLabel in pattern conditions/actions')
  97. elif pLabel not in self._pl2gi :
  98. self.__raise(\
  99. 'invalid getAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  100. 'with that __pLabel exists, or none is matched yet)')
  101. if attr == None :
  102. if not self._type.startswith('attr') :
  103. self.__raise(\
  104. 'getAttr() can only be called without parameters'+\
  105. 'in attribute conditions/actions')
  106. attr = self._attr
  107. n = self._graph.vs[self._pl2gi[pLabel]]
  108. if attr not in n.attribute_names() :
  109. self.__raise('invalid getAttr() attribute :: '+str(attr))
  110. return copy.deepcopy(n[attr])
  111. def _hasAttr(self,attr=None,pLabel=None) :
  112. if pLabel == None :
  113. if self._type.startswith('pattern') :
  114. self.__raise(\
  115. 'hasAttr() requires a __pLabel in pattern conditions/actions')
  116. pLabel = self._pLabel
  117. elif not self._type.startswith('pattern') :
  118. self.__raise(\
  119. 'hasAttr() only accepts a __pLabel in pattern conditions/actions')
  120. elif pLabel not in self._pl2gi :
  121. self.__raise(\
  122. 'invalid hasAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  123. 'with that __pLabel exists, or none is matched yet)')
  124. if attr == None :
  125. self.__raise(\
  126. 'hasAttr() can not be called without an attribute parameter')
  127. n = self._graph.vs[self._pl2gi[pLabel]]
  128. return attr in n.attribute_names()
  129. def _setAttr(self,attr,val,pLabel) :
  130. if self._type != 'patternAction' :
  131. self.__raise('setAttr() can only be used within RHS actions')
  132. elif pLabel == None :
  133. self.__raise('setAttr() requires a valid __pLabel')
  134. elif pLabel not in self._pl2gi :
  135. self.__raise(\
  136. 'invalid setAttr() __pLabel :: '+str(pLabel)+' (either no node '+\
  137. 'with that __pLabel exists, or none is matched yet)')
  138. n = self._graph.vs[self._pl2gi[pLabel]]
  139. if attr not in n.attribute_names() :
  140. self.__raise('invalid setAttr() attribute :: '+attr)
  141. oldVal = n[attr]
  142. if oldVal != val :
  143. n[attr] = val
  144. self._journal.append(
  145. {'op':'CHATTR',
  146. 'guid':n[HC.GUID],
  147. 'attr':attr,
  148. 'old_val':oldVal,
  149. 'new_val':val})
  150. n[HC.MT_DIRTY] = True
  151. def _getAllNodes(self,fulltypes=None) :
  152. if not self._type.startswith('pattern') :
  153. self.__raise(\
  154. 'getAllNodes() can only be used in pattern conditions/actions')
  155. elif fulltypes != None and fulltypes.__class__ != [].__class__ :
  156. self.__raise('invalid getAllNodes() fulltypes array :: '+fulltypes)
  157. pLabels = []
  158. for pLabel in self._pl2gi :
  159. n = self._graph.vs[self._pl2gi[pLabel]]
  160. if fulltypes == None or n[HC.FULLTYPE] in fulltypes :
  161. pLabels.append(pLabel)
  162. return pLabels
  163. def _getNeighbors(self,dir,type,pLabel) :
  164. if not self._type.startswith('pattern') :
  165. self.__raise(\
  166. 'getNeighbors() can only be used in pattern conditions/actions')
  167. elif pLabel == None :
  168. self.__raise('getNeighbors() requires a valid __pLabel')
  169. elif pLabel not in self._pl2gi :
  170. self.__raise(\
  171. 'invalid getNeighbors() __pLabel :: '+str(pLabel)+' (no node '+\
  172. 'with that __pLabel exists)')
  173. pLabels = set()
  174. if len(self._graph.es) > 0 :
  175. gi2pl = dict((v, k) for (k, v) in self._pl2gi.items())
  176. idx = self._pl2gi[pLabel]
  177. for e in self._graph.get_edgelist() :
  178. if e[0] == idx and \
  179. (dir == '>' or dir == '*') and \
  180. (type == '*' or self._graph.vs[e[1]][HC.FULLTYPE] == type) :
  181. pLabels.add(gi2pl[e[1]])
  182. elif e[1] == idx and \
  183. (dir == '<' or dir == '*') and \
  184. (type == '*' or self._graph.vs[e[0]][HC.FULLTYPE] == type) :
  185. pLabels.add(gi2pl[e[0]])
  186. return list(pLabels)
  187. def _isConnectionType(self,pLabel) :
  188. if not self._type.startswith('pattern') :
  189. self.__raise(\
  190. 'isConnectionType() can only be used in pattern conditions/actions')
  191. elif pLabel == None :
  192. self.__raise('isConnectionType() requires a valid __pLabel')
  193. elif pLabel not in self._pl2gi :
  194. self.__raise(\
  195. 'invalid isConnectionType() __pLabel :: '+str(pLabel)+' (no node '+\
  196. 'with that __pLabel exists)')
  197. return self._graph.vs[self._pl2gi[pLabel]][HC.CONNECTOR_TYPE]
  198. def _session_get(self,key) :
  199. if key in self._graph.session :
  200. return self._graph.session[key]
  201. def _session_put(self,key,val) :
  202. if not self._type.endswith('Action') :
  203. self.__raise(\
  204. 'session_put() can only be used in attribute and pattern actions')
  205. self._graph.session[key] = val
  206. return val
  207. def _pauseTransformation(self):
  208. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'pause'})
  209. def _stopTransformation(self):
  210. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'stop'})
  211. def _resumeTransformation(self):
  212. self._httpReq("PUT", '127.0.0.1:8125', '/execmode?wid='+self._mtwid, {'mode':'play'})
  213. def _httpReq(self,method,host,uri,data) :
  214. if host == None :
  215. return utils.httpReq(
  216. method,
  217. '127.0.0.1:8124',
  218. uri+'?wid='+self._aswid,
  219. data)
  220. else :
  221. return utils.httpReq(method,host,uri,data)
  222. def _print(self,str) :
  223. print(str)
  224. def _sys_call(self,args) :
  225. try :
  226. return subprocess.call(args)
  227. except OSError as ex :
  228. self.__raise('system call crashed on :: '+ex.strerror)
  229. def _sys_mkdir(self,path) :
  230. try :
  231. return os.makedirs('./users/'+self._username+'/'+path)
  232. except OSError as ex :
  233. if ex.errno != 17 :
  234. #ignore directory already exists error
  235. self.__raise('directory creation failed :: '+ex.strerror)
  236. def _sys_readf(self,path) :
  237. f = open('./users/'+self._username+'/'+path,'r')
  238. contents = f.read()
  239. f.close()
  240. return contents
  241. def _sys_writef(self,path,content,append=True) :
  242. if append :
  243. f = open('./users/'+self._username+'/'+path,'a')
  244. else :
  245. f = open('./users/'+self._username+'/'+path,'w')
  246. f.write(content)
  247. f.close()