Skip to content

Commit 3cccc69

Browse files
committed
upip: Initial steps towards self-hosted package manager.
Already capable of installing packages from command line, resolving dependencies recursively. Downloading is handled via wget due to SSL, so currently this is not self-hosted.
1 parent e8813f3 commit 3cccc69

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed

upip/upip.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import sys
2+
import os
3+
import os.path
4+
import errno
5+
import gzip
6+
try:
7+
import utarfile as tarfile
8+
except ImportError:
9+
import tarfile
10+
try:
11+
import ujson as json
12+
except ImportError:
13+
import json
14+
15+
16+
DEFAULT_MICROPYPATH = "~/.micropython/lib:/usr/lib/micropython"
17+
18+
def save_file(fname, subf):
19+
outf = open(fname, "wb")
20+
while True:
21+
buf = subf.read(1024)
22+
if not buf:
23+
break
24+
outf.write(buf)
25+
outf.close()
26+
27+
def install_tar(f, prefix):
28+
meta = {}
29+
for info in f:
30+
#print(info)
31+
fname = info.name
32+
try:
33+
fname = fname[fname.index("/") + 1:]
34+
except ValueError:
35+
fname = ""
36+
37+
save = True
38+
for p in ("setup.", "PKG-INFO"):
39+
#print(fname, p)
40+
if fname.startswith(p) or ".egg-info" in fname:
41+
if fname.endswith("/requires.txt"):
42+
meta["deps"] = f.extractfile(info).read()
43+
save = False
44+
print("Skipping", fname)
45+
break
46+
47+
if save:
48+
outfname = prefix + fname
49+
if info.type == tarfile.DIRTYPE:
50+
try:
51+
os.makedirs(outfname)
52+
print("Created " + outfname)
53+
except OSError as e:
54+
if e.args[0] != errno.EEXIST:
55+
raise
56+
else:
57+
print("Extracting " + outfname)
58+
subf = f.extractfile(info)
59+
save_file(outfname, subf)
60+
return meta
61+
62+
def expandhome(s):
63+
h = os.getenv("HOME")
64+
s = s.replace("~/", h + "/")
65+
return s
66+
67+
def download(url, local_name):
68+
os.system("wget -q %s -O %s" % (url, local_name))
69+
70+
def get_pkg_metadata(name):
71+
download("https://pypi.python.org/pypi/%s/json" % name, "pkg.json")
72+
with open("pkg.json") as f:
73+
s = f.read()
74+
return json.loads(s)
75+
76+
77+
def fatal(msg):
78+
print(msg)
79+
sys.exit(1)
80+
81+
def gzdecompress(package_fname):
82+
f = open(package_fname, "rb")
83+
zipdata = f.read()
84+
data = gzip.decompress(zipdata)
85+
return data
86+
87+
def gzdecompress_(package_fname):
88+
os.system("gzip -d -c %s > ungz" % package_fname)
89+
with open("ungz", "rb") as f:
90+
return f.read()
91+
92+
def install_pkg(pkg_spec, install_path):
93+
data = get_pkg_metadata(pkg_spec)
94+
95+
latest_ver = data["info"]["version"]
96+
print("Installing %s %s" % (pkg_spec, latest_ver))
97+
packages = data["releases"][latest_ver]
98+
assert len(packages) == 1
99+
package_url = packages[0]["url"]
100+
package_fname = os.path.basename(package_url)
101+
print(package_url)
102+
download(package_url, package_fname)
103+
104+
data = gzdecompress(package_fname)
105+
106+
f = open("pkg.tar", "wb")
107+
f.write(data)
108+
f.close()
109+
110+
f = tarfile.TarFile("pkg.tar")
111+
return install_tar(f, install_path)
112+
113+
def main():
114+
install_path = None
115+
116+
if sys.argv[1] != "install":
117+
fatal("Only 'install' command supported")
118+
119+
i = 2
120+
while sys.argv[i][0] == "-":
121+
opt = sys.argv[i][1]
122+
i += 1
123+
if opt == "p":
124+
install_path = sys.argv[i]
125+
i += 1
126+
else:
127+
fatal("Unknown/unsupported option: " + opt)
128+
129+
if install_path is None:
130+
install_path = DEFAULT_MICROPYPATH
131+
132+
install_path = install_path.split(":", 1)[0]
133+
134+
install_path = expandhome(install_path)
135+
136+
if install_path[-1] != "/":
137+
install_path += "/"
138+
139+
print("Installing to: " + install_path)
140+
141+
to_install = sys.argv[i:]
142+
# sets would be perfect here, but don't depend on them
143+
installed = []
144+
while to_install:
145+
print("Queue:", to_install)
146+
pkg_spec = to_install.pop(0)
147+
if pkg_spec in installed:
148+
continue
149+
meta = install_pkg(pkg_spec, install_path)
150+
installed.append(pkg_spec)
151+
print(meta)
152+
deps = meta.get("deps", "").rstrip()
153+
if deps:
154+
deps = deps.decode("utf-8").split("\n")
155+
to_install.extend(deps)
156+
157+
158+
main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy