def test_stderr_bad_encoding(crx_bytes): """The errors and warnings can include cited garbage from invalid files and we should handle that gracefully.""" with pytest.raises(HatanakaException) as excinfo: crx2rnx(crx_bytes[:-1] + b'\xFF\0') msg = excinfo.value.args[0] assert msg.startswith('The file seems to be truncated') assert msg.endswith('\\xff<end')
def test_non_ascii_is_tolerated(rnx_bytes, crx_bytes): def add_non_ascii(txt): return txt.replace( b'VERSION / TYPE', b'VERSION / TYPE' + 'õäü'.encode().ljust(60) + b'COMMENT\n') converted = crx2rnx(add_non_ascii(crx_bytes)) assert clean(converted) == clean(add_non_ascii(rnx_bytes)) converted = rnx2crx(add_non_ascii(rnx_bytes)) assert clean(converted) == clean(add_non_ascii(crx_bytes))
def test_invalid(): with pytest.raises(HatanakaException) as excinfo: crx2rnx('blah') msg = excinfo.value.args[0] assert msg.startswith('The file seems to be truncated in the middle.')
def test_crx2rnx_extra_args_good(rnx_str, crx_str): converted = crx2rnx(crx_str, skip_strange_epochs=True) assert clean(converted) == clean(rnx_str)
def test_crx2rnx_bytes_stream(crx_bytes_stream, rnx_bytes): assert clean(crx2rnx(crx_bytes_stream)) == clean(rnx_bytes)
def test_crx2rnx_str_stream(crx_str_stream, rnx_str): assert crx2rnx(crx_str_stream) == rnx_str
def test_crx2rnx_bytes(crx_bytes, rnx_bytes): assert clean(crx2rnx(crx_bytes)) == clean(rnx_bytes)
def test_crx2rnx_str(crx_str, rnx_str): assert crx2rnx(crx_str) == rnx_str
def opener(fn: T.TextIO | Path, header: bool = False) -> T.Iterator[T.TextIO]: """provides file handle for regular ASCII or gzip files transparently""" if isinstance(fn, str): fn = Path(fn).expanduser() if isinstance(fn, io.StringIO): fn.seek(0) yield fn elif isinstance(fn, Path): # need to have this check for Windows if not fn.is_file(): raise FileNotFoundError(fn) finf = fn.stat() if finf.st_size > 100e6: logging.info(f"opening {finf.st_size/1e6} MByte {fn.name}") suffix = fn.suffix.lower() if suffix == ".gz": with gzip.open(fn, "rt") as f: _, is_crinex = rinex_version(first_nonblank_line(f)) f.seek(0) if is_crinex and not header: # Conversion to string is necessary because of a quirk where gzip.open() # even with 'rt' doesn't decompress until read. f = io.StringIO(crx2rnx(f.read())) yield f elif suffix == ".bz2": # this is for plain bzip2 files, NOT tar.bz2, which requires f.seek(512) with bz2.open(fn, "rt") as f: _, is_crinex = rinex_version(first_nonblank_line(f)) f.seek(0) if is_crinex and not header: f = io.StringIO(crx2rnx(f.read())) yield f elif suffix == ".zip": with zipfile.ZipFile(fn, "r") as z: flist = z.namelist() for rinexfn in flist: with z.open(rinexfn, "r") as bf: f = io.StringIO( io.TextIOWrapper( bf, encoding="ascii", errors="ignore").read() # type: ignore ) yield f elif suffix == ".z": if unlzw is None: raise ImportError("pip install unlzw3") with fn.open("rb") as zu: with io.StringIO(unlzw(zu.read()).decode("ascii")) as f: yield f else: # assume not compressed (or Hatanaka) with fn.open("r", encoding="ascii", errors="ignore") as f: _, is_crinex = rinex_version(first_nonblank_line(f)) f.seek(0) if is_crinex and not header: f = io.StringIO(crx2rnx(f)) yield f else: raise OSError(f"Unsure what to do with input of type: {type(fn)}")