Skip to content

Commit f8f0fd1

Browse files
committed
initial shapip installs from https://annejan.com/shabadge 'repo'
1 parent fe22297 commit f8f0fd1

File tree

1 file changed

+307
-0
lines changed

1 file changed

+307
-0
lines changed

esp32/modules/shapip.py

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
import sys
2+
import gc
3+
import uos as os
4+
import uerrno as errno
5+
import ujson as json
6+
import uzlib
7+
import upip_utarfile as tarfile
8+
gc.collect()
9+
10+
11+
debug = False
12+
install_path = None
13+
cleanup_files = []
14+
gzdict_sz = 16 + 15
15+
16+
file_buf = bytearray(512)
17+
18+
class NotFoundError(Exception):
19+
pass
20+
21+
def op_split(path):
22+
if path == "":
23+
return ("", "")
24+
r = path.rsplit("/", 1)
25+
if len(r) == 1:
26+
return ("", path)
27+
head = r[0]
28+
if not head:
29+
head = "/"
30+
return (head, r[1])
31+
32+
def op_basename(path):
33+
return op_split(path)[1]
34+
35+
# Expects *file* name
36+
def _makedirs(name, mode=0o777):
37+
ret = False
38+
s = ""
39+
comps = name.rstrip("/").split("/")[:-1]
40+
if comps[0] == "":
41+
s = "/"
42+
for c in comps:
43+
if s and s[-1] != "/":
44+
s += "/"
45+
s += c
46+
try:
47+
os.mkdir(s)
48+
ret = True
49+
except OSError as e:
50+
if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR:
51+
raise
52+
ret = False
53+
return ret
54+
55+
56+
def save_file(fname, subf):
57+
global file_buf
58+
with open(fname, "wb") as outf:
59+
while True:
60+
sz = subf.readinto(file_buf)
61+
if not sz:
62+
break
63+
outf.write(file_buf, sz)
64+
65+
def install_tar(f, prefix):
66+
meta = {}
67+
for info in f:
68+
#print(info)
69+
fname = info.name
70+
try:
71+
fname = fname[fname.index("/") + 1:]
72+
except ValueError:
73+
fname = ""
74+
75+
save = True
76+
for p in ("setup.", "PKG-INFO", "README"):
77+
#print(fname, p)
78+
if fname.startswith(p) or ".egg-info" in fname:
79+
if fname.endswith("/requires.txt"):
80+
meta["deps"] = f.extractfile(info).read()
81+
save = False
82+
if debug:
83+
print("Skipping", fname)
84+
break
85+
86+
if save:
87+
outfname = prefix + fname
88+
if info.type != tarfile.DIRTYPE:
89+
if debug:
90+
print("Extracting " + outfname)
91+
_makedirs(outfname)
92+
subf = f.extractfile(info)
93+
save_file(outfname, subf)
94+
return meta
95+
96+
def expandhome(s):
97+
if "~/" in s:
98+
h = os.getenv("HOME")
99+
s = s.replace("~/", h + "/")
100+
return s
101+
102+
import ussl
103+
import usocket
104+
warn_ussl = True
105+
def url_open(url):
106+
global warn_ussl
107+
108+
if debug:
109+
print(url)
110+
111+
proto, _, host, urlpath = url.split('/', 3)
112+
try:
113+
ai = usocket.getaddrinfo(host, 443)
114+
except OSError as e:
115+
fatal("Unable to resolve %s (no Internet?)" % host, e)
116+
#print("Address infos:", ai)
117+
addr = ai[0][4]
118+
119+
s = usocket.socket(ai[0][0])
120+
try:
121+
#print("Connect address:", addr)
122+
s.connect(addr)
123+
124+
if proto == "https:":
125+
s = ussl.wrap_socket(s)
126+
if warn_ussl:
127+
print("Warning: %s SSL certificate is not validated" % host)
128+
warn_ussl = False
129+
130+
# MicroPython rawsocket module supports file interface directly
131+
s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host))
132+
l = s.readline()
133+
protover, status, msg = l.split(None, 2)
134+
if status != b"200":
135+
if status == b"404" or status == b"301":
136+
raise NotFoundError("Package not found")
137+
raise ValueError(status)
138+
while 1:
139+
l = s.readline()
140+
if not l:
141+
raise ValueError("Unexpected EOF in HTTP headers")
142+
if l == b'\r\n':
143+
break
144+
except Exception as e:
145+
s.close()
146+
raise e
147+
148+
return s
149+
150+
151+
def get_pkg_metadata(name):
152+
f = url_open("https://annejan.com/shabadge/%s/json" % name)
153+
try:
154+
return json.load(f)
155+
finally:
156+
f.close()
157+
158+
159+
def fatal(msg, exc=None):
160+
print("Error:", msg)
161+
if exc and debug:
162+
raise exc
163+
sys.exit(1)
164+
165+
def install_pkg(pkg_spec, install_path):
166+
data = get_pkg_metadata(pkg_spec)
167+
168+
latest_ver = data["info"]["version"]
169+
packages = data["releases"][latest_ver]
170+
del data
171+
gc.collect()
172+
assert len(packages) == 1
173+
package_url = packages[0]["url"]
174+
print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url))
175+
package_fname = op_basename(package_url)
176+
f1 = url_open(package_url)
177+
try:
178+
f2 = uzlib.DecompIO(f1, gzdict_sz)
179+
f3 = tarfile.TarFile(fileobj=f2)
180+
meta = install_tar(f3, install_path)
181+
finally:
182+
f1.close()
183+
del f3
184+
del f2
185+
gc.collect()
186+
return meta
187+
188+
def install(to_install, install_path=None):
189+
# Calculate gzip dictionary size to use
190+
global gzdict_sz
191+
sz = gc.mem_free() + gc.mem_alloc()
192+
if sz <= 65536:
193+
gzdict_sz = 16 + 12
194+
195+
if install_path is None:
196+
install_path = get_install_path()
197+
if install_path[-1] != "/":
198+
install_path += "/"
199+
if not isinstance(to_install, list):
200+
to_install = [to_install]
201+
print("Installing to: " + install_path)
202+
# sets would be perfect here, but don't depend on them
203+
installed = []
204+
try:
205+
while to_install:
206+
if debug:
207+
print("Queue:", to_install)
208+
pkg_spec = to_install.pop(0)
209+
if pkg_spec in installed:
210+
continue
211+
meta = install_pkg(pkg_spec, install_path)
212+
installed.append(pkg_spec)
213+
if debug:
214+
print(meta)
215+
deps = meta.get("deps", "").rstrip()
216+
if deps:
217+
deps = deps.decode("utf-8").split("\n")
218+
to_install.extend(deps)
219+
except Exception as e:
220+
print("Error installing '{}': {}, packages may be partially installed".format(
221+
pkg_spec, e),
222+
file=sys.stderr)
223+
224+
def get_install_path():
225+
global install_path
226+
if install_path is None:
227+
# sys.path[0] is current module's path
228+
install_path = sys.path[1]
229+
install_path = expandhome(install_path)
230+
return install_path
231+
232+
def cleanup():
233+
for fname in cleanup_files:
234+
try:
235+
os.unlink(fname)
236+
except OSError:
237+
print("Warning: Cannot delete " + fname)
238+
239+
def help():
240+
print("""\
241+
shapip - Clone of the Simple PyPI package manager for MicroPython
242+
Usage: micropython -m shapip install [-p <path>] <package>... | -r <requirements.txt>
243+
import shapip; shapip.install(package_or_list, [<path>])
244+
245+
If <path> is not given, packages will be installed into sys.path[1]
246+
(can be set from MICROPYPATH environment variable, if current system
247+
supports that).""")
248+
print("Current value of sys.path[1]:", sys.path[1])
249+
print("""\
250+
251+
Note: only MicroPython packages are supported for installation,
252+
shapip, like upip does not support arbitrary code in setup.py.
253+
""")
254+
255+
def main():
256+
global debug
257+
global install_path
258+
install_path = None
259+
260+
if len(sys.argv) < 2 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
261+
help()
262+
return
263+
264+
if sys.argv[1] != "install":
265+
fatal("Only 'install' command supported")
266+
267+
to_install = []
268+
269+
i = 2
270+
while i < len(sys.argv) and sys.argv[i][0] == "-":
271+
opt = sys.argv[i]
272+
i += 1
273+
if opt == "-h" or opt == "--help":
274+
help()
275+
return
276+
elif opt == "-p":
277+
install_path = sys.argv[i]
278+
i += 1
279+
elif opt == "-r":
280+
list_file = sys.argv[i]
281+
i += 1
282+
with open(list_file) as f:
283+
while True:
284+
l = f.readline()
285+
if not l:
286+
break
287+
if l[0] == "#":
288+
continue
289+
to_install.append(l.rstrip())
290+
elif opt == "--debug":
291+
debug = True
292+
else:
293+
fatal("Unknown/unsupported option: " + opt)
294+
295+
to_install.extend(sys.argv[i:])
296+
if not to_install:
297+
help()
298+
return
299+
300+
install(to_install)
301+
302+
if not debug:
303+
cleanup()
304+
305+
306+
if __name__ == "__main__":
307+
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