summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gen-cpydiff.py224
-rwxr-xr-xtools/mpy-tool.py23
-rwxr-xr-xtools/mpy_cross_all.py38
-rwxr-xr-xtools/pyboard.py91
-rwxr-xr-xtools/tinytest-codegen.py3
-rw-r--r--tools/upip.py89
6 files changed, 418 insertions, 50 deletions
diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py
new file mode 100644
index 000000000..4b273d97f
--- /dev/null
+++ b/tools/gen-cpydiff.py
@@ -0,0 +1,224 @@
+# This file is part of the MicroPython project, http://micropython.org/
+#
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Rami Ali
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+""" gen-cpydiff generates documentation which outlines operations that differ between MicroPython
+ and CPython. This script is called by the docs Makefile for html and Latex and may be run
+ manually using the command make gen-cpydiff. """
+
+import os
+import errno
+import subprocess
+import time
+import re
+from collections import namedtuple
+
+# Micropython supports syntax of CPython 3.4 with some features from 3.5, and
+# such version should be used to test for differences. If your default python3
+# executable is of lower version, you can point MICROPY_CPYTHON3 environment var
+# to the correct executable.
+if os.name == 'nt':
+ CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe')
+ MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../windows/micropython.exe')
+else:
+ CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3')
+ MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', '../unix/micropython')
+
+TESTPATH = '../tests/cpydiff/'
+DOCPATH = '../docs/genrst/'
+INDEXTEMPLATE = '../docs/differences/index_template.txt'
+INDEX = 'index.rst'
+
+HEADER = '.. This document was generated by tools/gen-cpydiff.py\n\n'
+UIMPORTLIST = {'struct', 'collections', 'json'}
+CLASSMAP = {'Core': 'Core Language', 'Types': 'Builtin Types'}
+INDEXPRIORITY = ['syntax', 'core_language', 'builtin_types', 'modules']
+RSTCHARS = ['=', '-', '~', '`', ':']
+SPLIT = '"""\n|categories: |description: |cause: |workaround: '
+TAB = ' '
+
+Output = namedtuple('output', ['name', 'class_', 'desc', 'cause', 'workaround', 'code',
+ 'output_cpy', 'output_upy', 'status'])
+
+def readfiles():
+ """ Reads test files """
+ tests = list(filter(lambda x: x.endswith('.py'), os.listdir(TESTPATH)))
+ tests.sort()
+ files = []
+
+ for test in tests:
+ text = open(TESTPATH + test, 'r').read()
+
+ try:
+ class_, desc, cause, workaround, code = [x.rstrip() for x in \
+ list(filter(None, re.split(SPLIT, text)))]
+ output = Output(test, class_, desc, cause, workaround, code, '', '', '')
+ files.append(output)
+ except IndexError:
+ print('Incorrect format in file ' + TESTPATH + test)
+
+ return files
+
+def uimports(code):
+ """ converts CPython module names into MicroPython equivalents """
+ for uimport in UIMPORTLIST:
+ uimport = bytes(uimport, 'utf8')
+ code = code.replace(uimport, b'u' + uimport)
+ return code
+
+def run_tests(tests):
+ """ executes all tests """
+ results = []
+ for test in tests:
+ with open(TESTPATH + test.name, 'rb') as f:
+ input_cpy = f.read()
+ input_upy = uimports(input_cpy)
+
+ process = subprocess.Popen(CPYTHON3, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ output_cpy = [com.decode('utf8') for com in process.communicate(input_cpy)]
+
+ process = subprocess.Popen(MICROPYTHON, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+ output_upy = [com.decode('utf8') for com in process.communicate(input_upy)]
+
+ if output_cpy[0] == output_upy[0] and output_cpy[1] == output_upy[1]:
+ status = 'Supported'
+ print('Supported operation!\nFile: ' + TESTPATH + test.name)
+ else:
+ status = 'Unsupported'
+
+ output = Output(test.name, test.class_, test.desc, test.cause,
+ test.workaround, test.code, output_cpy, output_upy, status)
+ results.append(output)
+
+ results.sort(key=lambda x: x.class_)
+ return results
+
+def indent(block, spaces):
+ """ indents paragraphs of text for rst formatting """
+ new_block = ''
+ for line in block.split('\n'):
+ new_block += spaces + line + '\n'
+ return new_block
+
+def gen_table(contents):
+ """ creates a table given any set of columns """
+ xlengths = []
+ ylengths = []
+ for column in contents:
+ col_len = 0
+ for entry in column:
+ lines = entry.split('\n')
+ for line in lines:
+ col_len = max(len(line) + 2, col_len)
+ xlengths.append(col_len)
+ for i in range(len(contents[0])):
+ ymax = 0
+ for j in range(len(contents)):
+ ymax = max(ymax, len(contents[j][i].split('\n')))
+ ylengths.append(ymax)
+
+ table_divider = '+' + ''.join(['-' * i + '+' for i in xlengths]) + '\n'
+ table = table_divider
+ for i in range(len(ylengths)):
+ row = [column[i] for column in contents]
+ row = [entry + '\n' * (ylengths[i]-len(entry.split('\n'))) for entry in row]
+ row = [entry.split('\n') for entry in row]
+ for j in range(ylengths[i]):
+ k = 0
+ for entry in row:
+ width = xlengths[k]
+ table += ''.join(['| {:{}}'.format(entry[j], width - 1)])
+ k += 1
+ table += '|\n'
+ table += table_divider
+ return table + '\n'
+
+def gen_rst(results):
+ """ creates restructured text documents to display tests """
+
+ # make sure the destination directory exists
+ try:
+ os.mkdir(DOCPATH)
+ except OSError as e:
+ if e.args[0] != errno.EEXIST and e.args[0] != errno.EISDIR:
+ raise
+
+ toctree = []
+ class_ = []
+ for output in results:
+ section = output.class_.split(',')
+ for i in range(len(section)):
+ section[i] = section[i].rstrip()
+ if section[i] in CLASSMAP:
+ section[i] = CLASSMAP[section[i]]
+ if i >= len(class_) or section[i] != class_[i]:
+ if i == 0:
+ filename = section[i].replace(' ', '_').lower()
+ rst = open(DOCPATH + filename + '.rst', 'w')
+ rst.write(HEADER)
+ rst.write(section[i] + '\n')
+ rst.write(RSTCHARS[0] * len(section[i]))
+ rst.write(time.strftime("\nGenerated %a %d %b %Y %X UTC\n\n", time.gmtime()))
+ toctree.append(filename)
+ else:
+ rst.write(section[i] + '\n')
+ rst.write(RSTCHARS[min(i, len(RSTCHARS)-1)] * len(section[i]))
+ rst.write('\n\n')
+ class_ = section
+ rst.write('**' + output.desc + '**\n\n')
+ if output.cause != 'Unknown':
+ rst.write('**Cause:** ' + output.cause + '\n\n')
+ if output.workaround != 'Unknown':
+ rst.write('**Workaround:** ' + output.workaround + '\n\n')
+
+ rst.write('Sample code::\n\n' + indent(output.code, TAB) + '\n')
+ output_cpy = indent(''.join(output.output_cpy[0:2]), TAB).rstrip()
+ output_cpy = ('::\n\n' if output_cpy != '' else '') + output_cpy
+ output_upy = indent(''.join(output.output_upy[0:2]), TAB).rstrip()
+ output_upy = ('::\n\n' if output_upy != '' else '') + output_upy
+ table = gen_table([['CPy output:', output_cpy], ['uPy output:', output_upy]])
+ rst.write(table)
+
+ template = open(INDEXTEMPLATE, 'r')
+ index = open(DOCPATH + INDEX, 'w')
+ index.write(HEADER)
+ index.write(template.read())
+ for section in INDEXPRIORITY:
+ if section in toctree:
+ index.write(indent(section + '.rst', TAB))
+ toctree.remove(section)
+ for section in toctree:
+ index.write(indent(section + '.rst', TAB))
+
+def main():
+ """ Main function """
+
+ # set search path so that test scripts find the test modules (and no other ones)
+ os.environ['PYTHONPATH'] = TESTPATH
+ os.environ['MICROPYPATH'] = TESTPATH
+
+ files = readfiles()
+ results = run_tests(files)
+ gen_rst(results)
+
+main()
diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py
index ce373a4f5..544f90cc8 100755
--- a/tools/mpy-tool.py
+++ b/tools/mpy-tool.py
@@ -45,7 +45,7 @@ import sys
import struct
from collections import namedtuple
-sys.path.append('../py')
+sys.path.append(sys.path[0] + '/../py')
import makeqstrdata as qstrutil
class FreezeError(Exception):
@@ -57,6 +57,7 @@ class FreezeError(Exception):
return 'error while freezing %s: %s' % (self.rawcode.source_file, self.msg)
class Config:
+ MPY_VERSION = 2
MICROPY_LONGINT_IMPL_NONE = 0
MICROPY_LONGINT_IMPL_LONGLONG = 1
MICROPY_LONGINT_IMPL_MPZ = 2
@@ -93,7 +94,7 @@ def make_opcode_format():
OC4(U, U, U, U), # 0x0c-0x0f
OC4(B, B, B, U), # 0x10-0x13
OC4(V, U, Q, V), # 0x14-0x17
- OC4(B, U, V, V), # 0x18-0x1b
+ OC4(B, V, V, Q), # 0x18-0x1b
OC4(Q, Q, Q, Q), # 0x1c-0x1f
OC4(B, B, V, V), # 0x20-0x23
OC4(Q, Q, Q, B), # 0x24-0x27
@@ -331,25 +332,25 @@ class RawCode:
raise FreezeError(self, 'freezing of object %r is not implemented' % (obj,))
# generate constant table
- print('STATIC const mp_uint_t const_table_data_%s[%u] = {'
+ print('STATIC const mp_rom_obj_t const_table_data_%s[%u] = {'
% (self.escaped_name, len(self.qstrs) + len(self.objs) + len(self.raw_codes)))
for qst in self.qstrs:
- print(' (mp_uint_t)MP_OBJ_NEW_QSTR(%s),' % global_qstrs[qst].qstr_id)
+ print(' MP_ROM_QSTR(%s),' % global_qstrs[qst].qstr_id)
for i in range(len(self.objs)):
if type(self.objs[i]) is float:
print('#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B')
- print(' (mp_uint_t)&const_obj_%s_%u,' % (self.escaped_name, i))
+ print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
print('#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C')
n = struct.unpack('<I', struct.pack('<f', self.objs[i]))[0]
n = ((n & ~0x3) | 2) + 0x80800000
- print(' (mp_uint_t)0x%08x,' % (n,))
+ print(' (mp_rom_obj_t)(0x%08x),' % (n,))
print('#else')
print('#error "MICROPY_OBJ_REPR_D not supported with floats in frozen mpy files"')
print('#endif')
else:
- print(' (mp_uint_t)&const_obj_%s_%u,' % (self.escaped_name, i))
+ print(' MP_ROM_PTR(&const_obj_%s_%u),' % (self.escaped_name, i))
for rc in self.raw_codes:
- print(' (mp_uint_t)&raw_code_%s,' % rc.escaped_name)
+ print(' MP_ROM_PTR(&raw_code_%s),' % rc.escaped_name)
print('};')
# generate module
@@ -361,7 +362,7 @@ class RawCode:
print(' .n_pos_args = %u,' % self.prelude[3])
print(' .data.u_byte = {')
print(' .bytecode = bytecode_data_%s,' % self.escaped_name)
- print(' .const_table = const_table_data_%s,' % self.escaped_name)
+ print(' .const_table = (mp_uint_t*)const_table_data_%s,' % self.escaped_name)
print(' #if MICROPY_PERSISTENT_CODE_SAVE')
print(' .bc_len = %u,' % len(self.bytecode))
print(' .n_obj = %u,' % len(self.objs))
@@ -438,8 +439,8 @@ def read_mpy(filename):
header = bytes_cons(f.read(4))
if header[0] != ord('M'):
raise Exception('not a valid .mpy file')
- if header[1] != 0:
- raise Exception('incompatible version')
+ if header[1] != config.MPY_VERSION:
+ raise Exception('incompatible .mpy version')
feature_flags = header[2]
config.MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE = (feature_flags & 1) != 0
config.MICROPY_PY_BUILTINS_STR_UNICODE = (feature_flags & 2) != 0
diff --git a/tools/mpy_cross_all.py b/tools/mpy_cross_all.py
new file mode 100755
index 000000000..2bda71e9b
--- /dev/null
+++ b/tools/mpy_cross_all.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+import argparse
+import os
+import os.path
+
+argparser = argparse.ArgumentParser(description="Compile all .py files to .mpy recursively")
+argparser.add_argument("-o", "--out", help="output directory (default: input dir)")
+argparser.add_argument("--target", help="select MicroPython target config")
+argparser.add_argument("-mcache-lookup-bc", action="store_true", help="cache map lookups in the bytecode")
+argparser.add_argument("dir", help="input directory")
+args = argparser.parse_args()
+
+TARGET_OPTS = {
+ "unix": "-mcache-lookup-bc",
+ "baremetal": "",
+}
+
+args.dir = args.dir.rstrip("/")
+
+if not args.out:
+ args.out = args.dir
+
+path_prefix_len = len(args.dir) + 1
+
+for path, subdirs, files in os.walk(args.dir):
+ for f in files:
+ if f.endswith(".py"):
+ fpath = path + "/" + f
+ #print(fpath)
+ out_fpath = args.out + "/" + fpath[path_prefix_len:-3] + ".mpy"
+ out_dir = os.path.dirname(out_fpath)
+ if not os.path.isdir(out_dir):
+ os.makedirs(out_dir)
+ cmd = "mpy-cross -v -v %s -s %s %s -o %s" % (TARGET_OPTS.get(args.target, ""),
+ fpath[path_prefix_len:], fpath, out_fpath)
+ #print(cmd)
+ res = os.system(cmd)
+ assert res == 0
diff --git a/tools/pyboard.py b/tools/pyboard.py
index d4ce8b788..5eac030bd 100755
--- a/tools/pyboard.py
+++ b/tools/pyboard.py
@@ -39,6 +39,7 @@ Or:
import sys
import time
+import os
try:
stdout = sys.stdout.buffer
@@ -116,9 +117,93 @@ class TelnetToSerial:
else:
return n_waiting
+
+class ProcessToSerial:
+ "Execute a process and emulate serial connection using its stdin/stdout."
+
+ def __init__(self, cmd):
+ import subprocess
+ self.subp = subprocess.Popen(cmd.split(), bufsize=0, shell=True, preexec_fn=os.setsid,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+
+ # Initially was implemented with selectors, but that adds Python3
+ # dependency. However, there can be race conditions communicating
+ # with a particular child process (like QEMU), and selectors may
+ # still work better in that case, so left inplace for now.
+ #
+ #import selectors
+ #self.sel = selectors.DefaultSelector()
+ #self.sel.register(self.subp.stdout, selectors.EVENT_READ)
+
+ import select
+ self.poll = select.poll()
+ self.poll.register(self.subp.stdout.fileno())
+
+ def close(self):
+ import signal
+ os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM)
+
+ def read(self, size=1):
+ data = b""
+ while len(data) < size:
+ data += self.subp.stdout.read(size - len(data))
+ return data
+
+ def write(self, data):
+ self.subp.stdin.write(data)
+ return len(data)
+
+ def inWaiting(self):
+ #res = self.sel.select(0)
+ res = self.poll.poll(0)
+ if res:
+ return 1
+ return 0
+
+
+class ProcessPtyToTerminal:
+ """Execute a process which creates a PTY and prints slave PTY as
+ first line of its output, and emulate serial connection using
+ this PTY."""
+
+ def __init__(self, cmd):
+ import subprocess
+ import re
+ import serial
+ self.subp = subprocess.Popen(cmd.split(), bufsize=0, shell=False, preexec_fn=os.setsid,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ pty_line = self.subp.stderr.readline().decode("utf-8")
+ m = re.search(r"/dev/pts/[0-9]+", pty_line)
+ if not m:
+ print("Error: unable to find PTY device in startup line:", pty_line)
+ self.close()
+ sys.exit(1)
+ pty = m.group()
+ # rtscts, dsrdtr params are to workaround pyserial bug:
+ # http://stackoverflow.com/questions/34831131/pyserial-does-not-play-well-with-virtual-port
+ self.ser = serial.Serial(pty, interCharTimeout=1, rtscts=True, dsrdtr=True)
+
+ def close(self):
+ import signal
+ os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM)
+
+ def read(self, size=1):
+ return self.ser.read(size)
+
+ def write(self, data):
+ return self.ser.write(data)
+
+ def inWaiting(self):
+ return self.ser.inWaiting()
+
+
class Pyboard:
def __init__(self, device, baudrate=115200, user='micro', password='python', wait=0):
- if device and device[0].isdigit() and device[-1].isdigit() and device.count('.') == 3:
+ if device.startswith("exec:"):
+ self.serial = ProcessToSerial(device[len("exec:"):])
+ elif device.startswith("execpty:"):
+ self.serial = ProcessPtyToTerminal(device[len("qemupty:"):])
+ elif device and device[0].isdigit() and device[-1].isdigit() and device.count('.') == 3:
# device looks like an IP address
self.serial = TelnetToSerial(device, user, password, read_timeout=10)
else:
@@ -234,7 +319,7 @@ class Pyboard:
# check if we could exec command
data = self.serial.read(2)
if data != b'OK':
- raise PyboardError('could not exec command')
+ raise PyboardError('could not exec command (response: %s)' % data)
def exec_raw(self, command, timeout=10, data_consumer=None):
self.exec_raw_no_follow(command);
@@ -300,6 +385,7 @@ def main():
pyb.enter_raw_repl()
except PyboardError as er:
print(er)
+ pyb.close()
sys.exit(1)
def execbuffer(buf):
@@ -307,6 +393,7 @@ def main():
ret, ret_err = pyb.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes)
except PyboardError as er:
print(er)
+ pyb.close()
sys.exit(1)
except KeyboardInterrupt:
sys.exit(1)
diff --git a/tools/tinytest-codegen.py b/tools/tinytest-codegen.py
index 3436d0f45..dadfea1cc 100755
--- a/tools/tinytest-codegen.py
+++ b/tools/tinytest-codegen.py
@@ -48,10 +48,9 @@ testgroup_member = (
# currently these tests are selected because they pass on qemu-arm
test_dirs = ('basics', 'micropython', 'float', 'extmod', 'inlineasm') # 'import', 'io', 'misc')
exclude_tests = (
- 'float/float2int_doubleprec.py', # requires double precision floating point to work
+ 'float/float2int_doubleprec_intbig.py', # requires double precision floating point to work
'inlineasm/asmfpaddsub.py', 'inlineasm/asmfpcmp.py', 'inlineasm/asmfpldrstr.py', 'inlineasm/asmfpmuldiv.py', 'inlineasm/asmfpsqrt.py',
'extmod/ticks_diff.py', 'extmod/time_ms_us.py', 'extmod/uheapq_timeq.py',
- 'extmod/machine_pinbase.py', 'extmod/machine_pulse.py',
'extmod/vfs_fat_ramdisk.py', 'extmod/vfs_fat_fileio.py', 'extmod/vfs_fat_fsusermount.py', 'extmod/vfs_fat_oldproto.py',
)
diff --git a/tools/upip.py b/tools/upip.py
index db18a7427..7b85c718f 100644
--- a/tools/upip.py
+++ b/tools/upip.py
@@ -104,48 +104,62 @@ import usocket
warn_ussl = True
def url_open(url):
global warn_ussl
+
+ if debug:
+ print(url)
+
proto, _, host, urlpath = url.split('/', 3)
- ai = usocket.getaddrinfo(host, 443)
+ try:
+ ai = usocket.getaddrinfo(host, 443)
+ except OSError as e:
+ fatal("Unable to resolve %s (no Internet?)" % host, e)
#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":
- if status == b"404":
- print("Package not found")
- raise ValueError(status)
- while 1:
+ try:
+ #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()
- if not l:
- raise ValueError("Unexpected EOF")
- if l == b'\r\n':
- break
+ protover, status, msg = l.split(None, 2)
+ if status != b"200":
+ if status == b"404" or status == b"301":
+ raise NotFoundError("Package not found")
+ raise ValueError(status)
+ while 1:
+ l = s.readline()
+ if not l:
+ raise ValueError("Unexpected EOF in HTTP headers")
+ if l == b'\r\n':
+ break
+ except Exception as e:
+ s.close()
+ raise e
return s
def get_pkg_metadata(name):
f = url_open("https://pypi.python.org/pypi/%s/json" % name)
- s = f.read()
- f.close()
- return json.loads(s)
+ try:
+ return json.load(f)
+ finally:
+ f.close()
-def fatal(msg):
- print(msg)
+def fatal(msg, exc=None):
+ print("Error:", msg)
+ if exc and debug:
+ raise exc
sys.exit(1)
def install_pkg(pkg_spec, install_path):
@@ -160,10 +174,12 @@ def install_pkg(pkg_spec, install_path):
print("Installing %s %s from %s" % (pkg_spec, latest_ver, package_url))
package_fname = op_basename(package_url)
f1 = url_open(package_url)
- f2 = uzlib.DecompIO(f1, gzdict_sz)
- f3 = tarfile.TarFile(fileobj=f2)
- meta = install_tar(f3, install_path)
- f1.close()
+ try:
+ f2 = uzlib.DecompIO(f1, gzdict_sz)
+ f3 = tarfile.TarFile(fileobj=f2)
+ meta = install_tar(f3, install_path)
+ finally:
+ f1.close()
del f3
del f2
gc.collect()
@@ -200,9 +216,10 @@ def install(to_install, install_path=None):
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)
+ except Exception as e:
+ print("Error installing '{}': {}, packages may be partially installed".format(
+ pkg_spec, e),
+ file=sys.stderr)
def get_install_path():
global install_path
@@ -267,6 +284,8 @@ def main():
l = f.readline()
if not l:
break
+ if l[0] == "#":
+ continue
to_install.append(l.rstrip())
elif opt == "--debug":
debug = True