def add(self, entry): """ Add an ENTRY to the CArchive. ENTRY must have: entry[0] is name (under which it will be saved). entry[1] is fullpathname of the file. entry[2] is a flag for it's storage format (0==uncompressed, 1==compressed) entry[3] is the entry's type code. Version 5: If the type code is 'o': entry[0] is the runtime option eg: v (meaning verbose imports) u (menaing unbuffered) W arg (warning option arg) s (meaning do site.py processing. """ (nm, pathnm, flag, typcd) = entry[:4] # FIXME Could we make the version 5 the default one? # Version 5 - allow type 'o' = runtime option. code_data = None fh = None try: if typcd in ('o', 'd'): ulen = 0 flag = 0 elif typcd == 's': # If it's a source code file, compile it to a code object and marshall # the object so it can be unmarshalled by the bootloader. code = get_code_object(nm, pathnm) code = strip_paths_in_code(code) code_data = marshal.dumps(code) ulen = len(code_data) else: fh = open(pathnm, 'rb') ulen = os.fstat(fh.fileno()).st_size except IOError: print("Cannot find ('%s', '%s', %s, '%s')" % (nm, pathnm, flag, typcd)) raise where = self.lib.tell() assert flag in range(3) if not fh and not code_data: # no need to write anything pass elif flag == 1: comprobj = zlib.compressobj(self.LEVEL) if code_data is not None: self.lib.write(comprobj.compress(code_data)) else: assert fh # We only want to change it for pyc files modify_header = typcd in ('M', 'm', 's') while 1: buf = fh.read(16 * 1024) if not buf: break if modify_header: modify_header = False buf = fake_pyc_timestamp(buf) self.lib.write(comprobj.compress(buf)) self.lib.write(comprobj.flush()) else: if code_data is not None: self.lib.write(code_data) else: assert fh while 1: buf = fh.read(16 * 1024) if not buf: break self.lib.write(buf) dlen = self.lib.tell() - where if typcd == 'm': if pathnm.find('.__init__.py') > -1: typcd = 'M' if fh: fh.close() # Record the entry in the CTOC self.toc.add(where, dlen, ulen, flag, typcd, nm)
def add(self, entry): """ Add an ENTRY to the CArchive. ENTRY must have: entry[0] is name (under which it will be saved). entry[1] is fullpathname of the file. entry[2] is a flag for it's storage format (0==uncompressed, 1==compressed) entry[3] is the entry's type code. Version 5: If the type code is 'o': entry[0] is the runtime option eg: v (meaning verbose imports) u (meaning unbuffered) W arg (warning option arg) s (meaning do site.py processing. """ (nm, pathnm, flag, typcd) = entry[:4] # FIXME Could we make the version 5 the default one? # Version 5 - allow type 'o' = runtime option. code_data = None fh = None try: if typcd in ('o', 'd'): ulen = 0 flag = 0 elif typcd == 's': # If it is a source code file, compile it to a code object and marshall the object, so it can be # unmarshalled by the bootloader. code = get_code_object(nm, pathnm) code = strip_paths_in_code(code) code_data = marshal.dumps(code) ulen = len(code_data) elif typcd == 'm': fh = open(pathnm, 'rb') ulen = os.fstat(fh.fileno()).st_size # Check if it is a PYC file header = fh.read(4) fh.seek(0) if header == BYTECODE_MAGIC: # Read whole header and load code. According to PEP-552, in python versions prior to 3.7, the PYC # header consists of three 32-bit words (magic, timestamp, and source file size). # From python 3.7 on, the PYC header was extended to four 32-bit words (magic, flags, and, depending # on the flags, either timestamp and source file size, or a 64-bit hash). if is_py37: header = fh.read(16) else: header = fh.read(12) code = marshal.load(fh) # Strip paths from code, marshal back into module form. The header fields (timestamp, size, hash, # etc.) are all referring to the source file, so our modification of the code object does not affect # them, and we can re-use the original header. code = strip_paths_in_code(code) data = header + marshal.dumps(code) # Create file-like object for timestamp re-write in the subsequent steps. fh = io.BytesIO(data) ulen = len(data) else: fh = open(pathnm, 'rb') ulen = os.fstat(fh.fileno()).st_size except IOError: print("Cannot find ('%s', '%s', %s, '%s')" % (nm, pathnm, flag, typcd)) raise where = self.lib.tell() assert flag in range(3) if not fh and not code_data: # No need to write anything. pass elif flag == 1: comprobj = zlib.compressobj(self.LEVEL) if code_data is not None: self.lib.write(comprobj.compress(code_data)) else: assert fh # We only want to change it for pyc files. modify_header = typcd in ('M', 'm', 's') while 1: buf = fh.read(16 * 1024) if not buf: break if modify_header: modify_header = False buf = fake_pyc_timestamp(buf) self.lib.write(comprobj.compress(buf)) self.lib.write(comprobj.flush()) else: if code_data is not None: self.lib.write(code_data) else: assert fh while 1: buf = fh.read(16 * 1024) if not buf: break self.lib.write(buf) dlen = self.lib.tell() - where if typcd == 'm': if pathnm.find('.__init__.py') > -1: typcd = 'M' if fh: fh.close() # Record the entry in the CTOC self.toc.add(where, dlen, ulen, flag, typcd, nm)