def url_for(directory, public=False, **kwargs): from appyter.context import get_env from appyter.util import safe_join, join_routes config = get_env() url = None if config['DEBUG']: try: from flask import url_for modified_directory = '.'.join( ('__main__', directory)) if directory == 'static' else directory url = url_for(modified_directory, **kwargs) except: pass # if url is None: filename = kwargs.get('filename', kwargs.get('path')) assert filename is not None url = safe_join(config['PREFIX'], directory, filename) # if public: try: from flask import request url = join_routes(request.url_root, url)[1:] except: pass # return url
def public_url(self): try: from flask import request return join_routes(request.base_url, self.value)[1:] except: from appyter.context import get_env config = get_env() return join_routes(config.get('PUBLIC_URL', 'file:///' + config.get('CWD')), self.value)[1:]
def serve(app_path, **kwargs): import os import sys import time import appyter import functools import logging logger = logging.getLogger(__name__) from subprocess import Popen from appyter.ext.fs import Filesystem from appyter.context import get_env, get_jinja2_env, find_blueprints, get_appyter_directory from appyter.util import join_routes from appyter.profiles.default.filters.url_for import url_for config = get_env(**kwargs) logger.info(kwargs) env = get_jinja2_env(config=config) with Filesystem('tmpfs://') as tmp_fs: logger.info(f"Working directory {tmp_fs.path()}") # logger.info(f"Pre-rendering pages...") with Filesystem(config['CWD']).open(config['IPYNB']) as fr: from appyter.parse.nb import nb_from_ipynb_io nbtemplate = nb_from_ipynb_io(fr) with tmp_fs.open('index.html', 'w') as fw: from appyter.render.form import render_form_from_nbtemplate fw.write(render_form_from_nbtemplate(env, nbtemplate)) with tmp_fs.open('index.json', 'w') as fw: import json from appyter.render.nbinspect import render_nbtemplate_json_from_nbtemplate json.dump(render_nbtemplate_json_from_nbtemplate(env, nbtemplate), fw) with tmp_fs.open('landing.html', 'w') as fw: env.get_template('landing.j2').stream(_nb=os.path.basename( config['IPYNB']), ).dump(fw) # logger.info(f"Generating production config...") with tmp_fs.open('supervisord.conf', 'w') as fw: env.get_template('production/supervisord.conf.j2').stream( _tmp_fs=tmp_fs, sys=sys, str=str).dump(fw) with tmp_fs.open('nginx.conf', 'w') as fw: env.get_template('production/nginx.conf.j2').stream( _tmp_fs=tmp_fs, os=os, s3_to_url=s3_to_url, get_appyter_directory=get_appyter_directory, find_blueprints=find_blueprints, ).dump(fw) logger.info( f"Starting production instance at http://{config['HOST']}:{config['PORT']}{config['PREFIX']} ..." ) with Popen( ['supervisord', '-n', '-c', tmp_fs.path('supervisord.conf')]) as proc: try: sys.exit(proc.wait()) except KeyboardInterrupt: proc.terminate() sys.exit(proc.wait())
def nbconstruct(cwd, ipynb, context, output, **kwargs): context = json.load(context) env = get_jinja2_env( config=get_env(cwd=cwd, ipynb=ipynb, mode='construct', **kwargs), context=context, ) nbtemplate = nb_from_ipynb_io(Filesystem(cwd).open(ipynb, 'r')) nb = render_nb_from_nbtemplate(env, nbtemplate) nb_to_ipynb_io(nb, output)
def serve(app_path, **kwargs): import os import asyncio import appyter from appyter.ext.asyncio.event_emitter import EventEmitter from appyter.ext.watchgod.watcher import GlobWatcher from appyter.context import get_env config = get_env(**kwargs) loop = asyncio.get_event_loop() emitter = EventEmitter() # run the app and reload it when necessary loop.create_task(app_runner(emitter, config)) # the underlying appyter library loop.create_task( file_watcher( emitter, 'reload', appyter.__path__[0], watcher_cls=GlobWatcher, watcher_kwargs=dict( include_dir_glob=['*'], include_file_glob=['*.py'], exclude_dir_glob=[], exclude_file_glob=[], ), )) # the underlying appyter library's templates/ipynb/staticfiles/... loop.create_task( file_watcher( emitter, 'livereload', os.path.join(appyter.__path__[0], 'profiles'), watcher_cls=GlobWatcher, watcher_kwargs=dict( include_dir_glob=['*'], include_file_glob=['*'], exclude_dir_glob=[], exclude_file_glob=['*.py'], ), )) # the appyter itself's filters/blueprints loop.create_task( file_watcher( emitter, 'reload', config['CWD'], watcher_cls=GlobWatcher, watcher_kwargs=dict( include_dir_glob=['filters', 'blueprints'], include_file_glob=['*.py'], exclude_dir_glob=[], exclude_file_glob=[], ), )) # the appyter itself's templates/ipynb/staticfiles/... loop.create_task( file_watcher( emitter, 'livereload', config['CWD'], watcher_cls=GlobWatcher, watcher_kwargs=dict( include_dir_glob=['*'], include_file_glob=['*'], exclude_dir_glob=[config['DATA_DIR']], exclude_file_glob=['*.py'], ), )) loop.create_task(app_messager(emitter, config)) loop.run_forever()
def dockerize(ipynb, cwd, output, **kwargs): env = get_jinja2_env(config=get_env(cwd=cwd, ipynb=ipynb, mode='dockerize', **kwargs), ) env.get_template('production/Dockerfile.j2').stream().dump(output)
def create_app(**kwargs): ''' Completely initialize the flask application ''' from aiohttp import web from aiohttp_wsgi import WSGIHandler from aiohttp_remotes import setup, XForwardedRelaxed # from flask import Flask, Blueprint, current_app, redirect from flask_cors import CORS # from appyter.render.flask_app.socketio import socketio from appyter.render.flask_app.core import core import appyter.render.flask_app.static import appyter.render.flask_app.download import appyter.render.flask_app.execution if kwargs['debug']: import appyter.render.flask_app.livereload # from appyter.context import get_env, find_blueprints from appyter.util import join_routes config = get_env(**kwargs) # if config['DEBUG']: logging.basicConfig( level=logging.DEBUG, format='%(name)s %(message).80s', ) else: logging.basicConfig( level=logging.WARNING, format='%(name)s %(message).80s', ) logging.getLogger(__package__).setLevel(logging.INFO) # logger.info('Initializing aiohttp...') app = web.Application() app['config'] = config # logger.info('Initializing socketio...') socketio.attach(app, join_routes(config['PREFIX'], 'socket.io')) # logger.info('Initializing flask...') flask_app = Flask(__name__, static_url_path=None, static_folder=None) CORS(flask_app) flask_app.config.update(config) flask_app.debug = config['DEBUG'] # logger.info('Registering blueprints...') flask_app.register_blueprint(core) for blueprint_name, blueprint in find_blueprints( config=flask_app.config).items(): if isinstance(blueprint, Blueprint): flask_app.register_blueprint(blueprint, url_prefix='/' + blueprint_name.strip('/')) elif callable(blueprint): blueprint(flask_app, url_prefix='/' + blueprint_name.strip('/')) else: raise Exception('Unrecognized blueprint type: ' + blueprint_name) # if app['config']['PREFIX'].strip('/'): logger.info('Registering prefix redirect') async def redirect_to_prefix(request): path = request.match_info['path'] if path == app['config']['PREFIX'].strip('/'): path = '' raise web.HTTPFound( join_routes(app['config']['PREFIX'], path) + '/') app.router.add_get('/{path:[^/]*}', redirect_to_prefix) # logger.info('Registering flask with aiohttp...') wsgi_handler = WSGIHandler(flask_app) app.router.add_route( '*', join_routes(app['config']['PREFIX'], '{path_info:.*}'), wsgi_handler) if flask_app.config['PROXY']: logger.info('Applying proxy fix middleware...') import asyncio asyncio.get_event_loop().run_until_complete( setup(app, XForwardedRelaxed())) return app
import os, sys sys.path.insert(0, os.path.realpath('..')) from appyter.parse.nb import nb_from_ipynb_file from appyter.render.nbconstruct import render_nb_from_nbtemplate from appyter.render.form import render_form_from_nbtemplate from appyter.context import get_jinja2_env, get_env from appyter.parse.nbtemplate import parse_fields_from_nbtemplate from appyter.parse.nb import parse_markdown nbtemplate = nb_from_ipynb_file('example.ipynb') config = get_env(cwd=os.path.dirname(__file__), ipynb='example.ipynb') env = get_jinja2_env(config=config) parse_fields_from_nbtemplate(env, nbtemplate) env = get_jinja2_env(config=config) render_form_from_nbtemplate(env, nbtemplate) env = get_jinja2_env( context={ 'number_1': 8, 'operator': 'subtract', }, config=config, ) render_nb_from_nbtemplate(env, nbtemplate)
def init(_globals, verbose=False, ipynb='app.ipynb', mode='magic', **kwargs): ''' Initialize appyter magic. Sets up a jinj2 environment and injects %%appyter magic into your environment. :param _globals: (Dict[str, Any]) A callable with your globals for the purpose of injection, basically just: `lambda _=globals: _()` :param verbose: (Optional[bool]) Expand exception reporting to be more verbose ''' import os import jinja2 import jinja2.meta import traceback from appyter.context import get_env, get_jinja2_env env = get_jinja2_env( config=get_env(verbose=verbose, ipynb=ipynb, mode=mode, **kwargs)) from IPython.core.magic import register_cell_magic from IPython.display import display, Markdown, HTML ''' register_cell_magic allows function to execute an entire cell with the following call structure: ```python %%my_magic whatever all my data ``` Results in a call: ```python my_magic( "whatever", """all my data""" ) ``` ''' @register_cell_magic def appyter(line, cell): ''' Appyter Cell Magic: See Steps for more information. Compile jinja2 into source code, and then evaluate that source code. ''' ''' Step 1. Render cell with jinja2, removing empty lines. execute or display the results, modifying a copy of the current python globals dict. ''' global_internal = _globals() cell_type = line.split('_') try: cell_lines = cell.splitlines() try: undeclared = jinja2.meta.find_undeclared_variables( env.parse(cell)) if undeclared: for lineno, cell_line in enumerate(cell_lines): if any(v in cell_line for v in undeclared): display( HTML( f"<div style='color: red'>{lineno}: {cell_line}</div>" )) raise Exception(f"undeclared variable(s) {undeclared}") template = env.from_string(cell) template_rendered = template.render() except Exception as e: if getattr(e, 'lineno', None) is not None: display( HTML( f"<div style='color: red'>{e.lineno}: {cell_lines[e.lineno-1]}</div>" )) raise e # rendered_template_lines = list( filter(None, map(str.rstrip, template_rendered.splitlines()))) try: if len(rendered_template_lines) > 0: if cell_type == ['markdown']: display(Markdown(template_rendered)) elif 'code' in cell_type: rendered = '\n'.join(rendered_template_lines[:-1]) rendered_last = rendered_template_lines[-1] display( Markdown('```python\n%s\n```' % ('\n'.join( (rendered, rendered_last))))) # if 'eval' in cell_type: exec(rendered, global_internal) # try: display(eval(rendered_last, global_internal)) except Exception as e: setattr(e, 'lineno', len(rendered_template_lines)) raise e elif 'exec' in cell_type: exec('\n'.join((rendered, rendered_last)), global_internal) else: raise Exception('Unrecognized appyter cell_type') # except Exception as e: if getattr(e, 'lineno', None) is not None: display( HTML( f"<div style='color: red'>{e.lineno}: {rendered_template_lines[e.lineno-1]}</div>" )) raise e except Exception as e: display(HTML(f"<div style='color: red'>Error: {e}</div>")) if verbose: traceback.print_exc() return ''' Step 2. Check for new variables in the internal global and pass them to the python global scope. Check for new variables in the jinja2 template and pass them to the template environment global so they are available in future jinja2 calls. ''' for k, v in global_internal.items(): if not k.startswith('_'): _globals()[k] = v for k, v in template.module.__dict__.items(): if not k.startswith('_'): env.globals[k] = v
def nbinspect(cwd, ipynb, output, **kwargs): env = get_jinja2_env( config=get_env(cwd=cwd, ipynb=ipynb, mode='inspect', **kwargs)) nbtemplate = nb_from_ipynb_io(Filesystem(cwd).open(ipynb, 'r')) fields = render_nbtemplate_json_from_nbtemplate(env, nbtemplate) json.dump(fields, output)