Skip to content

Commit 582580c

Browse files
authored
Merge pull request #54 from pyscript/fpliger/add_offline_mode
Add how to run pyscript apps offline to docs
2 parents 4afe14b + 24dbaae commit 582580c

File tree

4 files changed

+527
-2
lines changed

4 files changed

+527
-2
lines changed

docs/beginning-pyscript.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ All PyScript applications need three things:
3535
how your application works.
3636

3737
Create these files with your favourite code editor on your local file system.
38-
Alternatively, [pyscript.com](https://pyscript.com) will take away all the pain of
39-
organising, previewing and deploying your application.
38+
Alternatively, [pyscript.com](https://pyscript.com) will take away all the pain
39+
of organising, previewing and deploying your application.
4040

4141
If you decide to use [pyscript.com](https://pyscript.com) (recommended for first
4242
steps), once signed in, create a new project by pressing the "+" button on the
@@ -246,6 +246,12 @@ static web host will do (for example,
246246
[Google Cloud](https://cloud.google.com/storage/docs/hosting-static-website) or
247247
[Microsoft's Azure](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website)).
248248

249+
## Run PyScript Offline
250+
251+
To run PyScript offline, without the need of a CDN or internet connection, read
252+
the [Run PyScript Offline](user-guide/offline.md) section of the user
253+
guide.
254+
249255
## Conclusion
250256

251257
Congratulations!

docs/user-guide/offline.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# Use PyScript Offline
2+
3+
Sometimes you want to run PyScript applications offline.
4+
5+
Both PyScript core and the interpreter used to run code need to be served with
6+
the application itself. Two requirements are needed to create an
7+
offline PyScript are:
8+
9+
* Download and include PyScript core.
10+
* Download and include the Python interpreters used in your application.
11+
12+
## Get PyScript core
13+
14+
You have two choices:
15+
16+
1. **Build from source**. Clone the repository, install dependencies, then
17+
build and use the content found in the `./dist/` folder.
18+
2. **Grab the npm package**. For simplicity this is the method we currently
19+
recommend as the easiest to get started.
20+
21+
In the following instructions, we assume the existence of a folder called
22+
`pyscript-offline`. All the necessary files needed to use PyScript offline will
23+
eventually find their way in there.
24+
25+
In your computer's command line shell, create the `pyscript-offline` folder
26+
like this:
27+
28+
```sh
29+
mkdir -p pyscript-offline
30+
```
31+
32+
Now change into the newly created directory:
33+
34+
```sh
35+
cd pyscript-offline
36+
```
37+
38+
### PyScipt core from source
39+
40+
Build PyScript core by cloning the project repository and follow the
41+
instructions in our [developer guide](/developers)
42+
43+
Once completed, copy the `build` folder, that was been created by the build
44+
step, into your `pyscript-offline` folder.
45+
46+
### PyScript core from `npm`
47+
48+
Ensure you are in the `pyscript-offline` folder created earlier.
49+
50+
Create a `package.json` file. Even an empty one with just `{}` as content will
51+
suffice. This is needed to make sure our folder will include the local
52+
`npm_modules` folder instead of placing assets elsewhere. Our aim is to ensure
53+
everything is in the same place locally.
54+
55+
```sh
56+
# only if there is no package.json, create one
57+
echo '{}' > ./package.json
58+
```
59+
60+
Assuming you have
61+
[npm installed on your computer](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm),
62+
issue the following command in the `pyscript-offline` folder to install the
63+
PyScript core package.
64+
65+
```
66+
# install @pyscript/core
67+
npm i @pyscript/core
68+
```
69+
70+
Now the folder should contain a `node_module` folder in it, and we can copy the
71+
`dist` folder found within the `@pyscript/core` package wherever we like.
72+
73+
```sh
74+
# create a public folder to serve locally
75+
mkdir -p public
76+
77+
# move @pyscript/core dist into such folder
78+
cp -R ./node_modules/@pyscript/core/dist ./public/pyscript
79+
```
80+
81+
That's almost it!
82+
83+
## Set up your application
84+
85+
Simply create a `./public/index.html` file that loads the local PyScript:
86+
87+
```html
88+
<!DOCTYPE html>
89+
<html lang="en">
90+
<head>
91+
<meta charset="UTF-8">
92+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
93+
<title>PyScript Offline</title>
94+
<script type="module" src="/pyscript/core.js"></script>
95+
<link rel="stylesheet" href="/pyscript/core.css">
96+
</head>
97+
<body>
98+
<script type="mpy">
99+
from pyscript import document
100+
101+
document.body.append("Hello from PyScript")
102+
</script>
103+
</body>
104+
</html>
105+
```
106+
107+
Run this project directly (after being sure that `index.html` file is saved
108+
into the `public` folder):
109+
110+
```sh
111+
python3 -m http.server -d ./public/
112+
```
113+
114+
If you would like to test `worker` features, try instead:
115+
116+
```sh
117+
npx static-handler --coi ./public/
118+
```
119+
120+
## Download a local interpreter
121+
122+
PyScript officially supports *MicroPython* and *Pyodide* interpreters, so let's
123+
see how to get a local copy for each one of them.
124+
125+
### Local MicroPython
126+
127+
Similar to `@pyscript/core`, we can also install *MicroPython* from *npm*:
128+
129+
```sh
130+
npm i @micropython/micropython-webassembly-pyscript
131+
```
132+
133+
Our `node_modules` folder should contain a `@micropython` one and from there we
134+
can move relevant files into our `public` folder.
135+
136+
Let's be sure we have a target for that:
137+
138+
```sh
139+
# create a folder in our public space
140+
mkdir -p ./public/micropython
141+
142+
# copy related files into such folder
143+
cp ./node_modules/@micropython/micropython-webassembly-pyscript/micropython.* ./public/micropython/
144+
```
145+
146+
The folder should contain at least both `micropython.mjs` and
147+
`micropython.wasm` files. These are the files to use locally via a dedicated
148+
config.
149+
150+
```html
151+
<!DOCTYPE html>
152+
<html lang="en">
153+
<head>
154+
<meta charset="UTF-8">
155+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
156+
<title>PyScript Offline</title>
157+
<script type="module" src="/pyscript/core.js"></script>
158+
<link rel="stylesheet" href="/pyscript/core.css">
159+
</head>
160+
<body>
161+
<mpy-config>
162+
interpreter = "/micropython/micropython.mjs"
163+
</mpy-config>
164+
<script type="mpy">
165+
from pyscript import document
166+
167+
document.body.append("Hello from PyScript")
168+
</script>
169+
</body>
170+
</html>
171+
```
172+
173+
### Local Pyodide
174+
175+
Remember, Pyodide uses `micropip` to install third party packages. While the
176+
procedure for offline Pyodide is very similar to the one for MicroPython,
177+
if we want to use 3rd party packages we also need to have these available
178+
locally. We'll start simple and cover such packaging issues at the end.
179+
180+
```sh
181+
# locally install the pyodide module
182+
npm i pyodide
183+
184+
# create a folder in our public space
185+
mkdir -p ./public/pyodide
186+
187+
# move all necessary files into that folder
188+
cp ./node_modules/pyodide/pyodide* ./public/pyodide/
189+
cp ./node_modules/pyodide/python_stdlib.zip ./public/pyodide/
190+
```
191+
192+
Please **note** that the `pyodide-lock.json` file is needed, so please don't
193+
change that `cp` operation as all `pyodide*` files need to be moved.
194+
195+
At this point, all we need to do is to change the configuration on our *HTML*
196+
page to use *pyodide* instead:
197+
198+
```html
199+
<!DOCTYPE html>
200+
<html lang="en">
201+
<head>
202+
<meta charset="UTF-8">
203+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
204+
<title>PyScript Offline</title>
205+
<script type="module" src="/pyscript/core.js"></script>
206+
<link rel="stylesheet" href="/pyscript/core.css">
207+
</head>
208+
<body>
209+
<py-config>
210+
interpreter = "/pyodide/pyodide.mjs"
211+
</py-config>
212+
<script type="py">
213+
from pyscript import document
214+
215+
document.body.append("Hello from PyScript")
216+
</script>
217+
</body>
218+
</html>
219+
```
220+
221+
## Wrap up
222+
223+
That's basically it!
224+
225+
Disconnect from the internet, run the local server, and the page will still
226+
show that very same `Hello from PyScript` message.
227+
228+
## Local Pyodide packages
229+
230+
Finally, we need the ability to install Python packages from a local source
231+
when using Pyodide.
232+
233+
Put simply, we use the packages bundle from
234+
[pyodide releases](https://github.com/pyodide/pyodide/releases/tag/0.24.1).
235+
236+
!!! warning
237+
238+
This bundle is more than 200MB!
239+
240+
It contains each package that is required by Pyodide, and Pyodide will only
241+
load packages when needed.
242+
243+
Once downloaded and extracted (we're using version `0.24.1` in this example),
244+
we can simply copy the files and folders inside the `pyodide-0.24.1/pyodide/*`
245+
directory into our `./public/pyodide/*` folder.
246+
247+
Feel free to either skip or replace the content, or even directly move the
248+
`pyodide` folder inside our `./public/` one.
249+
250+
Now use any package available in via the Pyodide bundle.
251+
252+
For example:
253+
254+
```html
255+
<!DOCTYPE html>
256+
<html lang="en">
257+
<head>
258+
<meta charset="UTF-8">
259+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
260+
<title>PyScript Offline</title>
261+
<script type="module" src="/pyscript/core.js"></script>
262+
<link rel="stylesheet" href="/pyscript/core.css">
263+
</head>
264+
<body>
265+
<py-config>
266+
interpreter = "/pyodide/pyodide.mjs"
267+
packages = ["pandas"]
268+
</py-config>
269+
<script type="py">
270+
import pandas as pd
271+
x = pd.Series([1,2,3,4,5,6,7,8,9,10])
272+
273+
from pyscript import document
274+
document.body.append(str([i**2 for i in x]))
275+
</script>
276+
</body>
277+
</html>
278+
```
279+
280+
We should now be able to read `[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]` on the
281+
page *even* if we disconnect from the Internet.
282+
283+
That's it!

0 commit comments

Comments
 (0)