From 2919d64ff1bfdbb3acecbb730fc95c478b771000 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 28 Dec 2023 20:36:59 +0100 Subject: [PATCH] mpremote: Support HTTP(S) for fs cp SRC Enables copying directly from a http/https URL. Convenient for putting resources onto device without needing to make a local copy or using external download tool. Usecases include installing module that is not mpip installable, or adding resource files such as fonts/images/blobs Signed-off-by: Jon Nordby --- tools/mpremote/mpremote/transport_serial.py | 29 +++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tools/mpremote/mpremote/transport_serial.py b/tools/mpremote/mpremote/transport_serial.py index 3b4cd00078..15863bf5c0 100644 --- a/tools/mpremote/mpremote/transport_serial.py +++ b/tools/mpremote/mpremote/transport_serial.py @@ -35,7 +35,7 @@ # Once the API is stabilised, the idea is that mpremote can be used both # as a command line tool and a library for interacting with devices. -import ast, io, errno, os, re, struct, sys, time +import ast, io, errno, os, re, struct, sys, time, contextlib, urllib from collections import namedtuple from errno import EPERM from .console import VT_ENABLED @@ -58,6 +58,24 @@ def reraise_filesystem_error(e, info): raise +@contextlib.contextmanager +def open_file(src : str): + """Open a file. Can be either on local disk or a HTTP(S) URL""" + is_http = src.startswith('https://') or src.startswith('http://') + if is_http: + conn = urllib.request.urlopen(src, timeout=60.0) + size = int(conn.headers['content-length']) + file_like = conn + else: + size = os.path.getsize(src) + file_like = open(src, "rb") + + # attach a size() method, so consumers can know the file size + file_like.size = lambda : size + yield file_like + file_like.close() + + class SerialTransport(Transport): def __init__(self, device, baudrate=115200, wait=0, exclusive=True): self.in_raw_repl = False @@ -405,11 +423,12 @@ class SerialTransport(Transport): self.exec("f.close()") def fs_put(self, src, dest, chunk_size=256, progress_callback=None): - if progress_callback: - src_size = os.path.getsize(src) - written = 0 + self.exec("f=open('%s','wb')\nw=f.write" % dest) - with open(src, "rb") as f: + with open_file(src) as f: + if progress_callback: + src_size = f.size() + written = 0 while True: data = f.read(chunk_size) if not data: