2015-02-18 17:56:29 +00:00
|
|
|
def upip_import(mod, sub=None):
|
2015-02-10 03:36:13 +00:00
|
|
|
try:
|
2015-02-18 17:56:29 +00:00
|
|
|
mod_ = mod
|
|
|
|
if sub:
|
|
|
|
mod_ += "_" + sub
|
|
|
|
return __import__("upip_" + mod_)
|
2015-02-10 03:36:13 +00:00
|
|
|
except ImportError:
|
2015-02-18 17:56:29 +00:00
|
|
|
m = __import__(mod)
|
|
|
|
if sub:
|
|
|
|
return getattr(m, sub)
|
|
|
|
return m
|
2015-02-10 03:36:13 +00:00
|
|
|
|
|
|
|
sys = upip_import("sys")
|
|
|
|
os = upip_import("os")
|
2015-02-18 17:56:29 +00:00
|
|
|
#os.path = upip_import("os.path").path
|
|
|
|
ospath = upip_import("os", "path")
|
|
|
|
|
2015-02-10 03:36:13 +00:00
|
|
|
errno = upip_import("errno")
|
|
|
|
gzip = upip_import("gzip")
|
2015-02-08 07:15:07 +00:00
|
|
|
try:
|
2015-02-10 03:36:13 +00:00
|
|
|
tarfile = upip_import("utarfile")
|
2015-02-08 07:15:07 +00:00
|
|
|
except ImportError:
|
2015-02-10 03:36:13 +00:00
|
|
|
tarfile = upip_import("tarfile")
|
2015-02-08 07:15:07 +00:00
|
|
|
try:
|
2015-02-10 03:36:13 +00:00
|
|
|
json = upip_import("ujson")
|
2015-02-08 07:15:07 +00:00
|
|
|
except ImportError:
|
2015-02-10 03:36:13 +00:00
|
|
|
json = upip_import("json")
|
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
|
|
|
|
cleanup_files = [".pkg.tar"]
|
|
|
|
|
2015-06-24 22:38:15 +00:00
|
|
|
class NotFoundError(Exception):
|
|
|
|
pass
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
def save_file(fname, subf):
|
|
|
|
outf = open(fname, "wb")
|
|
|
|
while True:
|
|
|
|
buf = subf.read(1024)
|
|
|
|
if not buf:
|
|
|
|
break
|
|
|
|
outf.write(buf)
|
|
|
|
outf.close()
|
|
|
|
|
|
|
|
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:
|
|
|
|
try:
|
|
|
|
os.makedirs(outfname)
|
|
|
|
print("Created " + outfname)
|
|
|
|
except OSError as e:
|
|
|
|
if e.args[0] != errno.EEXIST:
|
|
|
|
raise
|
|
|
|
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
|
|
|
|
|
2015-09-03 14:26:58 +00:00
|
|
|
try:
|
|
|
|
import ussl
|
|
|
|
import usocket
|
|
|
|
def download(url, local_name):
|
|
|
|
proto, _, host, urlpath = url.split('/', 3)
|
|
|
|
ai = usocket.getaddrinfo(host, 443)
|
|
|
|
#print("Address infos:", ai)
|
|
|
|
addr = ai[0][4]
|
|
|
|
|
|
|
|
s = usocket.socket()
|
|
|
|
#print("Connect address:", addr)
|
|
|
|
s.connect(addr)
|
|
|
|
|
|
|
|
if proto == "https:":
|
|
|
|
s = ussl.wrap_socket(s)
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
l = s.readline()
|
|
|
|
if not l:
|
|
|
|
raise OSError()
|
|
|
|
if l == b'\r\n':
|
|
|
|
break
|
|
|
|
with open(local_name, "wb") as f:
|
|
|
|
while 1:
|
|
|
|
l = s.read(1024)
|
|
|
|
if not l:
|
|
|
|
break
|
|
|
|
f.write(l)
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
def download(url, local_name):
|
|
|
|
if debug:
|
|
|
|
print("wget -q %s -O %s" % (url, local_name))
|
|
|
|
rc = os.system("wget -q %s -O %s" % (url, local_name))
|
|
|
|
if local_name not in cleanup_files:
|
|
|
|
cleanup_files.append(local_name)
|
|
|
|
if rc == 8 * 256:
|
|
|
|
raise NotFoundError
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
|
|
|
|
def get_pkg_metadata(name):
|
2015-05-03 21:10:09 +00:00
|
|
|
download("https://pypi.python.org/pypi/%s/json" % name, ".pkg.json")
|
|
|
|
with open(".pkg.json") as f:
|
2015-02-08 07:15:07 +00:00
|
|
|
s = f.read()
|
|
|
|
return json.loads(s)
|
|
|
|
|
|
|
|
|
|
|
|
def fatal(msg):
|
|
|
|
print(msg)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
def gzdecompress(package_fname):
|
|
|
|
f = open(package_fname, "rb")
|
|
|
|
zipdata = f.read()
|
|
|
|
data = gzip.decompress(zipdata)
|
|
|
|
return data
|
|
|
|
|
|
|
|
def gzdecompress_(package_fname):
|
|
|
|
os.system("gzip -d -c %s > ungz" % package_fname)
|
|
|
|
with open("ungz", "rb") as f:
|
|
|
|
return f.read()
|
|
|
|
|
|
|
|
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-02-18 17:56:29 +00:00
|
|
|
package_fname = ospath.basename(package_url)
|
2015-02-08 07:15:07 +00:00
|
|
|
download(package_url, package_fname)
|
|
|
|
|
|
|
|
data = gzdecompress(package_fname)
|
|
|
|
|
2015-05-03 21:10:09 +00:00
|
|
|
f = open(".pkg.tar", "wb")
|
2015-02-08 07:15:07 +00:00
|
|
|
f.write(data)
|
|
|
|
f.close()
|
|
|
|
|
2015-05-03 21:10:09 +00:00
|
|
|
f = tarfile.TarFile(".pkg.tar")
|
2015-02-08 07:15:07 +00:00
|
|
|
return install_tar(f, 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("upip - Simple PyPI package manager for MicroPython")
|
|
|
|
print("Usage: micropython -m upip install <package>... | -r <requirements.txt>")
|
|
|
|
print("""\
|
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
|
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)
|
|
|
|
|
|
|
|
if install_path is None:
|
2015-10-17 17:10:37 +00:00
|
|
|
install_path = os.getenv("MICROPYPATH", DEFAULT_MICROPYPATH)
|
2015-02-08 07:15:07 +00:00
|
|
|
|
|
|
|
install_path = install_path.split(":", 1)[0]
|
|
|
|
|
|
|
|
install_path = expandhome(install_path)
|
|
|
|
|
|
|
|
if install_path[-1] != "/":
|
|
|
|
install_path += "/"
|
|
|
|
|
|
|
|
print("Installing to: " + install_path)
|
|
|
|
|
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()
|
|
|
|
|
2015-02-08 07:15:07 +00:00
|
|
|
# sets would be perfect here, but don't depend on them
|
|
|
|
installed = []
|
2015-06-24 22:38:15 +00:00
|
|
|
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)
|
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
|
|
|
|
|
|
|
main()
|