# 'Make' support for Online Python Tutor## Copyright (C) 2013 Peter Robinson (pjr@itee.uq.edu.au)### Permission is hereby granted, free of charge, to any person obtaining a# copy of this software and associated documentation files (the# "Software"), to deal in the Software without restriction, including# without limitation the rights to use, copy, modify, merge, publish,# distribute, sublicense, and/or sell copies of the Software, and to# permit persons to whom the Software is furnished to do so, subject to# the following conditions:## The above copyright notice and this permission notice shall be included# in all copies or substantial portions of the Software.## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."""This program is a 'make' program for embedding traces in web pages.The process is driven by a .json file (the 'Makefile')Assuming the file viz_makefile.json is in the cwd which will typicallybe where the web pages and python code to be visualized reside,and $OPT is the path to the installation of OPT(including make_visualizations.py) thenpython $OPT/make_visualizations.py viz_makefile.jsonwill generate all the .js traces and update the web pages as specifiedin viz_makefile.json.python $OPT/make_visualizations.py viz_makefile.json eg1.htmlwill do the same thing but only for the given page(s).Here is an example of the 'Makefile' viz_makefile.json----------------------------{"visualizer_url":"http://path_to_local_visualizer/visualize.html","default_viz_config":{"embeddedMode": true,"verticalStack": true},"eg1.html":[# hash to end of line is a comment"js/t1.js",{"python_scripts/t1a.py": {}"python_scripts/t1b.py": {"embeddedMode": false}}]"eg2.html":["js/t2.js",{# verticalStack is removed from the params below"python_scripts/t2.py": {"verticalStack": ""}}]}----------------------------For each .html file the first argument is the .js file where the traces will goand the second argument is a dictionary whose keys are the python scriptsto be used to generate the traces.If "visualizer_url" is not supplied it defaults to"http://pythontutor.com/visualize.html"If "default_viz_config" is not supplied it defaults to{'heightChangeCallback': 'redrawAllVisualizerArrows','editCodeBaseURL': viz_url}where viz_url is the value associated with the "visualizer_url" key.Note: for this version heightChangeCallback is hard-wired to beredrawAllVisualizerArrows.The dictionary associated with a python script overrides the"default_viz_config" dictionary.In other words, if all visualizations behave the same way then the dictionaryassociated with a python script can be set to {} and so will end up being the(supplied) default."""import subprocessimport osimport reimport jsonfrom optparse import OptionParser# The OPT program for generating tracesGEN_JSON = "generate_json_trace.py"# The command used to generate traces {0} will be the full path to GEN_JSON# !! WARNING !! python below might need to be modified to point at the# correct versionCOMMAND = "python {0} --create_jsvar={1}Trace {2}"# The possible keys for configuring the visualization and their types# At the moment changing callbacks are not supported -# 'heightChangeCallback' is hard-wired inVIZ_CONFIG_TYPES = {#'heightChangeCallback': unicode,#'updateOutputCallback': unicode,#'executeCodeWithRawInputFunc': unicode,'embeddedMode': bool,'startingInstruction': int,'verticalStack': bool,'jumpToEnd': bool,'codeDivWidth': int,'codeDivHeight': int,'hideOutput' : bool,'editCodeBaseURL' : unicode,'allowEditAnnotations' : bool,'disableHeapNesting' : bool,'drawParentPointer' : bool,'textualMemoryLabels' : bool,'showOnlyOutputs' : bool,}CALLBACK_PATTERN_REPL = [(r'"heightChangeCallback"\s*:\s*"([^"]*)"', r'"heightChangeCallback":1円'),(r'"dateOutputCallback"\s*:\s*"([^"]*)"', r'"OutputCallback":1円'),(r'"executeCodeWithRawInputFunc"\s*:\s*"([^"]*)"',r'"executeCodeWithRawInputFunc":1円')]# visualize.html in OPT homeDEFALUT_VIZ_URL = 'http://pythontutor.com/visualize.html'# The global default visualization configuration# Note: heightChangeCallback is hard-wired to be redrawAllVisualizerArrowsDEFAULT_VIZ_CONFIG = {'heightChangeCallback': 'redrawAllVisualizerArrows'}# The string used to generate js code for ExecutionVisualizerVIZ_VAR = \"""var {0}Visualizer =new ExecutionVisualizer('{0}Div', {0}Trace,{1});"""# The following is used to inject the dependencies and other info# into the web pages. If this info is already there then it will be replaced.# If your file already has this info in but does not have the# PY_TUTOR_END footer then manually remove this info.# This information is based on the example from 'http://pythontutor.com'PY_TUTOR_START = '<!-- dependencies for pytutor.js -->'PY_TUTOR_END = '<!-- end of dependencies for pytutor.js -->'PY_TUTOR_DEPEND = PY_TUTOR_START + \"""<script type="text/javascript" src="js/d3.v2.min.js"></script><script type="text/javascript" src="js/jquery-1.8.2.min.js"></script><script type="text/javascript" src="js/jquery.simplemodal.js"></script> <!-- for questions --><script type="text/javascript" src="js/jquery.ba-bbq.min.js"></script> <!-- for handling back button and URL hashes --><script type="text/javascript" src="js/jquery.jsPlumb-1.3.10-all-min.js "></script> <!-- for rendering SVG connectorsDO NOT UPGRADE ABOVE 1.3.10 OR ELSE BREAKAGE WILL OCCUR --><script type="text/javascript" src="js/jquery-ui-1.8.24.custom.min.js"></script> <!-- for sliders and other UI elements --><link type="text/css" href="css/ui-lightness/jquery-ui-1.8.24.custom.css" rel="stylesheet" /><link type="text/css" href="css/basic.css" rel="stylesheet" /><!-- Python Tutor frontend code and styles --><script type="text/javascript" src="js/pytutor.js"></script><link rel="stylesheet" href="css/pytutor.css"/><!-- Visualize Script --><script type="text/javascript" src="{0}"></script><!-- Cut and paste the following lines to the place where the visualizations are to be inserted{1}-->""" + PY_TUTOR_END# A regular expression used to remove the old infoPY_TUTOR_RE = PY_TUTOR_START + '.*' + PY_TUTOR_END# Used to inject a comment along with the dependency information so that# you can cut and paste into the appropriate place in to the page bodyDIV_TEXT = '<div id="{0}Div"></div>'# The ready function to go at the end ot the generated .js fileDOCUMENT_READY_TEXT = \"""$(document).ready(function() {{{0}function redrawAllVisualizerArrows() {{{1}}}$(window).resize(redrawAllVisualizerArrows);}});"""REDRAW_CONNECTORS_TEXT = "if ({0}Visualizer) {0}Visualizer.redrawConnectors();"COMMENT_RE = r'#.*$'def check_viz_config(parent, config):"""Check that config is a valid visualization configuration"""isOK = Trueif type(config) != dict:print "In {0}, {1} is not a dictionary".format(parent,config)return Falsefor key, value in config.iteritems():config_field_type = VIZ_CONFIG_TYPES.get(key)if config_field_type is None:print "Unknown visualizer key:", keyisOK = Falseelif value != '' and type(value) != config_field_type:print "The visualizer configure entry {0}:{1} has the wrong type".format(key,value)isOK = Falsereturn isOKdef check_html_build(html, html_build_info):"""Check that the build information for this html file is valid."""if len(html_build_info) != 2:print "The build list for {0} should have 2 entries".format(html)return Falseif type(html_build_info[0]) != unicode or \not html_build_info[0].endswith('.js'):print "The first argument of build list for {0} should be a .js file".format(html)if type(html_build_info[1]) != dict:print "The html build information must be a dictionary"return FalseisOK = Truefor key, value in html_build_info[1].iteritems():if type(key) != unicode or not key.endswith('.py'):print "The key {0} is not a .py file".format(key)isOK = FalseisOK = check_viz_config(html_build_info[1], value) and isOKreturn isOKdef check_build_info(build_info):"""Check that the json info is a valid 'Makefile'"""isOK = Trueif type(build_info) != dict:print "The build information must be a dictionary"return Falsefor key, value in build_info.iteritems():if key == 'default_viz_config':isOK = check_viz_config(build_info, value) and isOKelif key == 'visualizer_url':if type(value) != unicode:print 'The visualizer url must be a string'isOK = Falseelif key.endswith('.html'):isOK = check_html_build(key, value) and isOKelse:print 'Unknown key:', keyisOK = Falsereturn isOKdef update_dict(d, defaultd):"""Update d with key,value pairs in defaultd if the key is not in d.If the value is the empty string then that entry is removed fromthe dictionary"""for key, value in defaultd.iteritems():if key not in d:d[key] = valueelif value == '':d.pop(key)def get_build_info(json_file):"""Return the build information in json_file.Check if file is valid."""try:fp = open(json_file, 'rU')text = fp.read()fp.close()text = re.sub(COMMENT_RE, '', text, flags=re.M)build_info = json.loads(text)except Exception as e:print "Error in {0}:\n{1}".format(json_file, str(e))return Noneif not check_build_info(build_info):return None# if necessary add a value for "visualizer_url"if "visualizer_url" not in build_info:build_info["visualizer_url"] = DEFALUT_VIZ_URL# merge DEFAULT_VIZ_CONFIG with the supplied "default_viz_config"config = DEFAULT_VIZ_CONFIGconfig["editCodeBaseURL"] = build_info["visualizer_url"]config.update(build_info.get("default_viz_config", {}))build_info["default_viz_config"] = config# update all thefor key, value in build_info.iteritems():if key.endswith('.html'):for py_key, py_dict in value[1].iteritems():update_dict(py_dict, build_info.get("default_viz_config", {}))return build_infodef get_vizname_root(py_file):"""Return the name to be used as the prefix for Trace, Visualizer etc"""return os.path.basename(py_file).replace('.', '_')def run_command(gen_trace, py_file):""" Return the output of generate_json_trace.py on py_file"""command = COMMAND.format(gen_trace, get_vizname_root(py_file), py_file)return subprocess.check_output(command,bufsize=1,close_fds=True,shell=True)def make_viz(build_info, html_files):"""Build the visualizations specified in build_info and html_files"""abspath = os.path.abspath(__file__)dname = os.path.dirname(abspath)prog = os.path.join(dname, GEN_JSON)# if no html files are supplied 'make' all of themif not html_files:html_files = [h for h in build_info if h.endswith('.html')]for html in html_files:if html in build_info:make_html_viz(prog, html, build_info[html])else:print "{0} is not in the json file".format(html)def get_viz_config(conf):"""Get the configuration params for visualization."""text = json.dumps(conf)# The callbacks are strings in conf and need to be turned into# function relreences by removing the quotesfor patt, repl in CALLBACK_PATTERN_REPL:text = re.sub(patt, repl, text)return textdef make_html_viz(prog, html_file, html_info):"""'Make' the visualizations for html_file"""js_out, py_dict = html_info# all_traces is a string containing all the js trace datastructures# for all the supplied .py files for html_filetry:all_traces = '\n'.join(run_command(prog, py) for py in py_dict)except Exception as e:print str(e)return# all_viz is the string containing the js code for creating the# required instances of ExecutionVisualizer for all the .py files# the root of the .py file is used as the root names in ExecutionVisualizerall_viz = '\n'.join(VIZ_VAR.format(get_vizname_root(py),get_viz_config(pyd)) \for py,pyd in py_dict.iteritems())all_redraws = ' \n'.join(REDRAW_CONNECTORS_TEXT.format(get_vizname_root(py)) for py, pyd in py_dict.iteritems() if 'redrawAllVisualizerArrows' in pyd.values())ready_function = DOCUMENT_READY_TEXT.format(all_viz, all_redraws)# all_js is the string of all the require js codeall_js = all_traces + '\n' + ready_function + '\n'fd = open(js_out, 'w')fd.write(all_js)fd.close()# Update html_filetry:fd = open(html_file, 'rU')html_text = fd.read()fd.close()except Exception as e:print str(e)return# strip out the old OPT dependency infohtml_text = re.sub(PY_TUTOR_RE, '', html_text, flags=re.M | re.S)end_head_pos = html_text.find('\n</head>')if end_head_pos == -1:print "Could not find '\n</head>' in {0}".format(html_file)return# all_divs is the string of all div entries to embed in the html# this is added to the header comments for easy cut and pasting# to the correct location in the document.all_divs = '\n'.join(DIV_TEXT.format(get_vizname_root(py)) for py in py_dict)# add the updated dependency info just before </head>html_text = html_text[:end_head_pos] +\PY_TUTOR_DEPEND.format(js_out, all_divs) + \html_text[end_head_pos:]fd = open(html_file, 'w')fd.write(html_text)fd.close()def main(args):build_info = get_build_info(args[0])if build_info is None:print "Make aborted"else:make_viz(build_info, args[1:])if __name__ == '__main__':usage = "usage: %prog json_file [py_files]"parser = OptionParser(usage = usage)options,args = parser.parse_args()if len(args) == 0:print "Missing arguments - try python make_visualizations.py -h"else:main(args)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。