#! /usr/bin/env python3 # encoding: utf-8 import copy import imp import io import math import os import sys # -------------------------------------------------- def import_module(path): ''' Imports a python file as a module. The path can be relative or absolute. Based on the work of Yuval Greenfield released into the public domain. ''' # remove the .py suffix mod_dn = os.path.dirname(path) mod_fn = os.path.basename(path) if mod_fn.endswith('.py'): mod_name = mod_fn[:-3] else: # packages for example mod_name = mod_fn fd = None try: data = imp.find_module(mod_name, [mod_dn]) module = imp.load_module(mod_name, *data) fd = data[0] finally: if fd is not None: fd.close() return module # -------------------------------------------------- def batch_gen(data, batch_size): ''' Split data (a sequence) into sequences batch_size elements long. ''' for i in range(0, len(data), batch_size): yield data[i:i+batch_size] # -------------------------------------------------- def console(locals, globals=None, tab_completion=False): ''' Opens up a Python console. tab_completion enables completion of object names using TAB, however note that this will make pasting formatted snippets of code difficult ''' import code, readline, rlcompleter environment = copy.copy(locals) if globals: for key, value in globals.items(): if key not in environment: environment[key] = value if tab_completion: readline.parse_and_bind('tab: complete') c = code.InteractiveConsole(environment) c.interact(banner='') # -------------------------------------------------- def debugger(): ''' Drops into the Python Debugger where called. ''' import pdb pdb.set_trace() # -------------------------------------------------- def hexdump(data, indent=0, offset=16, show_header=True, show_offsets=True, show_ascii=True, output=sys.stdout): """ Writes a hex dump of 'data' to 'output'. The output text is indented with spaces and contains 'offset' bytes per line. If show_header is True, a single line with byte numbers preceeds all output. If show_offsets is True, each line is prefixed with the address of the first byte of that line. If show_ascii is True, each line is suffixed with its ASCII representation. Unprintable characters are replaced with a dot. The 'output' must implement a .write(str) method. If 'output' is None, the string is returned instead. """ output_io = io.StringIO() if not output else output if type(data) is not bytes: raise ValueError('data must be bytes') offset_figures = math.ceil(math.log2(len(data)) / 8) * 2 if data else 2 format_str = '%%0%dx ' %(offset_figures) ptr = 0 if show_header: line = [] if show_offsets: line.append('offset'[:offset_figures].ljust(offset_figures + 2)) for i in range(offset): line.append('%2x' %(i)) if show_ascii: line.append(' ASCII') output_io.write(' '.join(line) + '\n') while data[ptr:]: if indent: output_io.write(' ' * indent) if show_offsets: output_io.write(format_str %(ptr)) hex_bytes = [] ascii_bytes = [] for local_i, i in enumerate(range(ptr, ptr+offset)): if i < len(data): c = data[i] hex_bytes.append('%02x' %(c)) if 0x20 <= c <= 0x7e: ascii_bytes.append(chr(c)) else: ascii_bytes.append('.') elif i == len(data): hex_bytes.extend([' '] * (offset - local_i)) output_io.write(' '.join(hex_bytes)) if show_ascii: output_io.write(' |' + ''.join(ascii_bytes) + '|') output_io.write('\n') ptr += offset if not output: output_io.seek(0) return output_io.read()