def compile_sketch(sketch_name, generate_index=False, index_template=None, force_local=False): """ Transcrypt the sketch python code to javascript. :param sketch_name: name for new sketch :param generate_index: boolean to flag if the index.html file should be updated :param force_local: boolean to flag to force local run (used by web editor only) :type sketch_name: string :return: file names :rtype: list of strings """ sketch = Sketch(sketch_name) sketch.validate_name() if not sketch.sketch_exists: raise PythonSketchDoesNotExist(sketch) compile_sketch_js(sketch, force_local=force_local) if generate_index: # to be able to overwrite default index template file # useful for generating the docs or debugging sketch.config.index_template = index_template index_contet = get_sketch_index_content(sketch) with open(sketch.index_html, "w", encoding="utf-8") as fd: fd.write(index_contet) cprint.info(f"{sketch.index_html.resolve()} updated") return sketch
class TestNewSketchCommand(TestCase): def setUp(self): self.sketch_name = 'foo' self.sketch = Sketch(self.sketch_name) def tearDown(self): if SKETCHBOOK_DIR.exists(): shutil.rmtree(SKETCHBOOK_DIR) def test_create_new_sketch_with_all_required_files(self): commands.new_sketch(self.sketch_name) assert self.sketch.index_html.exists() assert self.sketch.sketch_py.exists() assert self.sketch.p5js.exists() assert self.sketch.config_file.exists() assert self.sketch.config.interpreter == TRANSCRYPT_INTERPRETER def test_create_pyodide_sketch(self): commands.new_sketch(self.sketch_name, interpreter=PYODIDE_INTERPRETER) self.sketch = Sketch(self.sketch_name) # read config after init assert self.sketch.index_html.exists() assert self.sketch.sketch_py.exists() assert self.sketch.p5js.exists() assert self.sketch.config_file.exists() assert self.sketch.config.interpreter == PYODIDE_INTERPRETER def test_raise_exception_if_dir_already_exist(self): self.sketch.create_sketch_dir() with pytest.raises(SketchDirAlreadyExistException): commands.new_sketch(self.sketch_name)
def new_sketch(sketch_name, interpreter=PYODIDE_INTERPRETER): """ Creates a new sketch with the required assets and a index.html file, based on pyp5js's templates :param sketch_name: name for new sketch :param interpreter: interpreter to use (transcrypt or pyodide) :type sketch_name: string :return: file names :rtype: list of strings """ sketch = Sketch(sketch_name, interpreter=interpreter) sketch.create_sketch_dir() templates_files = [ (sketch.config.get_base_sketch_template(), sketch.sketch_py), (PYP5JS_FILES.p5js, sketch.p5js), ] for src, dest in templates_files: shutil.copyfile(src, dest) index_contet = get_sketch_index_content(sketch) with open(sketch.index_html, "w") as fd: fd.write(index_contet) return sketch
def test_loads_config_from_config_file(self): files = Sketch('bar', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() # writes config file json same_files = Sketch('bar') assert same_files.config_file == files.config_file assert same_files.config.interpreter == PYODIDE_INTERPRETER
def test_create_pyodide_sketch(self): commands.new_sketch(self.sketch_name, interpreter=PYODIDE_INTERPRETER) self.sketch = Sketch(self.sketch_name) # read config after init assert self.sketch.index_html.exists() assert self.sketch.sketch_py.exists() assert self.sketch.p5js.exists() assert self.sketch.config_file.exists() assert self.sketch.config.interpreter == PYODIDE_INTERPRETER
def test_get_target_sketch_context_for_local_pyodide(self): files = Sketch( self.files.sketch_name, pyodide_js_url="static/pyodide/pyodide.js", interpreter=PYODIDE_INTERPRETER) expected_context = { "sketch_name": files.sketch_name, "sketch_content": files.sketch_content, "pyodide_index_url": "static/pyodide/" } assert expected_context == files.get_target_sketch_context()
class TestNewSketchCommand(TestCase): def setUp(self): self.sketch_name = 'foo' self.sketch = Sketch(self.sketch_name) def tearDown(self): if SKETCHBOOK_DIR.exists(): shutil.rmtree(SKETCHBOOK_DIR) def test_create_new_sketch_with_all_required_files(self): commands.new_sketch(self.sketch_name) assert self.sketch.index_html.exists() assert self.sketch.sketch_py.exists() assert not self.sketch.p5js.exists() assert self.sketch.config_file.exists() assert self.sketch.config.interpreter == TRANSCRYPT_INTERPRETER assert self.sketch.config.index_template == "" def test_create_pyodide_sketch(self): commands.new_sketch(self.sketch_name, interpreter=PYODIDE_INTERPRETER) self.sketch = Sketch(self.sketch_name) # read config after init assert self.sketch.index_html.exists() assert self.sketch.sketch_py.exists() assert not self.sketch.p5js.exists() assert self.sketch.config_file.exists() assert self.sketch.config.interpreter == PYODIDE_INTERPRETER def test_raise_exception_if_dir_already_exist(self): self.sketch.create_sketch_dir() with pytest.raises(SketchDirAlreadyExistException): commands.new_sketch(self.sketch_name) def test_create_sketch_with_custom_index(self): template = PYP5JS_FILES.install.parent / "docs" / "examples" / "transcrypt" / "index.html.template" commands.new_sketch(self.sketch_name, template_file=template) assert self.sketch.index_html.exists() with open(self.sketch.index_html) as fd: content = fd.read() assert "demoContainer" in content def test_create_sketch_using_local_installed_assets(self): commands.new_sketch(self.sketch_name, use_cdn=False) assert self.sketch.p5js.exists()
def compile_sketch(sketch_name): """ Transcrypt the sketch python code to javascript. :param sketch_name: name for new sketch :type sketch_name: string :return: file names :rtype: list of strings """ sketch = Sketch(sketch_name) sketch.validate_name() if not sketch.sketch_exists: raise PythonSketchDoesNotExist(sketch) compile_sketch_js(sketch) return sketch
def sketches_list_view(): sketches = [] for sketch_dir in (p for p in SKETCHBOOK_DIR.iterdir() if p.is_dir()): name = sketch_dir.name sketch = Sketch(name) if sketch.has_all_files: sketches.append({'name': name, 'url': f'/sketch/{name}/'}) sketches = sorted(sketches, key=lambda s: s['name']) return render_template('index.html', sketches=sketches, sketches_dir=SKETCHBOOK_DIR.resolve())
def new_sketch(sketch_name, interpreter=PYODIDE_INTERPRETER, template_file="", use_cdn=True): """ Creates a new sketch with the required assets and a index.html file, based on pyp5js's templates :param sketch_name: name for new sketch :param interpreter: interpreter to use (transcrypt or pyodide) :param template_file: use a custom template for index.html instead of default one :param use_cdn: if false, the sketch will have copies of required static assets (p5.js and pyodide) :type sketch_name: string :return: file names :rtype: list of strings """ cfg = { "interpreter": interpreter, "index_template": template_file, } sketch = Sketch(sketch_name, **cfg) sketch.create_sketch_dir() sketch.copy_initial_files(use_cdn=use_cdn) index_contet = get_sketch_index_content(sketch) with open(sketch.index_html, "w", encoding="utf-8") as fd: fd.write(index_contet) return sketch
def test_run_compiler_as_expected_if_dir_name_with_space(self): previous_dir = config.SKETCHBOOK_DIR dir_with_space = Path.home().joinpath("pyp5js space dir") if not dir_with_space.exists(): dir_with_space.mkdir() config.__dict__["SKETCHBOOK_DIR"] = dir_with_space self.sketch = Sketch('foo') self.compiler = TranscryptCompiler(self.sketch) self.sketch.create_sketch_dir() self.sketch.sketch_py.touch() try: self.compiler.prepare() self.compiler.run_compiler() assert self.compiler.target_dir.exists() assert self.sketch.target_sketch.exists() finally: if dir_with_space.exists(): shutil.rmtree(dir_with_space) config.__dict__["SKETCHBOOK_DIR"] = previous_dir
def monitor_sketch(sketch_name): """ Monitor for any change in any .py inside the sketch dir. For every new change, runs the transcrypt to update the js files. :param sketch_name: name for new sketch :type sketch_name: string :return: file names :rtype: list of strings """ sketch = Sketch(sketch_name) sketch.validate_name() if not sketch.sketch_exists: raise PythonSketchDoesNotExist(sketch) cprint(f"Monitoring for changes in {sketch.sketch_dir.resolve()}...") try: monitor_sketch_service(sketch) except KeyboardInterrupt: cprint.info("Exiting monitor...")
def render_sketch_view(sketch_name, static_path): sketch = Sketch(sketch_name) error = '' if static_path: return _serve_static(sketch.sketch_dir, static_path) elif request.method == 'POST': py_code = request.form.get('py_code', '') if not py_code.strip(): error = 'You have to input the Python code.' elif 'def setup():' not in py_code: error = 'You have to define a setup function.' elif 'def draw():' not in py_code: error = 'You have to define a draw function.' else: try: ast.parse(py_code, sketch.sketch_py.name) sketch.sketch_py.write_text(py_code) except SyntaxError as exc: error = f'SyntaxError: {exc}' if not error: try: # web editor must always use local install of JS dependencies commands.compile_sketch(sketch_name, force_local=True) except PythonSketchDoesNotExist: return f"There's no sketch in {sketch.sketch_dir.resolve()}", 404 context = { 'p5_js_url': sketch.urls.p5_js_url, 'sketch_js_url': sketch.urls.sketch_js_url, 'sketch_name': sketch.sketch_name, 'py_code': sketch.sketch_py.read_text(), 'error': error, 'js_as_module': sketch.config.is_transcrypt, 'live_run': sketch.config.is_pyodide, } return render_template('view_sketch.html', **context)
def test_copy_local_files_if_not_using_cdn(self): files = Sketch('test', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() files.copy_initial_files(use_cdn=False) pyodide_js_dir = files.static_dir / "pyodide" assert files.config.p5_js_url == "/static/p5.js" assert files.config.pyodide_js_url == "/static/pyodide/pyodide_v0.18.1.js" assert files.p5js.exists() assert pyodide_js_dir.exists() assert (pyodide_js_dir / "pyodide.asm.data").exists() assert (pyodide_js_dir / "pyodide.asm.js").exists() assert (pyodide_js_dir / "pyodide.asm.wasm").exists() assert (pyodide_js_dir / "pyodide.js.map").exists() assert (pyodide_js_dir / "pyodide_v0.18.1.js").exists() assert not (pyodide_js_dir / "packages.json").exists()
def test_name_should_accept_underscore_in_the_beginning(self): file = Sketch('__name__') assert file.sketch_name == '__name__'
def test_raise_exception_when_name_creating_dir_with_invalid_name(self): files = Sketch('name&') with pytest.raises(InvalidName): files.create_sketch_dir()
def test_raise_exception_when_name_contains_non_alphanumeric_chars(self): files = Sketch('name&') with pytest.raises(InvalidName): files.validate_name()
def test_raise_exception_when_name_starts_with_numbers(self): files = Sketch('123name') with pytest.raises(InvalidName): files.validate_name()
def setUp(self): self.base_dir = SKETCHBOOK_DIR self.sketch_name = 'foo' self.files = Sketch(self.sketch_name)
class SketchTests(TestCase): def setUp(self): self.base_dir = SKETCHBOOK_DIR self.sketch_name = 'foo' self.files = Sketch(self.sketch_name) def tearDown(self): if self.base_dir.exists(): shutil.rmtree(self.base_dir) def get_expected_path(self, *args): return self.base_dir.joinpath(self.sketch_name, *args) def test_sketch_dirs(self): assert self.get_expected_path() == self.files.sketch_dir assert self.get_expected_path('static') == self.files.static_dir assert self.get_expected_path('target') == self.files.target_dir assert self.files.TARGET_NAME == 'target' def test_sketch(self): self.files.check_sketch_dir = False assert self.get_expected_path('index.html') == self.files.index_html assert self.get_expected_path('static', 'p5.js') == self.files.p5js assert self.get_expected_path('foo.py') == self.files.sketch_py assert self.get_expected_path('.properties.json') == self.files.config_file def test_target_sketch_variations(self): assert self.get_expected_path('target_sketch.py') == self.files.target_sketch self.files.config.interpreter = PYODIDE_INTERPRETER assert self.get_expected_path('target', 'target_sketch.js') == self.files.target_sketch def test_create_dirs(self): assert self.files.sketch_dir.exists() is False assert self.files.static_dir.exists() is False assert self.files.target_dir.exists() is False assert self.files.config_file.exists() is False self.files.create_sketch_dir() assert self.files.sketch_dir.exists() is True assert self.files.static_dir.exists() is True assert self.files.target_dir.exists() is True assert self.files.config_file.exists() is True with pytest.raises(SketchDirAlreadyExistException): self.files.create_sketch_dir() def test_raise_exception_when_name_starts_with_numbers(self): files = Sketch('123name') with pytest.raises(InvalidName): files.validate_name() def test_raise_exception_when_name_contains_non_alphanumeric_chars(self): files = Sketch('name&') with pytest.raises(InvalidName): files.validate_name() def test_raise_exception_when_name_creating_dir_with_invalid_name(self): files = Sketch('name&') with pytest.raises(InvalidName): files.create_sketch_dir() def test_name_should_accept_underscore_in_the_beginning(self): file = Sketch('__name__') assert file.sketch_name == '__name__' def test_name_should_accept_underscore_in_the_middle(self): file = Sketch('na_me') assert file.sketch_name == 'na_me' def test_loads_config_from_config_file(self): files = Sketch('bar', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() # writes config file json same_files = Sketch('bar') assert same_files.config_file == files.config_file assert same_files.config.interpreter == PYODIDE_INTERPRETER def test_sketch_custom_urls(self): files = Sketch(self.files.sketch_name, p5_js_url="static/p5.js", pyodide_js_url="static/pyodide/pyodide.js") urls = files.urls assert "static/p5.js" == urls.p5_js_url assert "static/pyodide/pyodide.js" == urls.pyodide_js_url assert "target/target_sketch.js" == urls.sketch_js_url def test_sketch_urls(self): urls = self.files.urls assert P5_JS_CDN == urls.p5_js_url assert PYODIDE_JS_CDN == urls.pyodide_js_url assert "target/target_sketch.js" == urls.sketch_js_url def test_get_target_sketch_context_for_local_pyodide(self): files = Sketch( self.files.sketch_name, pyodide_js_url="static/pyodide/pyodide.js", interpreter=PYODIDE_INTERPRETER) expected_context = { "sketch_name": files.sketch_name, "sketch_content": files.sketch_content, "pyodide_index_url": "static/pyodide/" } assert expected_context == files.get_target_sketch_context() def test_copy_local_files_if_not_using_cdn(self): files = Sketch('test', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() files.copy_initial_files(use_cdn=False) pyodide_js_dir = files.static_dir / "pyodide" assert files.config.p5_js_url == "/static/p5.js" assert files.config.pyodide_js_url == "/static/pyodide/pyodide_v0.18.1.js" assert files.p5js.exists() assert pyodide_js_dir.exists() assert (pyodide_js_dir / "pyodide.asm.data").exists() assert (pyodide_js_dir / "pyodide.asm.js").exists() assert (pyodide_js_dir / "pyodide.asm.wasm").exists() assert (pyodide_js_dir / "pyodide.js.map").exists() assert (pyodide_js_dir / "pyodide_v0.18.1.js").exists() assert not (pyodide_js_dir / "packages.json").exists()
class TranscryptCompilerTests(TestCase): def setUp(self): self.sketch = Sketch('foo') self.compiler = TranscryptCompiler(self.sketch) self.sketch.create_sketch_dir() self.sketch.sketch_py.touch() def tearDown(self): if config.SKETCHBOOK_DIR.exists(): shutil.rmtree(config.SKETCHBOOK_DIR) def test_transcrypt_target_dir_path(self): assert self.sketch.sketch_dir.joinpath( '__target__') == self.compiler.target_dir def test_command_line_string(self): pyp5_dir = PYP5JS_FILES.install target = self.sketch.target_sketch expected = ' '.join([str(c) for c in [ 'transcrypt', '-xp', f'"{pyp5_dir}"', '-k', '-ks', '-b', '-m', '-n', f'"{target}"' ]]) assert expected == self.compiler.command_line def test_run_compiler_as_expected(self): self.compiler.prepare() self.compiler.run_compiler() assert self.compiler.target_dir.exists() assert self.sketch.target_sketch.exists() def test_run_compiler_as_expected_if_dir_name_with_space(self): previous_dir = config.SKETCHBOOK_DIR dir_with_space = Path.home().joinpath("pyp5js space dir") if not dir_with_space.exists(): dir_with_space.mkdir() config.__dict__["SKETCHBOOK_DIR"] = dir_with_space self.sketch = Sketch('foo') self.compiler = TranscryptCompiler(self.sketch) self.sketch.create_sketch_dir() self.sketch.sketch_py.touch() try: self.compiler.prepare() self.compiler.run_compiler() assert self.compiler.target_dir.exists() assert self.sketch.target_sketch.exists() finally: if dir_with_space.exists(): shutil.rmtree(dir_with_space) config.__dict__["SKETCHBOOK_DIR"] = previous_dir def test_clean_up(self): self.compiler.target_dir.mkdir() self.sketch.target_sketch.touch() self.compiler.clean_up() assert not self.compiler.target_dir.exists() assert self.sketch.target_dir.exists() assert not self.sketch.target_sketch.exists() def test_prepare_sketch(self): expected_content = get_target_sketch_content(self.sketch) assert not self.sketch.target_sketch.exists() self.compiler.prepare() assert self.sketch.target_sketch.exists() with self.sketch.target_sketch.open('r') as fd: content = fd.read() assert expected_content == content
def test_can_specify_the_compiler_for_sketch(self): data = {'sketch_name': 'a_name', 'interpreter': 'transcrypt'} self.client.post(self.route, data=data) self.assert_template_used('new_sketch_success.html') assert Sketch('a_name').config.is_transcrypt
def test_sketch_custom_urls(self): files = Sketch(self.files.sketch_name, p5_js_url="static/p5.js", pyodide_js_url="static/pyodide/pyodide.js") urls = files.urls assert "static/p5.js" == urls.p5_js_url assert "static/pyodide/pyodide.js" == urls.pyodide_js_url assert "target/target_sketch.js" == urls.sketch_js_url
def sketch(): files = Sketch('foo') files.create_sketch_dir() yield files shutil.rmtree(SKETCHBOOK_DIR)
def sketch_pyodide(): files = Sketch('foo', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() yield files shutil.rmtree(SKETCHBOOK_DIR)
def test_post_with_sketch_name_should_render_success_form(self): self.client.post(self.route, data={'sketch_name': 'a_name'}) self.assert_template_used('new_sketch_success.html') assert Sketch('a_name').config.is_pyodide
def setUp(self): self.sketch_name = 'foo' self.sketch = Sketch(self.sketch_name)
def test_name_should_accept_underscore_in_the_middle(self): file = Sketch('na_me') assert file.sketch_name == 'na_me'
class SketchTests(TestCase): def setUp(self): self.base_dir = SKETCHBOOK_DIR self.sketch_name = 'foo' self.files = Sketch(self.sketch_name) def tearDown(self): if self.base_dir.exists(): shutil.rmtree(self.base_dir) def get_expected_path(self, *args): return self.base_dir.joinpath(self.sketch_name, *args) def test_sketch_dirs(self): assert self.get_expected_path() == self.files.sketch_dir assert self.get_expected_path('static') == self.files.static_dir assert self.get_expected_path('target') == self.files.target_dir assert self.files.TARGET_NAME == 'target' def test_sketch(self): self.files.check_sketch_dir = False assert self.get_expected_path('index.html') == self.files.index_html assert self.get_expected_path('static', 'p5.js') == self.files.p5js assert self.get_expected_path('foo.py') == self.files.sketch_py assert self.get_expected_path( '.properties.json') == self.files.config_file def test_target_sketch_variations(self): assert self.get_expected_path( 'target_sketch.py') == self.files.target_sketch self.files.config.interpreter = PYODIDE_INTERPRETER assert self.get_expected_path( 'target', 'target_sketch.js') == self.files.target_sketch def test_create_dirs(self): assert self.files.sketch_dir.exists() is False assert self.files.static_dir.exists() is False assert self.files.target_dir.exists() is False assert self.files.config_file.exists() is False self.files.create_sketch_dir() assert self.files.sketch_dir.exists() is True assert self.files.static_dir.exists() is True assert self.files.target_dir.exists() is True assert self.files.config_file.exists() is True with pytest.raises(SketchDirAlreadyExistException): self.files.create_sketch_dir() def test_raise_exception_when_name_starts_with_numbers(self): files = Sketch('123name') with pytest.raises(InvalidName): files.validate_name() def test_raise_exception_when_name_contains_non_alphanumeric_chars(self): files = Sketch('name&') with pytest.raises(InvalidName): files.validate_name() def test_raise_exception_when_name_creating_dir_with_invalid_name(self): files = Sketch('name&') with pytest.raises(InvalidName): files.create_sketch_dir() def test_name_should_accept_underscore_in_the_beginning(self): file = Sketch('__name__') assert file.sketch_name == '__name__' def test_name_should_accept_underscore_in_the_middle(self): file = Sketch('na_me') assert file.sketch_name == 'na_me' def test_loads_config_from_config_file(self): files = Sketch('bar', interpreter=PYODIDE_INTERPRETER) files.create_sketch_dir() # writes config file json same_files = Sketch('bar') assert same_files.config_file == files.config_file assert same_files.config.interpreter == PYODIDE_INTERPRETER
def setUp(self): self.sketch = Sketch('foo') self.compiler = TranscryptCompiler(self.sketch) self.sketch.create_sketch_dir() self.sketch.sketch_py.touch()