def send_head(self): """Common code for GET and HEAD commands. This sends the response code and MIME headers. Return value is either a file object (which has to be copied to the outputfile by the caller unless the command was HEAD, and must be closed by the caller under all circumstances), or None, in which case the caller has nothing further to do. """ path = self.translate_path(self.path) f = None if self.path.endswith('?md5') is True: if os.path.exists(path) is False: return 'error not exist' else: md5 = self.get_md5_small(path) print(md5) f = BytesIO() f.write(bytes(md5, encoding = "utf8" )) length = f.tell() f.seek(0) self.send_response(200) self.send_header("Content-type", "text/html") self.send_header("Content-Length", str(length)) self.end_headers() return f elif os.path.isdir(path): if not self.path.endswith('/'): # redirect browser - doing basically what apache does self.send_response(301) self.send_header("Location", self.path + "/") self.end_headers() return None for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: ## arkPath = os.path.join(path, "archive") return self.list_directory(path) ctype = self.guess_type(path) try: # Always read in binary mode. Opening files in text mode may cause # newline translations, making the actual size of the content # transmitted *less* than the content-length! f = open(path, 'rb') except IOError: self.send_error(404, "File not found") return None self.send_response(200) self.send_header("Content-type", ctype) fs = os.fstat(f.fileno()) self.send_header("Content-Length", str(fs[6])) self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) self.end_headers() return f
def convert(self, sch): def mock_open(name, mode): self.assertEqual(mode, "rb") return stream class MockOle: def __init__(ole, file): self.assertIs(file, stream) super().__init__() def listdir(ole): return [["FileHeader"], ["Storage"]] def exists(ole, name): return name in {"FileHeader", "Storage"} def openstream(ole, name): if name == "Storage": return BytesIO(b"\x15\x00\x00\x00" b"|HEADER=Icon storage\x00") self.assertEqual(name, "FileHeader") return stream class mock_os: def stat(fileno): return SimpleNamespace(st_mtime=0) class path: def isabs(path): return True stream = BytesIO() stream.fileno = lambda: stream weight = format(1 + len(sch)).encode("ascii") preliminary = ( b"|HEADER=Protel for Windows - Schematic Capture Binary File " b"Version 5.0|WEIGHT=" + weight + b"\x00", b"|RECORD=31|FONTIDCOUNT=1|SIZE1=10|FONTNAME1=Times New Roman" b"|SYSTEMFONT=1" b"|AREACOLOR=16317695|BORDERON=T|CUSTOMX=|CUSTOMY=" b"|DISPLAY_UNIT=4|HOTSPOTGRIDON=T|HOTSPOTGRIDSIZE=" b"|SNAPGRIDON=T|SNAPGRIDSIZE=|VISIBLEGRIDON=T" b"|VISIBLEGRIDSIZE=10|ISBOC=T|SHEETNUMBERSPACESIZE=4" b"|USEMBCS=T\x00", ) for list in preliminary + sch: stream.write(len(list).to_bytes(4, "little")) stream.write(list) stream.seek(0) output = StringIO() with patch("altium.open", mock_open), \ patch("altium.OleFileIO", MockOle), \ patch("altium.os", mock_os), \ redirect_stdout(output): altium.render("dummy.SchDoc", svg.Renderer) return output.getvalue()
def convert(self, sch): def mock_open(name, mode): self.assertEqual(mode, "rb") return stream class MockOle: def __init__(ole, file): self.assertIs(file, stream) super().__init__() def listdir(ole): return [["FileHeader"], ["Storage"]] def exists(ole, name): return name in {"FileHeader", "Storage"} def openstream(ole, name): if name == "Storage": return BytesIO( b"\x15\x00\x00\x00" b"|HEADER=Icon storage\x00" ) self.assertEqual(name, "FileHeader") return stream class mock_os: def stat(fileno): return None stream = BytesIO() stream.fileno = lambda: stream weight = format(1 + len(sch)).encode("ascii") preliminary = ( b"|HEADER=Protel for Windows - Schematic Capture Binary File " b"Version 5.0|WEIGHT=" + weight + b"\x00", b"|RECORD=31|FONTIDCOUNT=1|SIZE1=10|FONTNAME1=Times New Roman" b"|SYSTEMFONT=1" b"|AREACOLOR=16317695|BORDERON=T|CUSTOMX=|CUSTOMY=" b"|DISPLAY_UNIT=4|HOTSPOTGRIDON=T|HOTSPOTGRIDSIZE=" b"|SNAPGRIDON=T|SNAPGRIDSIZE=|VISIBLEGRIDON=T" b"|VISIBLEGRIDSIZE=10|ISBOC=T|SHEETNUMBERSPACESIZE=4" b"|USEMBCS=T\x00", ) for list in preliminary + sch: stream.write(len(list).to_bytes(4, "little")) stream.write(list) stream.seek(0) output = StringIO() with patch("altium.open", mock_open), \ patch("altium.OleFileIO", MockOle), \ patch("altium.os", mock_os), \ redirect_stdout(output): altium.render("dummy.SchDoc", svg.Renderer) return output.getvalue()
def rotate_image(data, angle): # bug workaround in some PIL versions def fileno(): raise AttributeError # open image from data and rotate image = Image.open(BytesIO(data)) rot = image.rotate(angle, expand=True) # save image to a file in memory memfile = BytesIO() memfile.fileno = fileno # required in some broken PILs rot.save(memfile, image.format) return memfile.getvalue()
def addphotoform(request): albumname = request.matchdict['albumname'] if request.method == "POST": filename = request.POST['jpg'].filename inputfile = request.POST['jpg'].file #put the photo in the store key = request.mystore.genkey(albumname, filename) request.mystore[key] = inputfile.read() #create the thumbnail inputfile.seek(0) size = 300, 300 im = Image.open(inputfile) im.thumbnail(size, Image.ANTIALIAS) #create a file-like object for storing the thumbnail imagefile = BytesIO() def fileno(): raise AttributeError imagefile.fileno = fileno #hack to make PIL and BytesIO work together... im.save(imagefile, 'JPEG') #save thumbnail in jpeg format into imagefile imagefile.seek(0) thumbkey = request.mystore.genkey(albumname, filename, thumbnail = True) request.mystore[thumbkey] = imagefile.read() #store the new photo in the database album = DBSession.query(Album).filter(Album.name==albumname).one() photo = Photo() photo.album = album photo.filekey = key photo.thumbkey = thumbkey photo.filename = filename DBSession.add(photo) return HTTPFound(request.route_path("addphotoform",albumname=albumname)) return {}
def generate_thumbnail(albumname, filename): log.debug("generating thumbnail for %s/%s"%(albumname, filename)) #get the Photo object: album = DBSession.query(Album).filter(Album.name==albumname).one() photo = DBSession.query(Photo).filter(Photo.album==album).filter(Photo.filename==filename).one() #retrieve the original image: f = celery.mystore[photo.filekey] #create the thumbnail size = 300, 300 inputfile = BytesIO(f) im = Image.open(inputfile) im.thumbnail(size, Image.ANTIALIAS) imagefile = BytesIO() def fileno(): raise AttributeError imagefile.fileno = fileno #hack to make PIL and BytesIO work together... im.save(imagefile, 'JPEG') imagefile.seek(0) # put the file cursor back to the origin #save the thumbnail into the storage system: dirname = os.path.dirname(photo.filekey) key = celery.mystore.genkey(dirname, filename, thumbnail=True) celery.mystore[key] = imagefile.read() #update photo url: photo.thumbkey = key DBSession.add(photo) transaction.commit() log.debug('the key is: %s'%key) log.debug("thumbnail generation done for %s/%s"%(albumname, filename))
def send_head(self): """Common code for GET and HEAD commands. This sends the response code and MIME headers. Return value is either a file object (which has to be copied to the outputfile by the caller unless the command was HEAD, and must be closed by the caller under all circumstances), or None, in which case the caller has nothing further to do. """ path, b64_encoded = self.translate_path(self.path) # Return to requests for files file_listing = False if path.endswith('/SHTTPSSgetFiles'): path = path.split('/SHTTPSSgetFiles')[0] file_listing = True f = BytesIO() f.write(self.list_files(path, b64_encoded)) length = f.tell() f.seek(0) self.send_response(200) self.send_header('Content-Type', 'text/html') self.send_header('Content-Length', str(length)) self.end_headers() return f f = None if os.path.isdir(path): parts = urllib.parse.urlsplit(self.path) # ==================================== # REDIRECT BROWSER BACK TO PATH WITH / # ==================================== 'Only when in a directory, though' if not parts.path.endswith('/'): # redirect browser - doing basically what apache does self.send_response(HTTPStatus.MOVED_PERMANENTLY) new_parts = (parts[0], parts[1], parts[2] + '/', parts[3], parts[4]) new_url = urllib.parse.urlunsplit(new_parts) self.send_header("Location", new_url) self.end_headers() return None for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: if CorsHandler.uploads_enabled: return self.list_directory(path,b64_encoded) else: return self.list_directory(path) ctype = self.guess_type(path) # check for trailing "/" which should return 404. See Issue17324 # The test for this was added in test_httpserver.py # However, some OS platforms accept a trailingSlash as a filename # See discussion on python-dev and Issue34711 regarding # parseing and rejection of filenames with a trailing slash if path.endswith("/"): self.send_error(HTTPStatus.NOT_FOUND, "File not found") return None try: f = open(path, 'rb') except OSError: self.send_error(HTTPStatus.NOT_FOUND, "File not found") return None try: fs = os.fstat(f.fileno()) if not CorsHandler.DISABLE_CACHING: # Use browser cache if possible if ("If-Modified-Since" in self.headers and "If-None-Match" not in self.headers): # compare If-Modified-Since and time of last file modification try: ims = email.utils.parsedate_to_datetime( self.headers["If-Modified-Since"]) except (TypeError, IndexError, OverflowError, ValueError): # ignore ill-formed values pass else: if ims.tzinfo is None: # obsolete format with no timezone, cf. # https://tools.ietf.org/html/rfc7231#section-7.1.1.1 ims = ims.replace(tzinfo=datetime.timezone.utc) if ims.tzinfo is datetime.timezone.utc: # compare to UTC datetime of last modification last_modif = datetime.datetime.fromtimestamp( fs.st_mtime, datetime.timezone.utc) # remove microseconds, like in If-Modified-Since last_modif = last_modif.replace(microsecond=0) if last_modif <= ims: self.send_response(HTTPStatus.NOT_MODIFIED) self.end_headers() f.close() return None self.send_response(HTTPStatus.OK) self.send_header("Content-type", ctype) self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) # Adding Access-Control-Allow-Origin header self.send_header('Access-Control-Allow-Origin','*') self.end_headers() if CorsHandler.B64_ENCODE_PAYLOAD and b64_encoded: # Read in the file and prepare for encoding encoded = f.read() self.log_message( 'Encoding target download file {} ({} bytes, md5sum: {})' \ .format(path, len(encoded), md5sum(encoded)) ) # TODO: Make iterations configurable for i in range(0,2): encoded = b64encode(encoded) encoded_length = len(encoded) self.log_message( 'File encoding success {} ({} bytes, md5sum: {})' \ .format(path, encoded_length, md5sum(encoded)) ) # Update the content-length header to reflect the length # of the encoded value self.send_header("Content-Length", encoded_length) return BytesIO(encoded) else: # Update the content-length header with the length of the # normal value self.send_header("Content-Length", str(fs[6])) return f except: f.close() raise
def send_head(self): """Send response code and MIME header. This is common code for GET and HEAD commands. Return value is either a file object (which has to be copied to the outputfile by the caller unless the command was HEAD, and must be closed by the caller under all circumstances), or None, in which case the caller has nothing further to do. """ path = self.translate_path(self.path) f = None if os.path.isdir(path): path_parts = list(self.path.partition('?')) if not path_parts[0].endswith('/'): # redirect browser - doing basically what apache does path_parts[0] += '/' self.send_response(301) self.send_header("Location", ''.join(path_parts)) # begin no-cache patch # For redirects. With redirects, caching is even worse and can # break more. Especially with 301 Moved Permanently redirects, # like this one. self.send_header("Cache-Control", "no-cache, no-store, " "must-revalidate") self.send_header("Pragma", "no-cache") self.send_header("Expires", "0") # end no-cache patch self.end_headers() return None for index in "index.html", "index.htm": index = os.path.join(path, index) if os.path.exists(index): path = index break else: return self.list_directory(path) ctype = self.guess_type(path) try: # Always read in binary mode. Opening files in text mode may cause # newline translations, making the actual size of the content # transmitted *less* than the content-length! f = open(path, 'rb') except IOError: self.send_error(404, "File not found: {}".format(path)) return None filtered_bytes = None if ctype == 'text/html': # Comment out any <base> to allow local resolution of relative URLs. data = f.read().decode('utf8') f.close() data = re.sub(r'<base\s([^>]*)>', r'<!--base \g<1>-->', data, flags=re.IGNORECASE) data = data.encode('utf8') f = StringIO() f.write(data) filtered_bytes = len(data) f.seek(0) self.send_response(200) if ctype.startswith('text/') or ctype.endswith('+xml'): self.send_header("Content-Type", "{0}; charset=UTF-8".format(ctype)) else: self.send_header("Content-Type", ctype) if os.path.splitext(path)[1] == '.svgz': # Special handling for svgz to make it work nice with browsers. self.send_header("Content-Encoding", 'gzip') if filtered_bytes is None: fs = os.fstat(f.fileno()) self.send_header('Content-Length', str(fs[6])) else: self.send_header('Content-Length', filtered_bytes) # begin no-cache patch # For standard requests. self.send_header("Cache-Control", "no-cache, no-store, " "must-revalidate") self.send_header("Pragma", "no-cache") self.send_header("Expires", "0") # end no-cache patch self.end_headers() return f
class _BaseBinaryWrapper: def __init__(self, stream: Union[typing.BinaryIO, bytes] = b""): if isinstance(stream, bytes) or isinstance(stream, bytearray): self.stream = BytesIO(stream) else: self.stream = stream # Wrappings: def close(self) -> None: return self.stream.close() def flush(self) -> None: return self.stream.flush() def read(self, n: int = -1) -> AnyStr: return self.stream.read(n) def readable(self) -> bool: return self.stream.readable() def readline(self, limit: int = -1) -> AnyStr: return self.stream.readline(limit) def readlines(self, hint: int = -1) -> List[AnyStr]: return self.stream.readlines(hint) def write(self, s: Union[bytes, bytearray]) -> int: return self.stream.write(s) def writable(self) -> bool: return self.stream.writable() def writelines(self, lines: Iterable[AnyStr]) -> None: self.stream.writelines(lines) def seek(self, offset: int, whence: int = 0) -> int: return self.stream.seek(offset, whence) def seekable(self) -> bool: return self.stream.seekable() def tell(self) -> int: return self.stream.tell() def fileno(self) -> int: return self.stream.fileno() def __enter__(self): self.stream.__enter__() return self def __exit__(self, exc_type, exc_val, exc_tb): self.stream.__exit__(exc_type, exc_val, exc_tb) # helper functions def readall(self): self.stream.seek(0) return self.stream.read() def getvalue(self): if isinstance(self.stream, BytesIO): return self.stream.getvalue() pos = self.stream.tell() ret = self.readall() self.stream.seek(pos) return ret def align(self, alignment=4): if offset := (self.tell() % alignment): self.seek(self.tell() + alignment - offset)
class DataIO(object): """ This class simply wraps a binary file or a bytes string and implements both the file and bytes interface. It allows an input to be provided as files of bytes and manipulated indifferently as a file or a bytes object. """ def __init__(self, f): if isinstance(f, bytes): from io import BytesIO self.f = BytesIO(f) else: self.f = f self.view = dataView(dataio=self) def __getitem__(self, i): stay = self.f.tell() sta = i.start if sta is None: sta = stay self.f.seek(sta, 0) if i.stop is None: data = self.f.read() else: data = self.f.read(i.stop - sta) self.f.seek(stay, 0) return data def size(self): stay = self.f.tell() self.f.seek(0, 2) sz = self.f.tell() self.f.seek(stay, 0) return sz def read(self, size=-1): return self.f.read(size) def readline(self, size=-1): return self.f.readline(size) def readlines(self, size=-1): return self.f.readlines(size) def xreadlines(self, size=-1): return self.f.xreadlines(size) def write(self, s): return self.f.write(s) def writelines(self, l): return self.f.writelines(l) def seek(self, offset, whence=0): return self.f.seek(offset, whence) def tell(self): return self.f.tell() def flush(self): return self.f.flush() def fileno(self): return self.f.fileno() def isatty(self): return self.f.isatty() def next(self): return self.f.next() def truncate(self, size=0): return self.f.truncate(size) def close(self): return self.f.close() @property def closed(self): return self.f.closed @property def encoding(self): return self.f.encoding @property def errors(self): return self.f.errors @property def mode(self): return self.f.mode @property def name(self): try: return self.f.name except AttributeError: s = bytes(self.f.getvalue()) return "(sc-%s...)" % ("".join(["%02x" % x for x in s])[:8]) filename = name @property def newlines(self): return self.f.newlines @property def softspace(self): return self.f.softspace
class Pdb(pdb.Pdb, ConfigurableClass, object): DefaultConfig = DefaultConfig config_filename = '.pdbrc.py' def __init__(self, *args, **kwds): self.ConfigFactory = kwds.pop('Config', None) self.start_lineno = kwds.pop('start_lineno', None) self.start_filename = kwds.pop('start_filename', None) self.config = self.get_config(self.ConfigFactory) self.config.setup(self) if self.config.disable_pytest_capturing: self._disable_pytest_capture_maybe() super(Pdb, self).__init__(*args, **kwds) self.prompt = self.config.prompt self.display_list = {} # frame --> (name --> last seen value) self.sticky = self.config.sticky_by_default self.first_time_sticky = self.sticky self.sticky_ranges = {} # frame --> (start, end) self.tb_lineno = {} # frame --> lineno where the exception raised self.history = [] self.show_hidden_frames = False self.hidden_frames = [] self.stdout = self.ensure_file_can_write_unicode(self.stdout) self.sticky_stdout = self.stdout def ensure_file_can_write_unicode(self, f): # Wrap with an encoder, but only if not already wrapped if (not hasattr(f, 'stream') and getattr(f, 'encoding', False) and f.encoding.lower() != 'utf-8'): f = codecs.getwriter('utf-8')(getattr(f, 'buffer', f)) return f def _disable_pytest_capture_maybe(self): try: import py.test # Force raising of ImportError if pytest is not installed. py.test.config except (ImportError, AttributeError): return try: capman = py.test.config.pluginmanager.getplugin('capturemanager') capman.suspendcapture() except KeyError: pass except AttributeError: # Newer pytest with support ready, or very old py.test for which # this hack does not work. pass def interaction(self, frame, traceback): # Restore the previous signal handler at the Pdb prompt. if getattr(pdb.Pdb, '_previous_sigint_handler', None): try: signal.signal(signal.SIGINT, pdb.Pdb._previous_sigint_handler) except ValueError: # ValueError: signal only works in main thread pass else: pdb.Pdb._previous_sigint_handler = None ret = self.setup(frame, traceback) if ret: # no interaction desired at this time (happens if .pdbrc contains # a command like "continue") self.forget() return if self.config.exec_if_unfocused: self.exec_if_unfocused() self.print_stack_entry(self.stack[self.curindex]) self.print_hidden_frames_count() completer = fancycompleter.setup() old_completer = completer.config.readline.get_completer() completer.config.readline.set_completer(self.complete) self.config.before_interaction_hook(self) # Use _cmdloop on py3 which catches KeyboardInterrupt. getattr(self, '_cmdloop', self.cmdloop)() completer.config.readline.set_completer(old_completer) self.forget() def print_hidden_frames_count(self): n = len(self.hidden_frames) if n and self.config.show_hidden_frames_count: plural = n > 1 and "s" or "" print( " %d frame%s hidden (try 'help hidden_frames')" % (n, plural), file=self.stdout, ) def exec_if_unfocused(self): import os import wmctrl term = os.getenv('TERM', '') try: winid = int(os.getenv('WINDOWID')) except (TypeError, ValueError): return # cannot find WINDOWID of the terminal active_win = wmctrl.Window.get_active() if not active_win or (int(active_win.id, 16) != winid) and \ not (active_win.wm_class == 'emacs.Emacs' and term.startswith('eterm')): os.system(self.config.exec_if_unfocused) def setup(self, frame, tb): ret = super(Pdb, self).setup(frame, tb) if not ret: while tb: lineno = lasti2lineno(tb.tb_frame.f_code, tb.tb_lasti) self.tb_lineno[tb.tb_frame] = lineno tb = tb.tb_next return ret def _is_hidden(self, frame): if not self.config.enable_hidden_frames: return False # Decorated code is always considered to be hidden. consts = frame.f_code.co_consts if consts and consts[-1] is _HIDE_FRAME: return True # Do not hide if this frame contains the initial set_trace. if frame is getattr(self, "_via_set_trace_frame", None): return False if frame.f_globals.get('__unittest'): return True if frame.f_locals.get('__tracebackhide__') \ or frame.f_globals.get('__tracebackhide__'): return True def get_stack(self, f, t): # show all the frames, except the ones that explicitly ask to be hidden fullstack, _ = super(Pdb, self).get_stack(f, t) self.fullstack = fullstack return self.compute_stack(fullstack) def compute_stack(self, fullstack): if self.show_hidden_frames: return fullstack, len(fullstack) - 1 self.hidden_frames = [] newstack = [] for frame, lineno in fullstack: if self._is_hidden(frame): self.hidden_frames.append((frame, lineno)) else: newstack.append((frame, lineno)) stack = newstack i = max(0, len(stack) - 1) return stack, i def refresh_stack(self): """ Recompute the stack after e.g. show_hidden_frames has been modified """ self.stack, _ = self.compute_stack(self.fullstack) # find the current frame in the new stack for i, (frame, _) in enumerate(self.stack): if frame is self.curframe: self.curindex = i break else: self.curindex = len(self.stack) - 1 self.curframe = self.stack[-1][0] self.print_current_stack_entry() def forget(self): if not hasattr(self, 'lineno'): # Only forget if not used with recursive set_trace. super(Pdb, self).forget() self.raise_lineno = {} @classmethod def _get_all_completions(cls, complete, text): r = [] i = 0 while True: comp = complete(text, i) if comp is None: break i += 1 r.append(comp) return r def complete(self, text, state): """Handle completions from fancycompleter and original pdb.""" if state == 0: if GLOBAL_PDB: GLOBAL_PDB._pdbpp_completing = True mydict = self.curframe.f_globals.copy() mydict.update(self.curframe.f_locals) completer = Completer(mydict) self._completions = self._get_all_completions( completer.complete, text) real_pdb = super(Pdb, self) for x in self._get_all_completions(real_pdb.complete, text): if x not in self._completions: self._completions.append(x) if GLOBAL_PDB: del GLOBAL_PDB._pdbpp_completing # Remove "\t" from fancycompleter if there are pdb completions. if len(self._completions) > 1 and self._completions[0] == "\t": self._completions.pop(0) try: return self._completions[state] except IndexError: return None def _init_pygments(self): if not self.config.use_pygments: return False try: from pygments.lexers import PythonLexer from pygments.formatters import TerminalFormatter, Terminal256Formatter except ImportError: return False if hasattr(self, '_fmt'): return True if hasattr(self.config, 'formatter'): self._fmt = self.config.formatter else: if (self.config.use_terminal256formatter or (self.config.use_terminal256formatter is None and "256color" in os.environ.get("TERM", ""))): Formatter = Terminal256Formatter else: Formatter = TerminalFormatter self._fmt = Formatter(bg=self.config.bg, colorscheme=self.config.colorscheme) self._lexer = PythonLexer() return True stack_entry_regexp = re.compile(r'(.*?)\(([0-9]+?)\)(.*)', re.DOTALL) def format_stack_entry(self, frame_lineno, lprefix=': '): entry = super(Pdb, self).format_stack_entry(frame_lineno, lprefix) entry = self.try_to_decode(entry) if self.config.highlight: match = self.stack_entry_regexp.match(entry) if match: filename, lineno, other = match.groups() filename = Color.set(self.config.filename_color, filename) lineno = Color.set(self.config.line_number_color, lineno) entry = '%s(%s)%s' % (filename, lineno, other) return entry def try_to_decode(self, s): for encoding in self.config.encodings: try: return s.decode(encoding) except (UnicodeDecodeError, AttributeError): pass return s def format_source(self, src): if not self._init_pygments(): return src from pygments import highlight src = self.try_to_decode(src) return highlight(src, self._lexer, self._fmt) def format_line(self, lineno, marker, line): lineno = '%4d' % lineno if self.config.highlight: lineno = Color.set(self.config.line_number_color, lineno) line = '%s %2s %s' % (lineno, marker, line) if self.config.highlight and marker == '->': line = setbgcolor(line, self.config.current_line_color) return line def parseline(self, line): if line.startswith('!!'): # force the "standard" behaviour, i.e. first check for the # command, then for the variable name to display line = line[2:] return super(Pdb, self).parseline(line) # pdb++ "smart command mode": don't execute commands if a variable # with the name exits in the current contex; this prevents pdb to quit # if you type e.g. 'r[0]' by mystake. cmd, arg, newline = super(Pdb, self).parseline(line) if arg and arg.endswith('?'): if hasattr(self, 'do_' + cmd): cmd, arg = ('help', cmd) elif arg.endswith('??'): arg = cmd + arg.split('?')[0] cmd = 'source' self.do_inspect(arg) self.stdout.write('%-28s\n' % Color.set(Color.red, 'Source:')) else: arg = cmd + arg.split('?')[0] cmd = 'inspect' return cmd, arg, newline # f-strings. if (cmd == 'f' and len(newline) > 1 and (newline[1] == "'" or newline[1] == '"')): return super(Pdb, self).parseline('!' + line) if cmd and hasattr(self, 'do_' + cmd) and (cmd in self.curframe.f_globals or cmd in self.curframe.f_locals or arg.startswith('=')): return super(Pdb, self).parseline('!' + line) if cmd == "list" and arg.startswith("("): # heuristic: handle "list(..." as the builtin. line = '!' + line return super(Pdb, self).parseline(line) return cmd, arg, newline def do_inspect(self, arg): obj = self._getval(arg) data = OrderedDict() data['Type'] = type(obj).__name__ data['String Form'] = str(obj).strip() try: data['Length'] = len(obj) except TypeError: pass try: data['File'] = inspect.getabsfile(obj) except TypeError: pass if (isinstance(obj, type) and hasattr(obj, '__init__') and getattr(obj, '__module__') != '__builtin__'): # Class - show definition and docstring for constructor data['Docstring'] = obj.__doc__ data['Constructor information'] = '' try: data[' Definition'] = '%s%s' % (arg, signature(obj)) except ValueError: pass data[' Docstring'] = obj.__init__.__doc__ else: try: data['Definition'] = '%s%s' % (arg, signature(obj)) except (TypeError, ValueError): pass data['Docstring'] = obj.__doc__ for key, value in data.items(): formatted_key = Color.set(Color.red, key + ':') self.stdout.write('%-28s %s\n' % (formatted_key, value)) def default(self, line): self.history.append(line) return super(Pdb, self).default(line) def do_help(self, arg): try: return super(Pdb, self).do_help(arg) except AttributeError: print("*** No help for '{command}'".format(command=arg), file=self.stdout) do_help.__doc__ = pdb.Pdb.do_help.__doc__ def help_hidden_frames(self): print("""\ Some frames might be marked as "hidden": by default, hidden frames are not shown in the stack trace, and cannot be reached using ``up`` and ``down``. You can use ``hf_unhide`` to tell pdb to ignore the hidden status (i.e., to treat hidden frames as normal ones), and ``hf_hide`` to hide them again. ``hf_list`` prints a list of hidden frames. Frames can marked as hidden in the following ways: - by using the @pdb.hideframe function decorator - by having __tracebackhide__=True in the locals or the globals of the function (this hides py.test internal stuff) - by having __unittest=True in the globals of the function (this hides unittest internal stuff) """, file=self.stdout) def do_hf_unhide(self, arg): """ {hf_show} unhide hidden frames, i.e. make it possible to ``up`` or ``down`` there """ self.show_hidden_frames = True self.refresh_stack() def do_hf_hide(self, arg): """ {hf_hide} (re)hide hidden frames, if they have been unhidden by ``hf_unhide`` """ self.show_hidden_frames = False self.refresh_stack() def do_hf_list(self, arg): for frame_lineno in self.hidden_frames: print(self.format_stack_entry(frame_lineno, pdb.line_prefix), file=self.stdout) def do_longlist(self, arg): """ {longlist|ll} List source code for the current function. Differently than list, the whole function is displayed; the current line is marked with '->'. In case of post-mortem debugging, the line which effectively raised the exception is marked with '>>'. If the 'highlight' config option is set and pygments is installed, the source code is colorized. """ self.lastcmd = 'longlist' self._printlonglist() def _printlonglist(self, linerange=None): try: if self.curframe.f_code.co_name == '<module>': # inspect.getsourcelines is buggy in this case: if we just # pass the frame, it returns the source for the first function # defined in the module. Instead, we want the full source # code of the module lines, _ = inspect.findsource(self.curframe) lineno = 1 else: try: lines, lineno = inspect.getsourcelines(self.curframe) except Exception as e: print('** Error in inspect.getsourcelines: %s **' % e, file=self.stdout) return except IOError as e: print('** Error: %s **' % e, file=self.stdout) return if linerange: start, end = linerange start = max(start, lineno) end = min(end, lineno + len(lines)) lines = lines[start - lineno:end - lineno] lineno = start self._print_lines_pdbpp(lines, lineno) def _print_lines_pdbpp(self, lines, lineno, print_markers=True): exc_lineno = self.tb_lineno.get(self.curframe, None) lines = [line[:-1] for line in lines] # remove the trailing '\n' lines = [line.replace('\t', ' ') for line in lines] # force tabs to 4 spaces width, height = self.get_terminal_size() if self.config.truncate_long_lines: maxlength = max(width - 9, 16) lines = [line[:maxlength] for line in lines] else: maxlength = max(map(len, lines)) if self.config.highlight: lines = [line.ljust(maxlength) for line in lines] src = self.format_source('\n'.join(lines)) lines = src.splitlines() if height >= 6: last_marker_line = max(self.curframe.f_lineno, exc_lineno if exc_lineno else 0) - lineno if last_marker_line >= 0: maxlines = last_marker_line + height * 2 // 3 if len(lines) > maxlines: lines = lines[:maxlines] lines.append('...') for i, line in enumerate(lines): marker = '' if lineno == self.curframe.f_lineno and print_markers: marker = '->' elif lineno == exc_lineno and print_markers: marker = '>>' lines[i] = self.format_line(lineno, marker, line) lineno += 1 print('\n'.join(lines), file=self.stdout) do_ll = do_longlist def do_list(self, arg): oldstdout = self.stdout self.stdout = StringIO() super(Pdb, self).do_list(arg) src = self.format_source(self.stdout.getvalue()) self.stdout = oldstdout print(src, file=self.stdout, end='') do_list.__doc__ = pdb.Pdb.do_list.__doc__ do_l = do_list def do_continue(self, arg): if arg != '': self.do_tbreak(arg) return super(Pdb, self).do_continue('') do_continue.__doc__ = pdb.Pdb.do_continue.__doc__ do_c = do_cont = do_continue def do_pp(self, arg): width, height = self.get_terminal_size() try: pprint.pprint(self._getval(arg), self.stdout, width=width) except: pass do_pp.__doc__ = pdb.Pdb.do_pp.__doc__ def do_debug(self, arg): # this is a hack (as usual :-)) # # inside the original do_debug, there is a call to the global "Pdb" to # instantiate the recursive debugger: we want to intercept this call # and instantiate *our* Pdb, passing our custom config. Therefore we # dynamically rebind the globals. Config = self.ConfigFactory class PdbppWithConfig(self.__class__): def __init__(self, *args): kwds = dict(Config=Config) super(PdbppWithConfig, self).__init__(*args, **kwds) # Backport of fix for bpo-31078 (not yet merged). self.use_rawinput = self.use_rawinput if sys.version_info < (3, ): do_debug_func = pdb.Pdb.do_debug.im_func else: do_debug_func = pdb.Pdb.do_debug newglobals = do_debug_func.__globals__.copy() newglobals['Pdb'] = PdbppWithConfig orig_do_debug = rebind_globals(do_debug_func, newglobals) # Handle any exception, e.g. SyntaxErrors. # This is about to be improved in Python itself (3.8, 3.7.3?). try: return orig_do_debug(self, arg) except Exception: exc_info = sys.exc_info()[:2] msg = traceback.format_exception_only(*exc_info)[-1].strip() if hasattr(self, 'error'): self.error(msg) else: # For py27. print('***', msg, file=self.stdout) do_debug.__doc__ = pdb.Pdb.do_debug.__doc__ def do_interact(self, arg): """ interact Start an interative interpreter whose global namespace contains all the names found in the current scope. """ ns = self.curframe.f_globals.copy() ns.update(self.curframe.f_locals) code.interact("*interactive*", local=ns) def do_track(self, arg): """ track expression Display a graph showing which objects are referred by the value of the expression. This command requires pypy to be in the current PYTHONPATH. """ try: from rpython.translator.tool.reftracker import track except ImportError: print('** cannot import pypy.translator.tool.reftracker **', file=self.stdout) return try: val = self._getval(arg) except: pass else: track(val) def _get_display_list(self): return self.display_list.setdefault(self.curframe, {}) def _getval_or_undefined(self, arg): try: return eval(arg, self.curframe.f_globals, self.curframe.f_locals) except NameError: return undefined def do_display(self, arg): """ display expression Add expression to the display list; expressions in this list are evaluated at each step, and printed every time its value changes. WARNING: since the expressions is evaluated multiple time, pay attention not to put expressions with side-effects in the display list. """ try: value = self._getval_or_undefined(arg) except: return self._get_display_list()[arg] = value def do_undisplay(self, arg): """ undisplay expression Remove expression from the display list. """ try: del self._get_display_list()[arg] except KeyError: print('** %s not in the display list **' % arg, file=self.stdout) def _print_if_sticky(self): old_stdout = self.stdout self.stdout = self.sticky_stdout if self.sticky: if self.first_time_sticky: self.first_time_sticky = False else: self.stdout.write(CLEARSCREEN) frame, lineno = self.stack[self.curindex] filename = self.canonic(frame.f_code.co_filename) s = '> %s(%r)' % (filename, lineno) print(s, file=self.stdout) print(file=self.stdout) sticky_range = self.sticky_ranges.get(self.curframe, None) self._printlonglist(sticky_range) if '__exception__' in frame.f_locals: exc = frame.f_locals['__exception__'] if len(exc) == 2: exc_type, exc_value = exc s = '' try: try: s = exc_type.__name__ except AttributeError: s = str(exc_type) if exc_value is not None: s += ': ' s += str(exc_value) except KeyboardInterrupt: raise except: s += '(unprintable exception)' print(Color.set(self.config.line_number_color, ' ' + s), file=self.stdout) return if '__return__' in frame.f_locals: rv = frame.f_locals['__return__'] try: s = repr(rv) except KeyboardInterrupt: raise except: s = '(unprintable return value)' print(Color.set(self.config.line_number_color, ' return ' + s), file=self.stdout) self.stdout = old_stdout def do_sticky(self, arg): """ sticky [start end] Toggle sticky mode. When in sticky mode, it clear the screen and longlist the current functions, making the source appearing always in the same position. Useful to follow the flow control of a function when doing step-by-step execution. If ``start`` and ``end`` are given, sticky mode is enabled and only lines within that range (extremes included) will be displayed. """ arg_list = arg.split() if len(arg_list) == 1 or len(arg_list) == 3: # We want to output to a tty self.sticky_stdout = open(arg_list[0], 'w') arg = " ".join(arg_list[1:]) if arg: try: start, end = map(int, arg.split()) except ValueError: print('** Error when parsing argument: %s **' % arg, file=self.stdout) return self.sticky = True self.sticky_ranges[self.curframe] = start, end + 1 else: self.sticky = not self.sticky self.sticky_range = None self._print_if_sticky() def print_stack_trace(self): try: for frame_index, frame_lineno in enumerate(self.stack): self.print_stack_entry(frame_lineno, frame_index=frame_index) except KeyboardInterrupt: pass def print_stack_entry(self, frame_lineno, prompt_prefix=pdb.line_prefix, frame_index=None): frame_index = frame_index if frame_index is not None else self.curindex frame, lineno = frame_lineno if frame is self.curframe: print("[%d] >" % frame_index, file=self.stdout, end=" ") else: print("[%d] " % frame_index, file=self.stdout, end=" ") print(self.format_stack_entry(frame_lineno, prompt_prefix), file=self.stdout) def print_current_stack_entry(self): if self.sticky: self._print_if_sticky() else: self.print_stack_entry(self.stack[self.curindex]) def preloop(self): RED = "\033[1;31m" RESET = "\033[0;0m" self._print_if_sticky() display_list = self._get_display_list() for expr, oldvalue in display_list.items(): newvalue = self._getval_or_undefined(expr) # check for identity first; this prevents custom __eq__ to # be called at every loop, and also prevents instances # whose fields are changed to be displayed if newvalue is not oldvalue or newvalue != oldvalue: display_list[expr] = newvalue print(RED + '%s: %r --> %r' % (expr, oldvalue, newvalue), RESET, file=self.sticky_stdout) else: print('%s: %r' % (expr, oldvalue), file=self.sticky_stdout) def _get_position_of_arg(self, arg): try: obj = self._getval(arg) except: return None, None, None if isinstance(obj, str): return obj, 1, None try: filename = inspect.getabsfile(obj) lines, lineno = inspect.getsourcelines(obj) except (IOError, TypeError) as e: print('** Error: %s **' % e, file=self.stdout) return None, None, None return filename, lineno, lines def do_source(self, arg): _, lineno, lines = self._get_position_of_arg(arg) if lineno is None: return self._print_lines_pdbpp(lines, lineno, print_markers=False) def do_frame(self, arg): try: arg = int(arg) except (ValueError, TypeError): print('*** Expected a number, got "{0}"'.format(arg), file=self.stdout) return if arg < 0 or arg >= len(self.stack): print('*** Out of range', file=self.stdout) else: self.curindex = arg self.curframe = self.stack[self.curindex][0] self.curframe_locals = self.curframe.f_locals self.print_current_stack_entry() self.lineno = None do_f = do_frame def do_up(self, arg='1'): arg = '1' if arg == '' else arg try: arg = int(arg) except (ValueError, TypeError): print('*** Expected a number, got "{0}"'.format(arg), file=self.stdout) return if self.curindex - arg < 0: print('*** Oldest frame', file=self.stdout) else: self.curindex = self.curindex - arg self.curframe = self.stack[self.curindex][0] self.curframe_locals = self.curframe.f_locals self.print_current_stack_entry() self.lineno = None do_up.__doc__ = pdb.Pdb.do_up.__doc__ do_u = do_up def do_down(self, arg='1'): arg = '1' if arg == '' else arg try: arg = int(arg) except (ValueError, TypeError): print('*** Expected a number, got "{0}"'.format(arg), file=self.stdout) return if self.curindex + arg >= len(self.stack): print('*** Newest frame', file=self.stdout) else: self.curindex = self.curindex + arg self.curframe = self.stack[self.curindex][0] self.curframe_locals = self.curframe.f_locals self.print_current_stack_entry() self.lineno = None do_down.__doc__ = pdb.Pdb.do_down.__doc__ do_d = do_down def get_terminal_size(self): fallback = (80, 24) try: from shutil import get_terminal_size except ImportError: try: import termios import fcntl import struct fd = self.stdout.fileno() call = fcntl.ioctl(fd, termios.TIOCGWINSZ, "\x00" * 8) height, width = struct.unpack("hhhh", call)[:2] except (SystemExit, KeyboardInterrupt): raise except: width = int(os.environ.get('COLUMNS', fallback[0])) height = int(os.environ.get('COLUMNS', fallback[1])) # Work around above returning width, height = 0, 0 in Emacs width = width if width != 0 else fallback[0] height = height if height != 0 else fallback[1] return width, height else: # We are not going to care about the os environment variable # of LINES and COLUMNS because it refers to the wrong tty try: size = os.get_terminal_size(self.stdout.fileno()) except (AttributeError, ValueError, OSError): # stdout is None, closed, detached, or not a terminal, or # os.get_terminal_size() is unsupported size = os.terminal_size(fallback) columns = size.columns lines = size.lines return os.terminal_size((columns, lines)) def _open_editor(self, editor, lineno, filename): filename = filename.replace('"', '\\"') os.system('%s +%d "%s"' % (editor, lineno, filename)) def _get_current_position(self): frame = self.curframe lineno = frame.f_lineno filename = os.path.abspath(frame.f_code.co_filename) return filename, lineno def do_edit(self, arg): "Open an editor visiting the current file at the current line" if arg == '': filename, lineno = self._get_current_position() else: filename, lineno, _ = self._get_position_of_arg(arg) if filename is None: return # this case handles code generated with py.code.Source() # filename is something like '<0-codegen foo.py:18>' match = re.match(r'.*<\d+-codegen (.*):(\d+)>', filename) if match: filename = match.group(1) lineno = int(match.group(2)) editor = self.config.editor self._open_editor(editor, lineno, filename) do_ed = do_edit def _get_history(self): return [s for s in self.history if not side_effects_free.match(s)] def _get_history_text(self): import linecache line = linecache.getline(self.start_filename, self.start_lineno) nspaces = len(line) - len(line.lstrip()) indent = ' ' * nspaces history = [indent + s for s in self._get_history()] return '\n'.join(history) + '\n' def _open_stdin_paste(self, stdin_paste, lineno, filename, text): proc = subprocess.Popen( [stdin_paste, '+%d' % lineno, filename], stdin=subprocess.PIPE) proc.stdin.write(text) proc.stdin.close() def _put(self, text): stdin_paste = self.config.stdin_paste if stdin_paste is None: print('** Error: the "stdin_paste" option is not configured **', file=self.stdout) filename = self.start_filename lineno = self.start_lineno self._open_stdin_paste(stdin_paste, lineno, filename, text) def do_put(self, arg): text = self._get_history_text() self._put(text) def do_paste(self, arg): arg = arg.strip() old_stdout = self.stdout self.stdout = StringIO() self.onecmd(arg) text = self.stdout.getvalue() self.stdout = old_stdout sys.stdout.write(text) self._put(text) def set_trace(self, frame=None): """Remember starting frame. This is used with pytest, which does not use pdb.set_trace(). """ if frame is None: frame = sys._getframe().f_back self._via_set_trace_frame = frame return super(Pdb, self).set_trace(frame) def is_skipped_module(self, module_name): """Backport for https://bugs.python.org/issue36130. Fixed in Python 3.8+. """ if module_name is None: return False return super(Pdb, self).is_skipped_module(module_name)
class DataIO(BinFormat): """This class wraps a binary file or a string of bytes and provides both the file and bytes API. """ def __init__(self, f): if isinstance(f,bytes): self.f=BytesIO(f) else: self.f=f def __getitem__(self,i): stay = self.f.tell() sta = i.start or stay self.f.seek(sta,0) if i.stop is None: data = self.f.read() else: data = self.f.read(i.stop-sta) self.f.seek(stay,0) return data def read(self,size=-1): return self.f.read(size) def readline(self,size=-1): return self.f.readline(size) def readlines(self,size=-1): return self.f.readlines(size) def xreadlines(self,size=-1): return self.f.xreadlines(size) def write(self,s): return self.f.write(s) def writelines(self,l): return self.f.writelines(l) def seek(self,offset,whence=0): return self.f.seek(offset,whence) def tell(self): return self.f.tell() def flush(self): return self.f.flush() def fileno(self): return self.f.fileno() def isatty(self): return self.f.isatty() def next(self): return self.f.next() def truncate(self,size=0): return self.f.truncate(size) def close(self): return self.f.close() @property def closed(self): return self.f.closed @property def encoding(self): return self.f.encoding @property def errors(self): return self.f.errors @property def mode(self): return self.f.mode @property def name(self): try: return self.f.name except AttributeError: try: from builtins import bytes except ImportError: pass s = bytes(self.f.getvalue()) return '(sc-%s...)'%(''.join(["%02x"%x for x in s])[:8]) filename = name @property def newlines(self): return self.f.newlines @property def softspace(self): return self.f.softspace
class VerifiableStream(BinaryIO): """A binary stream whose contents can be verified to not have changed. The stream does not accept a HMAC key, but generates it randomly as a nonce. While unusual, this is intentional -- these streams are meant to be used as part of model serialization, where their nonces and HMAC codes are stored in a cryptographically signed metadata file. In other words, the HMAC simply ensures that stream's data has not changed, and does not guarantee the data's origin -- that's the metadata signature's job. The stream is meant to be used in the following sequence: - instantiate the stream - write all data to the stream (the stream is not readable yet!) - call "finalize()" on the stream, saving the returned nonce and HMAC code - read data from the stream (the stream is not writable any more!) """ def __init__(self): """Create a new VerifiableStream with a random nonce.""" self._finalized = False self._random_nonce = os.urandom( 16) # this is bytes, be careful trying to add strings to it self._underlying_stream = BytesIO() self._hmac_state = hmac.new(self._random_nonce, digestmod=HASHER) def _ensure_finalized(self): """Raise an error if the stream has not already been finalized.""" if not self._finalized: raise AssertionError( "Expected the stream to be finalized, but it was not!") def _ensure_not_finalized(self): """Raise an error if the stream has already been finalized.""" if self._finalized: raise AssertionError( "Expected the stream to not be finalized, but it was!") def finalize(self): """Calculate the HMAC code for the stream, disable writing and enable reading. Returns: tuple (nonce, HMAC code) (both of type string) """ self._ensure_not_finalized() self._finalized = True nonce_string = _convert_base64_bytes_to_string(self._random_nonce) hmac_string = _convert_base64_bytes_to_string( self._hmac_state.digest()) return nonce_string, hmac_string # methods for writing require that the stream not be finalized def writable(self) -> bool: """Return True if the stream is writable, and False otherwise.""" if self._finalized: return False else: return self._underlying_stream.writable() @validate(b=bytes) def write(self, b: bytes) -> int: """Write the given binary data to the stream, and include it in the HMAC calculation.""" self._ensure_not_finalized() num_bytes = self._underlying_stream.write(b) self._hmac_state.update(b) return num_bytes def writelines(self, lines: Iterable[bytes]) -> None: """Write lines to a stream""" self._ensure_not_finalized( ) # technically done by `write` but doesn't hurt to be safe for line in lines: self.write(line) return None # methods for reading require that the stream is finalized def readable(self) -> bool: """Return True if the stream is readable, and False otherwise.""" if self._finalized: return self._underlying_stream.readable() else: return False def read(self, size=None) -> bytes: """Read bytes from stream""" self._ensure_finalized() return self._underlying_stream.read(size) def readall(self) -> bytes: """Read lines from stream""" raise NotImplementedError( "`VerifiablStream` does not implement `readall` since the underlying BtytesIO does not " "implement it.") def readline(self, size=None) -> bytes: """Read a line from stream""" self._ensure_finalized() return self._underlying_stream.readline(size) def readlines(self, size=None) -> List[bytes]: """Read lines from stream""" self._ensure_finalized() return self._underlying_stream.readlines(size) def read1(self, size) -> bytes: """Read bytes from stream""" self._ensure_finalized() return self._underlying_stream.read1(size) def readinto(self, b) -> Optional[int]: """Read bytes into another buffer""" self._ensure_finalized() return self._underlying_stream.readinto(b) def readinto1(self, b) -> Optional[int]: """Read bytes into another buffer""" self._ensure_finalized() return self._underlying_stream.readinto1(b) # seeking requires a finalized stream def seekable(self): """Return True if the read pointer in the stream can be moved, and False otherwise.""" if self._finalized: return self._underlying_stream.seekable() else: return False def seek(self, *args, **kwargs) -> int: """Seek to a new position. Return the new position""" self._ensure_finalized() return self._underlying_stream.seek(*args, **kwargs) def truncate(self, size: Optional[int] = ...) -> None: """Truncate the stream""" raise NotImplementedError( "`VerifiableStream` does not support truncation. It is too " "complicated to keep track of the hmac digests") def close(self): """Close the stream, discarding its data. Will raise an error if not finalized yet.""" if self._finalized: return self._underlying_stream.close() else: raise AssertionError( "Attempting to close an unfinalized VerifiableStream. This is " "almost certainly a bug.") # a bunch of attributes/methods that are always accessible def isatty(self) -> bool: """Determine whether this is a terminal""" return self._underlying_stream.isatty() @property def closed(self) -> bool: """Determine whether the stream is closed""" return self._underlying_stream.closed def fileno(self) -> int: """Return the underlying file descriptor""" # this will technically raise UnsuportedOperation, but better to let BytesIO do that return self._underlying_stream.fileno() def mode(self) -> str: """Return the underlying file descriptor""" # this doesn't exist for the underlying stream raise AssertionError( "`VerifiableStream` does not have a mode. This is probably a bug in " "something assuming that the stream is a backed by a file") def name(self) -> str: """Return the underlying file descriptor""" # this doesn't exist for the underlying stream raise AssertionError( "`VerifiableStream` does not have a name. This is probably a bug in " "something assuming the stream is a file descriptor") def flush(self) -> None: """Flush the underlying stream""" # this technically does nothing in BytesIO return self._underlying_stream.flush() def tell(self) -> int: """Tell the current position""" return self._underlying_stream.tell() # context manager methods def __enter__(self) -> "VerifiableStream": """Enter""" return self def __exit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> bool: """Exit""" return self._underlying_stream.__exit__(exc_type, exc_val, exc_tb)
def test_svg(self): def mock_open(name, mode): self.assertEqual(mode, "rb") return stream class MockOle: def __init__(ole, file): self.assertIs(file, stream) super().__init__() def openstream(ole, name): self.assertEqual(name, "FileHeader") return stream class mock_os: def stat(fileno): return None stream = BytesIO() stream.fileno = lambda: stream sch = ( b"\x00", b"|RECORD=31|FONTIDCOUNT=1|SIZE1=10|FONTNAME1=Times New Roman" b"|SYSTEMFONT=1" b"|AREACOLOR=16317695|BORDERON=T|CUSTOMX=|CUSTOMY=" b"|DISPLAY_UNIT=4|HOTSPOTGRIDON=T|HOTSPOTGRIDSIZE=" b"|SNAPGRIDON=T|SNAPGRIDSIZE=|VISIBLEGRIDON=T" b"|VISIBLEGRIDSIZE=10|ISBOC=T|SHEETNUMBERSPACESIZE=4" b"|USEMBCS=T\x00", b"|RECORD=15|LOCATION.X=100|LOCATION.Y=200|XSIZE=40|YSIZE=30" b"|COLOR=7846673|AREACOLOR=3381725|ISSOLID=T|OWNERPARTID=-1" b"|UNIQUEID=\x00", ) for list in sch: stream.write(len(list).to_bytes(4, "little")) stream.write(list) stream.seek(0) output = StringIO() with patch("altium.open", mock_open), \ patch("altium.OleFileIO", MockOle), \ patch("altium.os", mock_os), \ redirect_stdout(output): altium.convert("dummy.SchDoc", svg.Renderer) output = XML(output.getvalue()) SVG = "{http://www.w3.org/2000/svg}" self.assertEqual(output.tag, SVG + "svg") for [dimension, expected] in (("width", 11.506), ("height", 7.606)): with self.subTest(dimension): value = output.get(dimension) self.assertTrue(value.endswith("in")) self.assertAlmostEqual(float(value[:-2]), expected, 3) for [name, value] in ( ("viewBox", "-0.3,-760.3 1150.6,760.6"), ("stroke-width", "1"), ): with self.subTest(name): self.assertEqual(output.get(name), value) [style, defs, border, sheet] = output self.assertEqual(style.tag, SVG + "style") self.assertEqual(defs.tag, SVG + "defs") self.assertEqual(border.tag, SVG + "g") self.assertCountEqual(border.items(), ( ("transform", "translate(0, -760)"), )) self.assertEqual(sheet.tag, SVG + "rect") self.assertCountEqual(sheet.items(), ( ("transform", "translate(100, -200)"), ("width", "40"), ("height", "30"), ("stroke-width", "0.6"), ("class", "solid"), ("style", "fill: #DD9933; stroke: #11BB77"), ))
class DataIO(object): def __init__(self, f): if isinstance(f, bytes): self.f = BytesIO(f) else: self.f = f def __getitem__(self, i): self.f.seek(i.start, 0) return self.f.read(i.stop - i.start) def read(self, size=-1): return self.f.read(size) def readline(self, size=-1): return self.f.readline(size) def readlines(self, size=-1): return self.f.readlines(size) def xreadlines(self, size=-1): return self.f.xreadlines(size) def write(self, s): return self.f.write(s) def writelines(self, l): return self.f.writelines(l) def seek(self, offset, whence=0): return self.f.seek(offset, whence) def tell(self): return self.f.tell() def flush(self): return self.f.flush() def fileno(self): return self.f.fileno() def isatty(self): return self.f.isatty() def next(self): return self.f.next() def truncate(self, size=0): return self.f.truncate(size) def close(self): return self.f.close() @property def closed(self): return self.f.closed @property def encoding(self): return self.f.encoding @property def errors(self): return self.f.errors @property def mode(self): return self.f.mode @property def name(self): try: return self.f.name except AttributeError: from builtins import bytes s = bytes(self.f.getvalue()) return '(sc-%s...)' % (''.join(["%02x" % x for x in s])[:8]) filename = name @property def newlines(self): return self.f.newlines @property def softspace(self): return self.f.softspace