from __future__ import absolute_import
from __future__ import print_function
from os.path import join, basename, abspath, split, isfile, isdir
from optparse import OptionParser
from six.moves import cStringIO
from svgui.pyjs import pyjs
usage: %prog [options] <application module name or path>
This is the command line builder for the pyjamas project, which can
be used to build Ajax applications from Python.
For more information, see the website at http://pyjs.org/
# GWT1.2 Impl | GWT1.2 Output | Pyjamas 0.2 Platform | Pyjamas 0.2 Output
# -------------+-----------------------+----------------------+----------------------
# Opera | opera | Opera | opera
# Safari | safari | Safari | safari
# -- | gecko1_8 | Mozilla | mozilla
# -- | gecko | OldMoz | oldmoz
# Standard | all | (default code) | all
# Mozilla | gecko1_8, gecko | -- | --
# Old | safari, gecko, opera | -- | --
version = "%prog pyjamas version 2006-08-19"
# these names in lowercase need match the strings
# returned by "provider$user.agent" in order to be selected corretly
app_platforms = ['IE6', 'Opera', 'OldMoz', 'Safari', 'Mozilla']
# usually defaults to e.g. /usr/share/pyjamas
_data_dir = os.path.join(pyjs.prefix, "share/pyjamas")
# .cache.html files produces look like this
CACHE_HTML_PAT = re.compile(r'^[a-z]*.[0-9a-f]{32}\.cache\.html$')
# ok these are the three "default" library directories, containing
# the builtins (str, List, Dict, ord, round, len, range etc.)
# the main pyjamas libraries (pyjamas.ui, pyjamas.Window etc.)
# and the contributed addons
for p in ["library/builtins",
p = os.path.join(_data_dir, p)
def read_boilerplate(data_dir, filename):
return open(join(data_dir, "builder/boilerplate", filename)).read()
def copy_boilerplate(data_dir, filename, output_dir):
filename = join(data_dir, "builder/boilerplate", filename)
shutil.copy(filename, output_dir)
# taken and modified from python2.4
def copytree_exists(src, dst, symlinks=False):
if not os.path.exists(src):
if name.startswith('CVS'):
if name.startswith('.git'):
if name.startswith('.svn'):
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
copytree_exists(srcname, dstname, symlinks)
shutil.copy2(srcname, dstname)
except (IOError, os.error) as why:
errors.append((srcname, dstname, why))
def check_html_file(source_file, dest_path):
""" Checks if a base HTML-file is available in the PyJamas
If the HTML-file isn't available, it will be created.
If a CSS-file with the same name is available
in the output directory, a reference to this CSS-file
If no CSS-file is found, this function will look for a special
CSS-file in the output directory, with the name
"pyjamas_default.css", and if found it will be referenced
in the generated HTML-file.
[thank you to stef mientki for contributing this function]
<!-- auto-generated html - you should consider editing and
adapting this to suit your requirements
<meta name="pygwt:module" content="%(modulename)s">
<script language="javascript" src="pygwt.js"></script>
filename = os.path.split(source_file)[1]
mod_name = os.path.splitext(filename)[0]
file_name = os.path.join(dest_path, mod_name + '.html')
# if html file in output directory exists, leave it alone.
if os.path.exists(file_name):
if os.path.exists(os.path.join(dest_path, mod_name + '.css')):
css = "<link rel='stylesheet' href='" + mod_name + ".css'>"
elif os.path.exists(os.path.join(dest_path, 'pyjamas_default.css')):
css = "<link rel='stylesheet' href='pyjamas_default.css'>"
title = 'PyJamas Auto-Generated HTML file ' + mod_name
base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css}
fh = open(file_name, 'w')
def build(app_name, output, js_includes=(), debug=False, dynamic=0,
data_dir=None, cache_buster=False, optimize=False):
# make sure the output directory is always created in the current working
# directory or at the place given if it is an absolute path.
output = os.path.abspath(output)
msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals()
msg += " with debugging statements"
# check the output directory
if os.path.exists(output) and not os.path.isdir(output):
print("Output destination %s exists and is not a directory" % output, file=sys.stderr)
if not os.path.isdir(output):
print("Creating output directory")
print("Exception creating output directory %s: %s" % (output, e), file=sys.stderr)
pub_dir = join(p, 'public')
print("Copying: public directory of library %r" % p)
copytree_exists(pub_dir, output)
# AppName.html - can be in current or public directory
html_input_filename = app_name + ".html"
html_output_filename = join(output, basename(html_input_filename))
if os.path.isfile(html_input_filename):
if not os.path.isfile(html_output_filename) or \
os.path.getmtime(html_input_filename) > \
os.path.getmtime(html_output_filename):
shutil.copy(html_input_filename, html_output_filename)
print("Warning: Missing module HTML file %s" % html_input_filename, file=sys.stderr)
print("Copying: %(html_input_filename)s" % locals())
if check_html_file(html_input_filename, output):
print("Warning: Module HTML file %s has been auto-generated" % html_input_filename, file=sys.stderr)
print("Copying: pygwt.js")
pygwt_js_template = read_boilerplate(data_dir, "pygwt.js")
pygwt_js_output = open(join(output, "pygwt.js"), "w")
print(pygwt_js_template, file=pygwt_js_output)
print("Copying: Images and History")
copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output)
copy_boilerplate(data_dir, "corner_dialog_topright_black.png", output)
copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output)
copy_boilerplate(data_dir, "corner_dialog_bottomleft_black.png", output)
copy_boilerplate(data_dir, "corner_dialog_edge_black.png", output)
copy_boilerplate(data_dir, "corner_dialog_topleft.png", output)
copy_boilerplate(data_dir, "corner_dialog_topright.png", output)
copy_boilerplate(data_dir, "corner_dialog_bottomright.png", output)
copy_boilerplate(data_dir, "corner_dialog_bottomleft.png", output)
copy_boilerplate(data_dir, "corner_dialog_edge.png", output)
copy_boilerplate(data_dir, "tree_closed.gif", output)
copy_boilerplate(data_dir, "tree_open.gif", output)
copy_boilerplate(data_dir, "tree_white.gif", output)
copy_boilerplate(data_dir, "history.html", output)
app_files = generateAppFiles(data_dir, js_includes, app_name, debug,
output, dynamic, cache_buster, optimize)
print("Creating: %(app_name)s.nocache.html" % locals())
home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html")
home_nocache_html_output = open(join(output, app_name + ".nocache.html"),
# the selector templ is added to the selectScript function
select_tmpl = """O(["true","%s"],"%s");"""
script_selectors = cStringIO()
for platform, file_prefix in app_files:
print(select_tmpl % (platform, file_prefix), file=script_selectors)
home_nocache_html_template % dict(
script_selectors=script_selectors.getvalue(),
), file=home_nocache_html_output)
home_nocache_html_output.close()
print("Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals())
def generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic,
all_cache_html_template = read_boilerplate(data_dir, "all.cache.html")
mod_cache_html_template = read_boilerplate(data_dir, "mod.cache.html")
# clean out the old ones first
for name in os.listdir(output):
if CACHE_HTML_PAT.match(name):
print("Deleting existing app file %s" % p)
parser = pyjs.PlatformParser("platform")
scripts = ['<script type="text/javascript" src="%s"></script>' %
script for script in js_includes]
app_body = '\n'.join(scripts)
# First, generate all the code.
# Second, (dynamic only), post-analyse the places where modules
# Third, write everything out.
for platform in app_platforms:
early_app_libs[platform] = ''
app_modnames[platform] = {}
# Application.Platform.cache.html
parser.setPlatform(platform)
app_translator = pyjs.AppTranslator(
parser=parser, dynamic=dynamic, optimize=optimize)
early_app_libs[platform], appcode = \
app_translator.translate(None, is_app=False,
library_modules=['dynamicajax.js',
pover[platform].update(app_translator.overrides.items())
for mname, name in app_translator.overrides.items():
pd = overrides.setdefault(mname, {})
# mod_code[platform][app_name] = appcode
# platform.Module.cache.js
modules_done = ['pyjslib', 'sys', '_pyjs.js']
# modules_to_do = [app_name] + app_translator.library_modules
modules_to_do = [app_name] + app_translator.library_modules
deps = map(pyjs.strip_py, modules_to_do)
sublist = add_subdeps(dependencies, d)
# dependencies[app_name] = deps
modules[platform] = modules_done + modules_to_do
# print "modules to do", modules_to_do
mod_name = pyjs.strip_py(mn)
if mod_name in modules_done:
modules_done.append(mod_name)
mod_cache_name = "%s.%s.cache.js" % (platform.lower(), mod_name)
parser.setPlatform(platform)
mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize)
mod_libs[platform][mod_name], mod_code[platform][mod_name] = \
mod_translator.translate(mod_name,
pover[platform].update(mod_translator.overrides.items())
for mname, name in mod_translator.overrides.items():
pd = overrides.setdefault(mname, {})
mods = mod_translator.library_modules
modules[platform] += mods
deps = map(pyjs.strip_py, mods)
# print "modname preadd:", mod_name, deps
sublist = add_subdeps(dependencies, d)
modules_to_do += add_subdeps(dependencies, mod_name)
# print "modname:", mod_name, deps
# print "modname:", mod_name, deps
dependencies[mod_name] = deps
# work out the dependency ordering of the modules
mod_levels[platform] = make_deps(None, dependencies, modules_done)
# now write everything out
for platform in app_platforms:
early_app_libs_ = early_app_libs[platform]
app_libs_ = app_libs[platform]
app_code_ = app_code[platform]
# modules_ = filter_mods(app_name, modules[platform])
mods = flattenlist(mod_levels[platform])
modules_ = filter_mods(None, mods)
for mod_name in modules_:
mod_code_ = mod_code[platform][mod_name]
mod_name = pyjs.strip_py(mod_name)
override_name = "%s.%s" % (platform.lower(), mod_name)
if override_name in pover[platform]:
mod_cache_name = "%s.cache.js" % (override_name)
mod_cache_name = "%s.cache.js" % (mod_name)
print("Creating: " + mod_cache_name)
modlevels = make_deps(None, dependencies, dependencies[mod_name])
mnames = map(lambda x: "'%s'" % x, md)
mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(modnames)
overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items())
overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames)
mod_cache_html_output = open(join(output, mod_cache_name), "w")
mod_cache_html_output = cStringIO()
print(mod_cache_html_template % dict(
mod_libs=mod_libs[platform][mod_name],
), file=mod_cache_html_output)
mod_cache_html_output.close()
mod_cache_html_output.seek(0)
app_libs_ += mod_cache_html_output.read()
# write out the dependency ordering of the modules
for md in mod_levels[platform]:
mnames = map(lambda x: "'%s'" % x, md)
mnames = "new pyjslib.List([\n\t\t\t%s])" % ',\n\t\t\t'.join(mnames)
app_modnames.append(mnames)
app_modnames = "new pyjslib.List([\n\t\t%s\n\t])" % ',\n\t\t'.join(app_modnames)
overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items())
overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames)
# print "platform names", platform, overnames
# now write app.allcache including dependency-ordered list of
file_contents = all_cache_html_template % dict(
early_app_libs=early_app_libs_,
platform=platform.lower(),
app_modnames=app_modnames,
digest = md5.new(file_contents).hexdigest()
file_name = "%s.%s.%s" % (platform.lower(), app_name, digest)
file_name = "%s.%s" % (platform.lower(), app_name)
file_name += ".cache.html"
out_path = join(output, file_name)
out_file = open(out_path, 'w')
out_file.write(file_contents)
app_files.append((platform.lower(), file_name))
print("Created app file %s:%s: %s" % (
app_name, platform, out_path))
creates sub-dependencies e.g. pyjamas.ui.Widget
creates pyjamas.ui.Widget, pyjamas.ui and pyjamas.
for i in range(0, len(m)):
d.append('.'.join(m[:i+1]))
def add_subdeps(deps, mod_name):
# print "subdeps", mod_name, sd
for i in range(0, len(sd)-1):
makes unique and preserves list order
def filter_mods(app_name, md):
md = filter(lambda x: not x.endswith('.js'), md)
md = map(pyjs.strip_py, md)
def filter_deps(app_name, deps):
for (k, l) in deps.items():
def has_nodeps(mod, deps):
if mod not in deps or not deps[mod]:
def nodeps_list(mod_list, deps):
if has_nodeps(mod, deps):
# this function takes a dictionary of dependent modules and
# creates a list of lists. the first list will be modules
# that have no dependencies; the second list will be those
# modules that have the first list as dependencies; the
# third will be those modules that have the first and second...
def make_deps(app_name, deps, mod_list):
print("Calculating Dependencies ...")
mod_list = filter_mods(app_name, mod_list)
deps = filter_deps(app_name, deps)
for m, dl in deps.items():
raise Exception('Circular Imports found: \n%s %s -> %s %s'
# raise Exception('Could not calculate dependencies: \n%s' % deps)
# print "modlist", mod_list
nodeps = nodeps_list(mod_list, deps)
mod_list = filter(lambda x: x not in nodeps, mod_list)
depslist = filter(lambda x: x not in nodeps, depslist)
# print "newdeps", newdeps
ordered_deps.append(nodeps)
ordered_deps.append(mod_list) # last dependencies - usually the app(s)
parser = OptionParser(usage=usage, version=version)
help="directory to which the webapp should be written"
help="javascripts to load into the same frame as the rest of the script"
help="additional paths appended to PYJSPATH"
help="path for data directory"
help="Split output into separate dynamically-loaded modules (experimental)"
help="platforms to build for, comma-separated"
help="Optimize generated code (removes all print statements)",
help="Enable browser cache-busting (MD5 hash added to output filenames)"
parser.set_defaults(output="output", js_includes=[], library_dirs=[],
platforms=(','.join(app_platforms)),
data_dir=os.path.join(sys.prefix, "share/pyjamas"),
(options, args) = parser.parse_args()
parser.error("incorrect number of arguments")
data_dir = abspath(options.data_dir)
if app_path.endswith('.py'):
app_path = abspath(app_path)
parser.error("Application file not found %r" % app_path)
app_path, app_name = split(app_path)
pyjs.path.append(app_path)
elif os.path.sep in app_path:
parser.error("Not a valid module declaration %r" % app_path)
for d in options.library_dirs:
pyjs.path.append(abspath(d))
app_platforms = options.platforms.split(',')
# this is mostly for getting boilerplate stuff
data_dir = os.path.abspath(options.data_dir)
build(app_name, options.output, options.js_includes,
options.debug, options.dynamic and 1 or 0, data_dir,
options.cache_buster, options.optimize)
if __name__ == "__main__":