Embedding Rapydscript

84 views
Skip to first unread message

Val K

unread,
Feb 7, 2016, 6:20:12 PM2/7/16
to web2py-users
Hi!
Coding Python with switching to coding JS - really painful.
So, I tried Rapydscript (RS) and I like it very much! Unfortunately,  I couldn't find how  to automate compilation RS-files to pure JS.
Here is my solution to embed RS to web2py
  1. Download and install Node.js to anywhere (RS-compiler works on it, i.e. it's required only for developing, it won't be a dependence of your web2py project)
  2. Download and unpack Rapydscript  to anywhere. Or you can done it using GIT, as suggested in the manual (it's version 0.3.9 at the moment and it works w/o GIT-install)
  3. Code a little module to automate compilation your RS-files to pure JS and place it in your_w2p_app/modUles dir, here is my one
import sys
import os
from stat import ST_MTIME
import json as js
import subprocess as subp
import portalocker

get_fname = lambda full_name: os.path.split(full_name)[1]

def change_ext(f_name, ext):
    return os.path.splitext(f_name)[0] + ext

def clear_file(f):
    f.seek(0)
    f.truncate()

class  _log(object): # used to track changes to avoid parallel and extra recompilation 

    def __init__(self, f_name):
        self.f_name=f_name
        self.error={}
        self.f_obj=None
        self.log = None

    def open_read(self):
        f_name=self.f_name
        try:
            if os.path.isfile(f_name):  
                md='r+'
            else:
                md='w'
            f = open(f_name, md)
            self.f_obj = f
            portalocker.lock(f, portalocker.LOCK_EX )
            if md=='r+':
                tmp= f.read()
                self.log = (tmp and js.loads(tmp)) or {}
            else:
                self.log = {}
        except :
             self.error['open_read'] = sys.exc_info()[0]
        return  self.error['open_read'] is None

    def save(self):
        if not self.f_obj:
            return
        try:
            clear_file(self.f_obj)
            self.f_obj.write(js.dumps(self.log))
            self.f_obj.close()
        except :
             self.error['save'] = sys.exc_info()[0]

    def set_compile_time(self, pyj_fname):
        if self.log is None:
            return
        f_stat = os.stat(pyj_fname)
        self.log[ get_fname(pyj_fname) ] = f_stat[ST_MTIME]

    def is_up_to_date(self, pyj_fname):
        if self.log is None:
            return False
        f_stat = os.stat(pyj_fname)
        return self.log[ get_fname(pyj_fname) ] == f_stat[ST_MTIME]


class rs_compiler(object):
    def __init__(self, path_to_nodejs, path_to_RS):
        self.node = path_to_nodejs
        self.rs_engine = path_to_RS
        self.last_ret_code = None
        self.last_rslts = None

    def _rs_compile(self, src, dst, log, force_compile=False):
        ret = None
        if force_compile or not log.is_up_to_date(src) or not os.path.isfile(dst):
            ret = subp.call( [self.node, self.rs_engine, src, '-p', '-o', dst])
            if ret==0:
                 log.set_compile_time(src)
        self.last_ret_code = ret
        return ret

    def rs_compile_dir(self, src, dst, force_compile=False):

        log = _log(os.path.join(dst, 'compile_inf.json' ))   # 'compile_inf.json'  - contains dict  { 'file_1.pyj' : ST_MTIME,  'file_2.pyj' : ST_MTIME ...  }  
                                                                                                                        #  it 
will be created/overwrited in the same dir as JS-files
        if not log.open_read():
              return {};

        joinp = os.path.join
        all_dir = os.listdir(src)
        src_files = [f for f in all_dir if os.path.isfile(joinp(src, f)) and f[-4:]=='.pyj']
        ret={}
        for f in src_files:
            src_f =  joinp(src, f)
            dst_f =  joinp(dst, change_ext(f, '.js'))
            self._rs_compile(src_f, dst_f, log,  force_compile=force_compile)
            rslt=dict(
                    ret_code = self.last_ret_code,
                    open_read = log.error.get('open_read', None),
                    log_save_err = log.error.get('save', None),
                    )
            ret[f] = rslt
        self.last_rslts=ret
        log.save()
        return ret



def main():
   pass

if  __name__ == '__main__':
    main()



Now, to automate compilation you can place some code like below in db.py (or in another model-file)

...
import rs_compiler

def compile_pyj():
    node='path_to/nodejs/node.exe'
    rs_engine = 'path_to/RapydScript-master/bin/rapydscript'
    rs = rs_compiler.rs_compiler(node, rs_engine)
    src = os.path.join(request.folder, 'private') # I place my RS-files in 'w2p_app/private' dir  - you can choose another one 
    dst = os.path.join(request.folder, 'static/js') 

    print rs.rs_compile_dir(src, dst) # compile each src/file_name.pyj   to  dst/file_name.js  - overwriting JS-files without any warning!

compile_pyj()


   






Michele Comitini

unread,
Feb 8, 2016, 3:50:08 AM2/8/16
to web...@googlegroups.com
Very nice.
Did you evaluate running compile_pyjs() as a web2py's scheduler task?
It could simplify your code and make it more resilient.


--
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
---
You received this message because you are subscribed to the Google Groups "web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Val K

unread,
Feb 8, 2016, 3:18:08 PM2/8/16
to web2py-users
I think that scheduler is designed for asynchronous tasks, but I want to see changes effect immediately after edit/save pyj-file(s) and update web page. So, all pyj-files must be checked for out-of-date and recompiled (if there is anyone) per request call. I believe that cool IDEs allow doing this  w/o extra coding, but I use Pyscripter only. 
Reply all
Reply to author
Forward
0 new messages