2023-01-11 17:09:59 -07:00
|
|
|
from contextlib import contextmanager
|
|
|
|
from pathlib import Path
|
|
|
|
from sys import stdin, stdout, stderr
|
|
|
|
|
2024-02-06 14:06:44 -07:00
|
|
|
from scwrypts.env import getenv
|
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def get_combined_stream(input_file=None, output_file=None):
|
|
|
|
'''
|
|
|
|
context manager to open an "input_file" and "output_file"
|
|
|
|
|
|
|
|
But the "files" can be pipe-streams, stdin/stdout, or even
|
|
|
|
actual files! Helpful when trying to write CLI scwrypts
|
|
|
|
which would like to accept all kinds of input and output
|
|
|
|
configurations.
|
|
|
|
'''
|
|
|
|
with get_stream(input_file, 'r') as input_stream, get_stream(output_file, 'w+') as output_stream:
|
|
|
|
yield CombinedStream(input_stream, output_stream)
|
|
|
|
|
|
|
|
def add_io_arguments(parser, allow_input=True, allow_output=True):
|
|
|
|
'''
|
|
|
|
slap these puppies onto your argparse.ArgumentParser to
|
|
|
|
allow easy use of the get_combined_stream at the command line
|
|
|
|
'''
|
|
|
|
if allow_input:
|
|
|
|
parser.add_argument(
|
|
|
|
'-i', '--input-file',
|
|
|
|
dest = 'input_file',
|
|
|
|
default = None,
|
|
|
|
help = 'path to input file; omit for stdin',
|
|
|
|
required = False,
|
|
|
|
)
|
|
|
|
|
|
|
|
if allow_output:
|
|
|
|
parser.add_argument(
|
|
|
|
'-o', '--output-file',
|
|
|
|
dest = 'output_file',
|
|
|
|
default = None,
|
|
|
|
help = 'path to output file; omit for stdout',
|
|
|
|
required = False,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
#####################################################################
|
2023-01-11 17:09:59 -07:00
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def get_stream(filename=None, mode='r', encoding='utf-8', verbose=False, **kwargs):
|
|
|
|
allowed_modes = {'r', 'w', 'w+'}
|
|
|
|
|
|
|
|
if mode not in allowed_modes:
|
|
|
|
raise ValueError(f'mode "{mode}" not supported modes (must be one of {allowed_modes})')
|
|
|
|
|
|
|
|
is_read = mode == 'r'
|
|
|
|
|
|
|
|
if filename is not None:
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
print(f'opening file {filename} for {"read" if is_read else "write"}', file=stderr)
|
|
|
|
|
|
|
|
if filename[0] not in {'/', '~'}:
|
|
|
|
filename = Path(f'{getenv("EXECUTION_DIR")}/{filename}').resolve()
|
|
|
|
with open(filename, mode=mode, encoding=encoding, **kwargs) as stream:
|
|
|
|
yield stream
|
|
|
|
|
|
|
|
else:
|
|
|
|
if verbose:
|
|
|
|
print('using stdin for read' if is_read else 'using stdout for write', file=stderr)
|
|
|
|
|
|
|
|
yield stdin if is_read else stdout
|
|
|
|
|
2023-02-21 18:44:27 -07:00
|
|
|
if not is_read:
|
|
|
|
stdout.flush()
|
|
|
|
|
2023-01-11 17:09:59 -07:00
|
|
|
|
2023-02-21 18:44:27 -07:00
|
|
|
class CombinedStream:
|
|
|
|
def __init__(self, input_stream, output_stream):
|
|
|
|
self.input = input_stream
|
|
|
|
self.output = output_stream
|
|
|
|
|
|
|
|
def read(self, *args, **kwargs):
|
|
|
|
return self.input.read(*args, **kwargs)
|
|
|
|
|
|
|
|
def readline(self, *args, **kwargs):
|
|
|
|
return self.input.readline(*args, **kwargs)
|
|
|
|
|
|
|
|
def readlines(self, *args, **kwargs):
|
|
|
|
return self.input.readlines(*args, **kwargs)
|
|
|
|
|
|
|
|
def write(self, *args, **kwargs):
|
|
|
|
return self.output.write(*args, **kwargs)
|
|
|
|
|
|
|
|
def writeline(self, line):
|
|
|
|
x = self.output.write(f'{line}\n')
|
|
|
|
self.output.flush()
|
|
|
|
return x
|
|
|
|
|
|
|
|
def writelines(self, *args, **kwargs):
|
|
|
|
return self.output.writelines(*args, **kwargs)
|