Skip to content

Commit

Permalink
Merge pull request #195 from berinhard/feature/serve-pyodide-locally
Browse files Browse the repository at this point in the history
Feature/serve pyodide locally
  • Loading branch information
berinhard authored Nov 1, 2021
2 parents 9fe3bf2 + f6c8536 commit 147a211
Show file tree
Hide file tree
Showing 15 changed files with 163 additions and 55 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Development
-----------
- Serve pyodide JS files locally [PR #186](https://github.com/berinhard/pyp5js/pull/186)
- Create sketch using p5.js from CDN [PR #191](https://github.com/berinhard/pyp5js/pull/191)
- Add `keyIsDown` event to Transcrypt mode - [PR #187](https://github.com/berinhard/pyp5js/pull/187)
- Fix bug with multiple events calls - PR #187 too
- Serve JS files if `--local` flag [PR #195](https://github.com/berinhard/pyp5js/pull/195)

0.7.0
-----
Expand Down
4 changes: 2 additions & 2 deletions pyp5js/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ def command_line_entrypoint():
@click.option('--interpreter', '-i', type=click.Choice(AVAILABLE_INTERPRETERS), default=PYODIDE_INTERPRETER, help='Which python tool to use to run the sketch. (defaults to pyodide)')
@click.option('--template', '-t', type=click.Path(exists=True), help='Specify a custom index.html template to use.')
@click.option('--cdn/--local', default=True)
def configure_new_sketch(sketch_name, monitor, interpreter, template, use_cdn):
def configure_new_sketch(sketch_name, monitor, interpreter, template, cdn):
"""
Create dir and configure boilerplate - Example:\n
$ pyp5js new my_sketch -i pyodide
"""
files = commands.new_sketch(sketch_name, interpreter, template_file=template, use_cdn=use_cdn)
files = commands.new_sketch(sketch_name, interpreter, template_file=template, use_cdn=cdn)

cprint.ok(f"Your sketch was created!")

Expand Down
14 changes: 1 addition & 13 deletions pyp5js/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from cprint import cprint
from jinja2 import Environment, FileSystemLoader

from pyp5js.config.fs import PYP5JS_FILES
from pyp5js.compiler import compile_sketch_js
from pyp5js.exceptions import PythonSketchDoesNotExist
from pyp5js.sketch import Sketch
Expand All @@ -30,21 +29,10 @@ def new_sketch(sketch_name, interpreter=PYODIDE_INTERPRETER, template_file="", u
"interpreter": interpreter,
"index_template": template_file,
}
if not use_cdn:
cfg["p5_js_url"] = "/static/p5.js"
# TODO: static version for pyodide too

sketch = Sketch(sketch_name, **cfg)
sketch.create_sketch_dir()

templates_files = [
(sketch.config.get_base_sketch_template(), sketch.sketch_py),
]
if not use_cdn:
templates_files.append((PYP5JS_FILES.p5js, sketch.p5js))
# TODO: copy pyodide files to static dir too
for src, dest in templates_files:
shutil.copyfile(src, dest)
sketch.copy_initial_files(use_cdn=use_cdn)

index_contet = get_sketch_index_content(sketch)
with open(sketch.index_html, "w") as fd:
Expand Down
4 changes: 4 additions & 0 deletions pyp5js/config/fs.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@ def pyodide_index_html(self):
def pyodide_base_sketch_template(self):
return self.templates_dir.joinpath('pyodide', 'base_sketch.py.template')

@property
def pyodide_js_dir(self):
return self.static_dir / "js" / "pyodide"


PYP5JS_FILES = LibFiles()
9 changes: 7 additions & 2 deletions pyp5js/config/sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
TRANSCRYPT_INTERPRETER = 'transcrypt'
PYODIDE_INTERPRETER = 'pyodide'
P5_JS_CDN = 'https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.min.js'
PYODIDE_JS_CDN = 'https://cdn.jsdelivr.net/pyodide/v0.18.1/full/pyodide.js'


class SketchConfig:

Expand All @@ -19,6 +21,7 @@ def __init__(self, interpreter, **kwargs):
self.interpreter = interpreter
self.index_template = kwargs.get("index_template", "")
self.p5_js_url = kwargs.get("p5_js_url", P5_JS_CDN)
self.pyodide_js_url = kwargs.get("pyodide_js_url", PYODIDE_JS_CDN)

@property
def index_template_path(self):
Expand All @@ -31,10 +34,12 @@ def write(self, fname):
with open(fname, "w") as fd:
data = {
"interpreter": self.interpreter,
"index_template": index_template,
# TODO: also store pyodide_js_url
"p5_js_url": self.p5_js_url,
}
if self.index_template:
data.update({"index_template": index_template})
if self.is_pyodide:
data.update({"pyodide_js_url": self.pyodide_js_url})
json.dump(data, fd)

@property
Expand Down
12 changes: 6 additions & 6 deletions pyp5js/http/templates/view_sketch.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
}

</style>

<script src="/static/js/p5/p5.js"></script>
{% if live_run %}
<script src="/static/js/pyodide/pyodide_v0.18.1.js"></script>
{% endif %}
{% endblock %}

{% block content %}
Expand Down Expand Up @@ -119,11 +124,6 @@
});
</script>

{% if live_run %}
<script src="/static/js/pyodide/pyodide_v0.18.1.js"></script>
{% endif %}

<script src="/static/p5/p5.js.min"></script>
<script src="{{ sketch_js_url }}" {% if js_as_module %}type="module"{% endif %}></script>

<script type="text/javascript">
Expand Down Expand Up @@ -171,7 +171,7 @@
{% endif %}
}
}

});
</script>

