2016-09-30 07:28:29 +00:00
|
|
|
import sys
|
2015-12-11 22:17:42 +00:00
|
|
|
import uos as os
|
2016-05-27 23:56:11 +00:00
|
|
|
import uerrno as errno
|
2016-09-30 07:28:29 +00:00
|
|
|
import ujson as json
|
2016-09-28 16:00:33 +00:00
|
|
|
import uzlib
|
2016-09-30 07:28:29 +00:00
|
|
|
import upip_utarfile as tarfile
|
2015-02-08 07:15:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_MICROPYPATH = "~/.micropython/lib:/usr/lib/micropython"
|
|
|
|
|
2015-05-03 21:10:09 +00:00
|
|
|
debug = False
|
2016-10-07 21:12:17 +00:00
|
|
|
install_path = None
|
2016-09-28 16:00:33 +00:00
|
|
|
cleanup_files = []
|
2015-05-03 21:10:09 +00:00
|
|
|
|
2016-10-05 21:50:55 +00:00
|
|
|
file_buf = bytearray(512)
|
|
|
|
|
2015-06-24 22:38:15 +00:00
|
|
|
class NotFoundError(Exception):
|
|
|
|
pass
|
|
|
|
|
2015-11-13 22:02:10 +00:00
|
|
|
def op_split(path):
|
|
|
|
if path == "":
|
|
|
|
return ("", "")
|
|
|
|
r = path.rsplit("/", 1)
|
|
|
|
if len(r) == 1:
|
|
|
|
return ("", path)
|
|
|
|
head = r[0]
|
|
|
|
if not head:
|
|
|
|
head = "/"
|
|
|
|
return (head, r[1])
|
|
|
|
|
|
|
|
def op_basename(path):
|
|
|
|
return op_split(path)[1]
|
|
|
|
|
|
|
|
def _makedirs(name, mode=0o777):
|
|
|
|
ret = False
|
|
|
|
s = ""
|
|
|
|
for c in name.rstrip("/").split("/"):
|
|
|
|
s += c + "/"
|
|
|
|
try:
|
|
|
|
os.mkdir(s)
|
|
|
|
ret = True
|
|
|
|
except OSError as e:
|
2015-11-14 19:23:25 +00:00
|
|
|
if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR:
|
2015-11-13 22:02:10 +00:00
|
|
|
raise
|
|
|
|
ret = False
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
def save_file(fname, subf):
|
2016-10-05 21:50:55 +00:00
|
|
|
global file_buf
|
|
|
|
with open(fname, "wb") as outf:
|
|
|
|
while True:
|
|
|
|
sz = subf.readinto(file_buf)
|
|
|
|
if not sz:
|
|
|
|
break
|
|
|
|
outf.write(file_buf, sz)
|
2015-02-08 07:15:07 +00:00
|
|
|
|
|
|
|
def install_tar(f, prefix):
|
|
|
|
meta = {}
|
|
|
|
for info in f:
|
|
|
|
#print(info)
|
|
|
|
fname = info.name
|
|
|
|
try:
|
|
|
|
fname = fname[fname.index("/") + 1:]
|
|
|
|
except ValueError:
|
|
|
|
fname = ""
|
|
|
|
|
|
|
|
save = True
|
2015-05-03 21:10:09 +00:00
|
|
|
for p in ("setup.", "PKG-INFO", "README"):
|
2015-02-08 07:15:07 +00:00
|
|
|
#print(fname, p)
|
|
|
|
if fname.startswith(p) or ".egg-info" in fname:
|
|
|
|
if fname.endswith("/requires.txt"):
|
|
|
|
meta["deps"] = f.extractfile(info).read()
|
|
|
|
save = False
|
2015-05-03 21:10:09 +00:00
|
|
|
if debug:
|
|
|
|
print("Skipping", fname)
|
2015-02-08 07:15:07 +00:00
|
|
|
break
|
|
|
|
|
|
|
|
if save:
|
|
|
|
outfname = prefix + fname
|
|
|
|
if info.type == tarfile.DIRTYPE:
|
2015-11-13 22:02:10 +00:00
|
|
|
if _makedirs(outfname):
|
2015-02-08 07:15:07 +00:00
|
|
|
print("Created " + outfname)
|
|
|
|
else:
|
2015-05-03 21:10:09 +00:00
|
|
|
if debug:
|
|
|
|
print("Extracting " + outfname)
|
2015-02-08 07:15:07 +00:00
|
|
|
subf = f.extractfile(info)
|
|
|
|
save_file(outfname, subf)
|
|
|
|
return meta
|
|
|
|
|
|
|
|
def expandhome(s):
|
|
|
|
h = os.getenv("HOME")
|
|
|
|
s = s.replace("~/", h + "/")
|
|
|
|
return s
|
|
|
|
|
2016-10-02 00:32:07 +00:00
|
|
|
import ussl
|
|
|
|
import usocket
|
|
|
|
warn_ussl = True
|
|
|
|
def url_open(url):
|
|
|
|
global warn_ussl
|
|
|
|
proto, _, host, urlpath = url.split('/', 3)
|
|
|
|
ai = usocket.getaddrinfo(host, 443)
|
|
|
|
#print("Address infos:", ai)
|
|
|
|
addr = ai[0][4]
|
|
|
|
|
|
|
|
s = usocket.socket(ai[0][0])
|
|
|
|
#print("Connect address:", addr)
|
|
|
|
s.connect(addr)
|
|
|
|
|
|
|
|
if proto == "https:":
|
|
|
|
s = ussl.wrap_socket(s)
|
|
|
|
if warn_ussl:
|
|
|
|
print("Warning: %s SSL certificate is not validated" % host)
|
|
|
|
warn_ussl = False
|
|
|
|
|
|
|
|
# MicroPython rawsocket module supports file interface directly
|
|
|
|
s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host))
|
|
|
|
l = s.readline()
|
|
|
|
protover, status, msg = l.split(None, 2)
|
|
|
|
if status != b"200":
|
|
|
|
raise OSError()
|
|
|
|
while 1:
|
2015-09-03 14:26:58 +00:00
|
|
|
l = s.readline()
|
2016-10-02 00:32:07 +00:00
|
|
|
if not l:
|
2015-09-03 14:26:58 +00:00
|
|
|
raise OSError()
|
2016-10-02 00:32:07 +00:00
|
|
|
if l == b'\r\n':
|
|
|
|
break
|
|
|
|
|
|
|
|
return s
|
2015-09-03 14:26:58 +00:00
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
|
|
|
|
def get_pkg_metadata(name):
|
2016-09-28 16:00:33 +00:00
|
|
|
f = url_open("https://pypi.python.org/pypi/%s/json" % name)
|
|
|
|
s = f.read()
|
|
|
|
f.close()
|
2015-02-08 07:15:07 +00:00
|
|
|
return json.loads(s)
|
|
|
|
|
|
|
|
|
|
|
|
def fatal(msg):
|
|
|
|
print(msg)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
def install_pkg(pkg_spec, install_path):
|
|
|
|
data = get_pkg_metadata(pkg_spec)
|
|
|
|
|
|
|
|
latest_ver = data["info"]["version"]
|
|
|
|
packages = data["releases"][latest_ver]
|
|
|
|
assert len(packages) == 1
|
|
|
|
package_url = packages[0]["url"]
|
2015-05-03 21:10:09 +00:00
|
|
|
print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url))
|
2015-11-13 22:02:10 +00:00
|
|
|
package_fname = op_basename(package_url)
|
2016-09-28 16:00:33 +00:00
|
|
|
f1 = url_open(package_url)
|
|
|
|
f2 = uzlib.DecompIO(f1, 16 + 15)
|
|
|
|
f3 = tarfile.TarFile(fileobj=f2)
|
|
|
|
meta = install_tar(f3, install_path)
|
|
|
|
f1.close()
|
|
|
|
return meta
|
2015-02-08 07:15:07 +00:00
|
|
|
|
2016-10-07 21:12:17 +00:00
|
|
|
def install(to_install, install_path=None):
|
|
|
|
if install_path is None:
|
|
|
|
install_path = get_install_path()
|
|
|
|
if install_path[-1] != "/":
|
|
|
|
install_path += "/"
|
|
|
|
print("Installing to: " + install_path)
|
2016-10-06 22:41:19 +00:00
|
|
|
# sets would be perfect here, but don't depend on them
|
|
|
|
installed = []
|
|
|
|
try:
|
|
|
|
while to_install:
|
|
|
|
if debug:
|
|
|
|
print("Queue:", to_install)
|
|
|
|
pkg_spec = to_install.pop(0)
|
|
|
|
if pkg_spec in installed:
|
|
|
|
continue
|
|
|
|
meta = install_pkg(pkg_spec, install_path)
|
|
|
|
installed.append(pkg_spec)
|
|
|
|
if debug:
|
|
|
|
print(meta)
|
|
|
|
deps = meta.get("deps", "").rstrip()
|
|
|
|
if deps:
|
|
|
|
deps = deps.decode("utf-8").split("\n")
|
|
|
|
to_install.extend(deps)
|
|
|
|
except NotFoundError:
|
|
|
|
print("Error: cannot find '%s' package (or server error), packages may be partially installed" \
|
|
|
|
% pkg_spec, file=sys.stderr)
|
|
|
|
|
2016-10-07 21:12:17 +00:00
|
|
|
def get_install_path():
|
|
|
|
global install_path
|
|
|
|
if install_path is None:
|
|
|
|
if hasattr(os, "getenv"):
|
|
|
|
install_path = os.getenv("MICROPYPATH")
|
|
|
|
if install_path is None:
|
|
|
|
# sys.path[0] is current module's path
|
|
|
|
install_path = sys.path[1]
|
|
|
|
install_path = install_path.split(":", 1)[0]
|
|
|
|
install_path = expandhome(install_path)
|
|
|
|
return install_path
|
|
|
|
|
2015-05-03 21:10:09 +00:00
|
|
|
def cleanup():
|
|
|
|
for fname in cleanup_files:
|
|
|
|
try:
|
|
|
|
os.unlink(fname)
|
|
|
|
except OSError:
|
|
|
|
print("Warning: Cannot delete " + fname)
|
|
|
|
|
2015-02-13 03:07:07 +00:00
|
|
|
def help():
|
|
|
|
print("""\
|
2016-05-27 23:56:31 +00:00
|
|
|
upip - Simple PyPI package manager for MicroPython
|
|
|
|
Usage: micropython -m upip install [-p <path>] <package>... | -r <requirements.txt>
|
|
|
|
|
|
|
|
If -p is not given, packages will be installed to first path component of
|
|
|
|
MICROPYPATH, or to ~/.micropython/lib/ by default.
|
2015-05-08 21:25:11 +00:00
|
|
|
Note: only MicroPython packages (usually, micropython-*) are supported for
|
|
|
|
installation, upip does not support arbitrary code in setup.py.""")
|
2015-02-13 03:07:07 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
def main():
|
2015-05-03 21:10:09 +00:00
|
|
|
global debug
|
2016-10-07 21:12:17 +00:00
|
|
|
global install_path
|
2015-02-08 07:15:07 +00:00
|
|
|
install_path = None
|
|
|
|
|
2015-02-13 03:07:07 +00:00
|
|
|
if len(sys.argv) < 2 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
|
|
|
|
help()
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
if sys.argv[1] != "install":
|
|
|
|
fatal("Only 'install' command supported")
|
|
|
|
|
2015-02-09 01:57:31 +00:00
|
|
|
to_install = []
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
i = 2
|
2015-02-09 01:57:31 +00:00
|
|
|
while i < len(sys.argv) and sys.argv[i][0] == "-":
|
2015-02-13 03:07:07 +00:00
|
|
|
opt = sys.argv[i]
|
2015-02-08 07:15:07 +00:00
|
|
|
i += 1
|
2015-02-13 03:07:07 +00:00
|
|
|
if opt == "-h" or opt == "--help":
|
|
|
|
help()
|
|
|
|
elif opt == "-p":
|
2015-02-08 07:15:07 +00:00
|
|
|
install_path = sys.argv[i]
|
|
|
|
i += 1
|
2015-02-13 03:07:07 +00:00
|
|
|
elif opt == "-r":
|
2015-02-09 01:57:31 +00:00
|
|
|
list_file = sys.argv[i]
|
|
|
|
i += 1
|
|
|
|
with open(list_file) as f:
|
|
|
|
while True:
|
|
|
|
l = f.readline()
|
|
|
|
if not l:
|
|
|
|
break
|
|
|
|
to_install.append(l.rstrip())
|
2015-05-03 21:10:09 +00:00
|
|
|
elif opt == "--debug":
|
|
|
|
debug = True
|
2015-02-08 07:15:07 +00:00
|
|
|
else:
|
|
|
|
fatal("Unknown/unsupported option: " + opt)
|
|
|
|
|
2015-02-09 01:57:31 +00:00
|
|
|
to_install.extend(sys.argv[i:])
|
2015-02-13 03:07:07 +00:00
|
|
|
if not to_install:
|
|
|
|
help()
|
|
|
|
|
2016-10-07 21:12:17 +00:00
|
|
|
install(to_install)
|
2015-02-08 07:15:07 +00:00
|
|
|
|
2015-05-03 21:10:09 +00:00
|
|
|
if not debug:
|
|
|
|
cleanup()
|
2015-02-08 07:15:07 +00:00
|
|
|
|
2016-10-06 22:27:50 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|