10000 mpremote: Add support for relative urls in package.json files. · glenn20/micropython@4c8d89c · GitHub
[go: up one dir, main page]

Skip to content

Commit 4c8d89c

Browse files
committed
mpremote: Add support for relative urls in package.json files.
URLs in package.json may be specified relative to the base URL of the package.json file. Relative URLs wil work for package.json files installed from the web as well as local file paths. Update `docs/reference/packages.rst` to add documentation for: - Installing packages from local filesystems (PR micropython#12476); and - Using relative URLs in the package.json file (PR micropython#12477); - Update the packaging example to encourage relative URLs as the default in package.json.
1 parent 8987b39 commit 4c8d89c

File tree

2 files changed

+72
-19
lines changed

2 files changed

+72
-19
lines changed

docs/reference/packages.rst

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,19 @@ The ``--target=path``, ``--no-mpy``, and ``--index`` arguments can be set::
9696
$ mpremote mip install --no-mpy pkgname
9797
$ mpremote mip install --index https://host/pi pkgname
9898

99+
100+
:term:`mpremote` can also install packages from files stored on the host's local
101+
filesystem::
102+
103+
$ mpremote mip install path/to/pkg.py
104+
$ mpremote mip install path/to/app/package.json
105+
$ mpremote mip install \\path\\to\\pkg.py
106+
107+
This is especially useful for testing packages during development and for
108+
installing packages from local clones of GitHub repositories. Note that URLs in
109+
``package.json`` files must use forward slashes ("/") as directory separators,
110+
even on Windows, so that they are compatible with installing from the web.
111+
99112
Installing packages manually
100113
----------------------------
101114

@@ -116,12 +129,25 @@ To write a "self-hosted" package that can be downloaded by ``mip`` or
116129
``mpremote``, you need a static webserver (or GitHub) to host either a
117130
single .py file, or a ``package.json`` file alongside your .py files.
118131

119-
A typical ``package.json`` for an example ``mlx90640`` library looks like::
132+
An example ``mlx90640`` library hosted on GitHub could be installed with::
133+
134+
$ mpremote mip install github:org/micropython-mlx90640
135+
136+
The layout for the package on GitHub might look like::
137+
138+
https://github.com/org/micropython-mlx90640/
139+
package.json
140+
mlx90640/
141+
__init__.py
142+
utils.py
143+
144+
The ``package.json`` specifies the location of files to be installed and other
145+
dependencies::
120146

121147
{
122148
"urls": [
123-
["mlx90640/__init__.py", "github:org/micropython-mlx90640/mlx90640/__init__.py"],
124-
["mlx90640/utils.py", "github:org/micropython-mlx90640/mlx90640/utils.py"]
149+
["mlx90640/__init__.py", "mlx90640/__init__.py"],
150+
["mlx90640/utils.py", "mlx90640/utils.py"]
125151
],
126152
"deps": [
127153
["collections-defaultdict", "latest"],
@@ -132,9 +158,20 @@ A typical ``package.json`` for an example ``mlx90640`` library looks like::
132158
"version": "0.2"
133159
}
134160

135-
This includes two files, hosted at a GitHub repo named
136-
``org/micropython-mlx90640``, which install into the ``mlx90640`` directory on
137-
the device. It depends on ``collections-defaultdict`` and ``os-path`` which will
161+
The ``urls`` list specifies the files to be installed according to::
162+
163+
"urls": [
164+
[destination_path, source_url]
165+
...
166+
167+
where ``destination_path`` is the location and name of the file to be installed
168+
on the device and ``source_url`` is the URL of the file to be installed. The
169+
source URL would usually be specified relative to the directory containing the
170+
``package.json`` file, but can also be an absolute URL, eg::
171+
172+
["mlx90640/utils.py", "github:org/micropython-mlx90640/mlx90640/utils.py"]
173+
174+
The package depends on ``collections-defaultdict`` and ``os-path`` which will
138175
be installed automatically from the :term:`micropython-lib`. The third
139176
dependency installs the content as defined by the ``package.json`` file of the
140177
``main`` branch of the GitHub repo ``org/micropython-additions``.

tools/mpremote/mpremote/mip.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import json
88
import tempfile
99
import os
10+
import os.path
1011

1112
from .commands import CommandError, show_progress_bar
1213

@@ -64,22 +65,33 @@ def _rewrite_url(url, branch=None):
6465

6566

6667
def _download_file(transport, url, dest):
67-
try:
68-
with urllib.request.urlopen(url) as src:
69-
data = src.read()
70-
print("Installing:", dest)
71-
_ensure_path_exists(transport, dest)
72-
transport.fs_writefile(dest, data, progress_callback=show_progress_bar)
73-
except urllib.error.HTTPError as e:
74-
if e.status == 404:
75-
raise CommandError(f"File not found: {url}")
76-
else:
77-
raise CommandError(f"Error {e.status} requesting {url}")
78-
except urllib.error.URLError as e:
79-
raise CommandError(f"{e.reason} requesting {url}")
68+
if url.startswith(allowed_mip_url_prefixes):
69+
try:
70+
with urllib.request.urlopen(url) as src:
71+
data = src.read()
72+
except urllib.error.HTTPError as e:
73+
if e.status == 404:
74+
raise CommandError(f"File not found: {url}")
75+
else:
76+
raise CommandError(f"Error {e.status} requesting {url}")
77+
except urllib.error.URLError as e:
78+
raise CommandError(f"{e.reason} requesting {url}")
79+
else:
80+
if "\\" in url:
81+
raise CommandError(f'Use "/" instead of "\\" in file URLs: {url!r}\n')
82+
try:
83+
with open(url, "rb") as f:
84+
data = f.read()
85+
except OSError as e:
86+
raise CommandError(f"{e.strerror} opening {url}")
87+
88+
print("Installing:", dest)
89+
_ensure_path_exists(transport, dest)
90+
transport.fs_writefile(dest, data, progress_callback=show_progress_bar)
8091

8192

8293
def _install_json(transport, package_json_url, index, target, version, mpy):
94+
base_url = ""
8395
if package_json_url.startswith(allowed_mip_url_prefixes):
8496
try:
8597
with urllib.request.urlopen(_rewrite_url(package_json_url, version)) as response:
@@ -91,12 +103,14 @@ def _install_json(transport, package_json_url, index, target, version, mpy):
91103
raise CommandError(f"Error {e.status} requesting {package_json_url}")
92104
except urllib.error.URLError as e:
93105
raise CommandError(f"{e.reason} requesting {package_json_url}")
106+
base_url = package_json_url.rpartition("/")[0]
94107
elif package_json_url.endswith(".json"):
95108
try:
96109
with open(package_json_url, "r") as f:
97110
package_json = json.load(f)
98111
except OSError:
99112
raise CommandError(f"Error opening {package_json_url}")
113+
base_url = os.path.dirname(package_json_url)
100114
else:
101115
raise CommandError(f"Invalid url for package: {package_json_url}")
102116
for target_path, short_hash in package_json.get("hashes", ()):
@@ -105,6 +119,8 @@ def _install_json(transport, package_json_url, index, target, version, mpy):
105119
_download_file(transport, file_url, fs_target_path)
106120
for target_path, url in package_json.get("urls", ()):
107121
fs_target_path = target + "/" + target_path
122+
if base_url and not url.startswith(allowed_mip_url_prefixes):
123+
url = f"{base_url}/{url}" # Relative URLs
108124
_download_file(transport, _rewrite_url(url, version), fs_target_path)
109125
for dep, dep_version in package_json.get("deps", ()):
110126
_install_package(transport, dep, index, target, dep_version, mpy)

0 commit comments

Comments
 (0)
0