{% endblock %}
50 changes: 45 additions & 5 deletions pyp5js/sketch.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import os
import re
import shutil
from collections import namedtuple

from pyp5js import config
from pyp5js.config.fs import PYP5JS_FILES
from pyp5js.config.sketch import SketchConfig
from pyp5js.exceptions import SketchDirAlreadyExistException, InvalidName


SketchUrls = namedtuple('SketchUrls', ['p5_js_url', 'sketch_js_url'])
SketchUrls = namedtuple('SketchUrls', ['p5_js_url', 'pyodide_js_url', 'sketch_js_url'])


class Sketch:
Expand All @@ -29,10 +30,13 @@ def validate_name(self):
does_not_start_with_letter_or_underscore = r'^[^a-zA-Z_]'
contains_non_alphanumerics_except_underscore = r'[^a-zA-Z0-9_]'
if re.match(does_not_start_with_letter_or_underscore, self.sketch_name) or \
re.search(contains_non_alphanumerics_except_underscore, self.sketch_name):
re.search(contains_non_alphanumerics_except_underscore, self.sketch_name):
raise InvalidName(self)

def create_sketch_dir(self):
"""
Create sketch required directories
"""
self.validate_name()

if self.sketch_dir.exists():
Expand All @@ -43,6 +47,28 @@ def create_sketch_dir(self):
self.target_dir.mkdir()
self.config.write(self.config_file)

def copy_initial_files(self, use_cdn=True):
"""
Copy requlred template files to the sketch directory
"""
if not use_cdn:
self.config.p5_js_url = "/static/p5.js"
if self.config.is_pyodide:
self.config.pyodide_js_url = "/static/pyodide/pyodide_v0.18.1.js"

templates_files = [
(self.config.get_base_sketch_template(), self.sketch_py),
]
if not use_cdn:
templates_files.append((PYP5JS_FILES.p5js, self.p5js))
if self.config.is_pyodide:
shutil.copytree(PYP5JS_FILES.pyodide_js_dir, self.static_dir / "pyodide")
# delete packages.json that's not necessary
(self.static_dir / "pyodide" / "packages.json").unlink()

for src, dest in templates_files:
shutil.copyfile(src, dest)

@property
def sketch_exists(self):
return self.sketch_py.exists()
Expand All @@ -54,7 +80,6 @@ def sketch_content(self):
with self.sketch_py.open() as fd:
return fd.read()


