PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/ompc/__init__.py

https://bitbucket.org/juricap/ompc/
Python | 353 lines | 336 code | 7 blank | 10 comment | 7 complexity | fa9e399d071deceab986db58555dca78 MD5 | raw file
Possible License(s): LGPL-2.1
  1. import sys, os
  2. sys.path += [os.path.abspath('.'), os.path.abspath('..')]
  3. from ompcply import translate_to_str
  4. __all__ = ['mfunction', '_get_narginout', 'compile', 'build_return']
  5. def mfunction_simple(names):
  6. def dec(func):
  7. from doc_comments import get_comment_doc
  8. doc = get_comment_doc(func)
  9. def func_new(*args):
  10. return func(*args)
  11. func_new.__doc__ = doc
  12. func_new.__name__ = func.__name__
  13. #func_new.__module__ = func.__module__
  14. return func_new
  15. return dec
  16. from byteplay import Code, LOAD_GLOBAL, CALL_FUNCTION, \
  17. UNPACK_SEQUENCE, STORE_FAST, LOAD_FAST, \
  18. LOAD_CONST, BUILD_LIST, BINARY_SUBTRACT, \
  19. BINARY_MULTIPLY, BUILD_TUPLE, SLICE_2, \
  20. RETURN_VALUE
  21. def _get_narginout(nargout_default=1):
  22. """Return how many values the caller is expecting.
  23. `nargout_default` is the value returned if no return value is expected.
  24. By default this is 1.
  25. """
  26. import sys, dis
  27. f = sys._getframe()
  28. # step into the function that called us
  29. fb = f.f_back
  30. innames = fb.f_code.co_varnames[:fb.f_code.co_argcount]
  31. nargin = len([ x for x in innames if fb.f_locals.get(x, None) is not None ])
  32. vargin = fb.f_locals.get('varargin', None)
  33. if vargin is not None:
  34. nargin += len(vargin)
  35. # nargout is one frame back
  36. f = f.f_back.f_back
  37. c = f.f_code
  38. i = f.f_lasti
  39. bytecode = c.co_code
  40. instruction = ord(bytecode[i+3])
  41. if instruction == dis.opmap['UNPACK_SEQUENCE']:
  42. howmany = ord(bytecode[i+4])
  43. return nargin, howmany
  44. elif instruction == dis.opmap['POP_TOP']:
  45. # MATLAB assumes at least 1 value
  46. return nargin, nargout_default
  47. return nargin, 1
  48. class mfunction:
  49. """Decorator that allows emulation of MATLAB's treatement of functions.
  50. """
  51. def __init__(self, retstring):
  52. self._retvals = tuple( x.strip() for x in retstring.split(",") )
  53. def __call__(self, func):
  54. from byteplay import Code
  55. self._func = func
  56. self._c = Code.from_code(self._func.func_code)
  57. c = self._c
  58. # all return values must be initialized, for return (retvals)[:nargout]
  59. # not necessary anymore, the parser has to take care of this
  60. # check for maximum nargout, insert nargin nargout code
  61. self.__add_narg()
  62. # initialize novel variables, FIXME
  63. self.__add_return()
  64. self._func.func_code = self._c.to_code()
  65. return self._func
  66. def __add_narg(self):
  67. """Add nargin and nargout variables that emulate their behavior in
  68. MATLAB.
  69. """
  70. c = self._c
  71. pre_code = [(LOAD_GLOBAL,'_get_narginout'),
  72. (CALL_FUNCTION,0),
  73. (UNPACK_SEQUENCE, 2),
  74. (STORE_FAST,'nargin'),
  75. (STORE_FAST,'nargout')]
  76. # adjust the function preamble
  77. c.code[:0] = pre_code
  78. # replace LOAD_GLOBAL with LOAD_FAST for
  79. for i, x in enumerate(c.code):
  80. if x[0] == LOAD_GLOBAL and \
  81. (x[1] == 'nargout' or x[1] == 'nargin'):
  82. c.code[i] = (LOAD_FAST,x[1])
  83. def __init_novel(self, names):
  84. """MATLAB allows assignment to variables that are not initialized.
  85. For example 'a(2) = 1;' would result in 'a = [0 1];'.
  86. This function takes a list of names as parameters and initializes
  87. all of them to the default empty marray().
  88. """
  89. return
  90. def __add_return(self):
  91. # at first remove the original "return None"
  92. c = self._c
  93. c.code = c.code[:-2]
  94. #for i, x in enumerate(c.code):
  95. # if x[0] == LOAD_CONST and x[1] is None:
  96. # c.code[i] = (LOAD_FAST,x[1])
  97. postfix = []
  98. # postfix.extend([(BUILD_TUPLE, len(self._retvals)),
  99. # (LOAD_FAST, 'nargout'),
  100. # (SLICE_2, None),
  101. # (RETURN_VALUE, None)])
  102. postfix.extend([(LOAD_GLOBAL, 'build_return'),
  103. (LOAD_FAST, 'nargout')])
  104. for name in self._retvals:
  105. postfix += [(LOAD_FAST, name)]
  106. postfix.extend([(CALL_FUNCTION, len(self._retvals)+1),
  107. (RETURN_VALUE, None)])
  108. self._c.code.extend(postfix)
  109. def get_pym_string(source):
  110. """get_pym_string(source) -> python_code_string
  111. Read MATLAB string in and return string that contains equivalent
  112. Python compatible code that relies on features of OMPClib's mobject.
  113. This string can be further passed to Python's built-in compile method
  114. with argument mode='exec' and executed by Python's interpreter.
  115. """
  116. return translate_to_str(source)
  117. def get_pym_string_single(source):
  118. """get_pym_string_single(source) -> python_code_string
  119. Translate MATLAB single statement string into a functionally equivalent
  120. Python compatible statement that relies on features of OMPClib's mobject.
  121. This string can be further passed to Python's built-in compile method
  122. with argument mode='single' and executed by Python's interpreter.
  123. """
  124. return translate_to_str(source)
  125. def get_pym_string_eval(source):
  126. """get_pym_string_eval(source) -> python_code_string
  127. Translate MATLAB single statement string inot a functionally equivalent
  128. Python compatible statement that relies on features of OMPClib's mobject.
  129. This string can be further passed to Python's built-in compile method
  130. with argument mode='single' and executed by Python's interpreter.
  131. """
  132. return translate_to_str(source)
  133. PYM_TEMPLATE = """\
  134. # This file was automatically translated by OMPC (http://ompc.juricap.com)
  135. from ompc import *
  136. %(pym_string)s
  137. """
  138. def compile(source, filename, mode, flags=0, dont_inherit=0):
  139. """compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
  140. Compile the source string (a MATLAB module, statement or expression)
  141. into a code object that can be executed by the exec statement or eval().
  142. The filename will be used for run-time error messages.
  143. The mode must be 'exec' to compile a module, 'single' to compile a
  144. single (interactive) statement, or 'eval' to compile an expression.
  145. The flags and dont_inherit arguments are ignored by OMPC at the moment
  146. and are passed to the built-in compile method of Python."""
  147. import __builtin__
  148. # get the source code
  149. if mode == 'exec':
  150. pycode_str = get_pym_string(source)
  151. elif mode == 'single':
  152. pycode_str = get_pym_string_single(source)
  153. elif mode == 'eval':
  154. pycode_str = get_pym_string_eval(source)
  155. else:
  156. raise ValueError("compile() arg 3 must be 'exec' or 'eval' or 'single'")
  157. pycode_str = PYM_TEMPLATE%{"pym_string": pycode_str}
  158. pym = filename[:-1]+'pym'
  159. open(pym, 'wb').write(pycode_str)
  160. co = __builtin__.compile(pycode_str, pym, mode, flags, dont_inherit)
  161. # return the code object
  162. return co
  163. import ihooks, imp, os
  164. from os.path import join, exists, isdir, splitext, split
  165. from glob import glob
  166. from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE
  167. import m_compile
  168. M_COMPILABLE = ['.m']
  169. def get_mfiles(path):
  170. """Function returns all MATLAB imoportable files in the 'path' folder.
  171. """
  172. return glob(join(path,'*.m'))
  173. class MFileHooks(ihooks.Hooks):
  174. def load_source(self, name, filename, file=None):
  175. """Compile .m files."""
  176. if splitext(filename)[1] not in M_COMPILABLE:
  177. return ihooks.Hooks.load_source(self, name, filename, file)
  178. if file is not None:
  179. file.close()
  180. mfname = splitext(filename)[0]
  181. cfile = mfname + '.py' + (__debug__ and 'c' or 'o')
  182. m_compile.compile(filename, cfile) # m-file compilation
  183. cfile = open(cfile, 'rb')
  184. try:
  185. # print name, filename, cfile
  186. print 'Importing m-file: "%s"'%filename
  187. module = self.load_compiled(name, filename, cfile)
  188. #return module
  189. # Python at this point returns a module, we can actually return the
  190. # single function that is in it
  191. return getattr(module, name)
  192. finally:
  193. cfile.close()
  194. class MFileLoader(ihooks.ModuleLoader):
  195. """A hook to include .m files into importables and translate them
  196. on-demand to .pym and .pyc files."""
  197. def load_module(self, name, stuff):
  198. """Special-case package directory imports."""
  199. file, filename, (suff, mode, type) = stuff
  200. path = None
  201. module = None
  202. if type == imp.PKG_DIRECTORY:
  203. # try first importing it as Python PKG_DIRECTORY
  204. stuff = self.find_module_in_dir("__init__", filename, 0)
  205. mfiles = get_mfiles(filename)
  206. if stuff is not None:
  207. file = stuff[0] # package/__init__.py
  208. path = [filename]
  209. elif mfiles:
  210. # this is a directory with mfiles
  211. # create a new module and fill it with mfunctions
  212. module = imp.new_module(filename)
  213. for x in mfiles:
  214. #setattr(module, x, load_mfunction(filename, x))
  215. # all mfiles are considered source, the m2py compilation
  216. # stuff is in the load_source function
  217. mfunc_name = splitext(split(x)[1])[0]
  218. mfile = ihooks.ModuleLoader.load_module(self, mfunc_name,
  219. (open(x, 'U'), x, ('', '', PY_SOURCE)))
  220. setattr(module, mfunc_name, getattr(mfile, mfunc_name, None))
  221. if module is None:
  222. try: # let superclass handle the rest
  223. module = ihooks.ModuleLoader.load_module(self, name, stuff)
  224. finally:
  225. if file:
  226. file.close()
  227. if path:
  228. module.__path__ = path # necessary for pkg.module imports
  229. return module
  230. def match_mfile(self, name, dir):
  231. """The function should look for all possible files in the 'dir' that
  232. MATLAB would allow to call. This includes
  233. - .m, .dll, .mex ???? FIXME
  234. If the name is a directory the directory should be searched for all
  235. these files. If there are any importable files they should be loaded
  236. and presented to the importer behind a single module called 'name'.
  237. """
  238. path = join(dir, name)
  239. if exists(path) and isdir(path):
  240. # are there any mfiles
  241. mfiles = get_mfiles(path)
  242. if mfiles:
  243. # we have to load the mfiles ourselves create a module
  244. # with all the mfiles wrapped
  245. #from warnings import warn
  246. #warn("Importing directories not implemented yet!")
  247. return path
  248. elif exists(path+'.m'):
  249. return path+'.m'
  250. def find_module_in_dir(self, name, dir, allow_packages=1):
  251. if dir is None:
  252. # no documentation, dir=None maybe query the cache ???, TODO
  253. return ihooks.ModuleLoader.find_module_in_dir(
  254. self, name, dir, allow_packages)
  255. else:
  256. if allow_packages:
  257. resolved_path = self.match_mfile(name, dir)
  258. if resolved_path is not None:
  259. if os.path.isdir(resolved_path):
  260. return (None, resolved_path, ('', '', PKG_DIRECTORY))
  261. else:
  262. return (open(resolved_path,'rb'), resolved_path, ('', '', PY_SOURCE))
  263. ## PY_COMPILED)) load_compiled would be called
  264. return ihooks.ModuleLoader.find_module_in_dir(
  265. self, name, dir, allow_packages)
  266. def install():
  267. """Install the import hook"""
  268. ihooks.install(ihooks.ModuleImporter(MFileLoader(MFileHooks())))
  269. # populate the __main__ namespace with OMPCbase functions
  270. import __main__
  271. if not hasattr(__main__, '__OMPC'):
  272. import ompclib, warnings
  273. _ns = __main__
  274. # IPython needs special treatment
  275. if 'IPython' in sys.modules:
  276. if hasattr(__main__, '__IP'):
  277. _ns = __main__.__IP.user_ns
  278. else:
  279. import __builtin__
  280. if hasattr(__builtin__, '__IPYTHON__'):
  281. _ns = __builtin__.__IPYTHON__.user_ns
  282. else:
  283. warnings.warn("IPython seems to be running, but I can't find it! Populating the __main__ namespace.", UserWarning)
  284. _ns = __main__.__dict__
  285. else:
  286. _ns = __main__.__dict__
  287. for x in ompclib.__ompc_all__:
  288. _ns[x] = getattr(ompclib, x)
  289. from ompclib import *
  290. __all__ += ompclib.__ompc_all__
  291. install()
  292. __main__.__OMPC = True
  293. def build_return(nargout, *args):
  294. from ompclib import _marray, _dtype, _size
  295. ret = []
  296. for x in args[:nargout]:
  297. if isinstance(x, _marray): ret += [ x ]
  298. else: ret += [ _marray(_dtype(x), _size(x), x) ]
  299. if len(ret) == 1:
  300. ret = ret[0]
  301. return ret
  302. if __name__ == "__main__":
  303. pth = '../examples/mfiles/'
  304. src = file(pth+'Uncertainty_function.m','U').read()
  305. print get_pym_string(src)