@property
def has_all_files(self):
return all([
Expand Down Expand Up @@ -109,7 +134,22 @@ def __eq__(self, other):
@property
def urls(self):
return SketchUrls(
# TODO: add pyodide_js_url
p5_js_url=self.config.p5_js_url,
pyodide_js_url=self.config.pyodide_js_url,
sketch_js_url=f"{self.TARGET_NAME}/target_sketch.js",
)

def get_target_sketch_context(self):
"""
This method is used by the template renderers to get the context to be used
to render final target/target_sketch.js file.
"""
context = {
"sketch_name": self.sketch_name,
"sketch_content": self.sketch_content,
}
if self.config.is_pyodide:
index = "/".join(self.config.pyodide_js_url.split("/")[:-1]) + "/"
context.update({'pyodide_index_url': index})
return context

2 changes: 1 addition & 1 deletion pyp5js/templates/pyodide/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<title>{{ sketch_name }} - pyp5js (using pyodide)</title>
<style> body, html, canvas {padding: 0; margin: 0; overflow: hidden;} </style>

<script src="/static/js/pyodide/pyodide_v0.18.1.js"></script>
<script src="{{ pyodide_js_url }}"></script>
<script src="{{ p5_js_url }}"></script>
<script src="{{ sketch_js_url }}" type="module"></script>
</head>
Expand Down
5 changes: 3 additions & 2 deletions pyp5js/templates/pyodide/target_sketch.js.template
Original file line number Diff line number Diff line change
Expand Up @@ -1674,7 +1674,7 @@ function runCode() {

async function main() {
const config = {
indexURL : "/static/js/pyodide/", # TODO: replace hardcoded string by {{ indexURL }}
indexURL : "{{ pyodide_index_url }}",
fullStdLib: false,
}
window.pyodide = await loadPyodide(config);
Expand All @@ -1692,4 +1692,5 @@ async function main() {
runCode();
};

await main();
// async method
main();
8 changes: 2 additions & 6 deletions pyp5js/templates_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def get_sketch_index_content(sketch):
context = {
"sketch_name": sketch.sketch_name,
"p5_js_url": sketch.urls.p5_js_url,
"pyodide_js_url": sketch.urls.pyodide_js_url,
"sketch_js_url": sketch.urls.sketch_js_url,
"sketch_content": sketch.sketch_content,
}
Expand All @@ -31,11 +32,6 @@ def get_target_sketch_content(sketch):
"""
Renders the content to be written in the temporary SKETCH_NAME/target_sketch.py file
"""
context = {
"sketch_name": sketch.sketch_name,
"sketch_content": sketch.sketch_content,
# TODO: if pyodide, add the pyodide indexUrl here too
# (details about this here https://github.com/berinhard/pyp5js/pull/186#pullrequestreview-782362038)
}
context = sketch.get_target_sketch_context()
target_js_file = sketch.config.get_target_js_template()
return _template_from_file(target_js_file, context)
8 changes: 8 additions & 0 deletions pyp5js/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def sketch():
yield files
shutil.rmtree(SKETCHBOOK_DIR)


@fixture()
def sketch_pyodide():
files = Sketch('foo', interpreter=PYODIDE_INTERPRETER)
files.create_sketch_dir()
yield files
shutil.rmtree(SKETCHBOOK_DIR)

@fixture
def transcrypt_json_file():
try:
Expand Down
1 change: 0 additions & 1 deletion pyp5js/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,3 @@ def test_create_sketch_using_local_installed_assets(self):
commands.new_sketch(self.sketch_name, use_cdn=False)

assert self.sketch.p5js.exists()
# TODO: assertion on pyodide assets too
8 changes: 5 additions & 3 deletions pyp5js/tests/test_config/test_sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from tempfile import NamedTemporaryFile

from pyp5js.config import TRANSCRYPT_INTERPRETER, PYODIDE_INTERPRETER
from pyp5js.config.sketch import SketchConfig, P5_JS_CDN
from pyp5js.config.sketch import SketchConfig, P5_JS_CDN, PYODIDE_JS_CDN
from pyp5js.config.fs import PYP5JS_FILES

from ..fixtures import transcrypt_json_file, pyodide_json_file, transcrypt_config, pyodide_config, custom_index_json_file
Expand Down Expand Up @@ -36,15 +36,17 @@ def test_write_sketch_interpreter_config(custom_index_json_file):
assert data == expected


def test_wrrite_defaults():
def test_write_defaults():
config = SketchConfig(PYODIDE_INTERPRETER)
fd = NamedTemporaryFile(mode="w", delete=False)
config.write(fd.name)
fd.close()
with open(fd.name) as fd:
data = json.load(fd)

assert "" == data["index_template"]
assert PYODIDE_INTERPRETER == data["interpreter"]
assert P5_JS_CDN == data["p5_js_url"]
assert PYODIDE_JS_CDN == data["pyodide_js_url"]


def test_get_transcrypt_index_template(transcrypt_config):
Expand Down
35 changes: 33 additions & 2 deletions pyp5js/tests/test_sketch.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from unittest import TestCase

from pyp5js.config import SKETCHBOOK_DIR, PYODIDE_INTERPRETER
from pyp5js.config.sketch import P5_JS_CDN
from pyp5js.config.sketch import P5_JS_CDN, PYODIDE_JS_CDN
from pyp5js.exceptions import SketchDirAlreadyExistException
from pyp5js.sketch import Sketch
from pyp5js.exceptions import InvalidName
Expand Down Expand Up @@ -90,12 +90,43 @@ def test_loads_config_from_config_file(self):
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")
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()
Loading

0 comments on commit 147a211

Please sign in to comment.