def test_kill_descendents(): with ProcessGroup() as pg: pin = Pipe() pout = Pipe() pg.add( Process( runpy("""import subprocess import sys child = subprocess.Popen([sys.executable, '-c', ''' print("spam") input() print("eggs") ''']) child.wait() """), stdin=pin.side_out, stdout=pout.side_in, )) pg.start() pin.side_out.close() pout.side_in.close() assert pout.side_out.read(4) == b'spam' pg.terminate() pg.join() try: pin.side_in.write(b'\n') pin.side_in.close() except (BrokenPipeError, OSError): # BrokenPipeError on Linux, nothing on Mac, OSError on Windows pass assert pout.side_out.read() in b'\r\n'
def test_tee_basics(): pin = Pipe() pout = Pipe() buf = io.BytesIO() closed = False def _closeit(): nonlocal closed closed = True t = Tee( # noqa side_in=pin.side_out, side_out=pout.side_in, callback=buf.write, eof=_closeit, ) pin.side_in.write(b'foobar') pin.side_in.close() roundtrip = pout.side_out.read() assert roundtrip == b'foobar' # This is only guarenteed _after_ it appears on the pipe assert buf.getvalue() == b'foobar' assert closed
def test_valve_stop(): pin = Pipe() pout = Pipe() v = Valve( side_in=pin.side_out, side_out=pout.side_in, ) pin.side_in.write(b'spameggs') pin.side_in.close() buf = None timediff = None def clockread(): nonlocal buf, timediff s = time.perf_counter() buf = pout.side_out.read() e = time.perf_counter() timediff = e - s v.turn_off() t = threading.Thread(target=clockread, daemon=True) t.start() time.sleep(1.0) v.turn_on() t.join() assert buf == b'spameggs' assert timediff > 0.9
def test_through(): pin = Pipe() pout = Pipe() qc = QuickConnect( # noqa side_in=pin.side_out, side_out=pout.side_in, keepopen=False) pin.side_in.write(b'spameggs') pin.side_in.close() roundtrip = pout.side_out.read() assert roundtrip == b'spameggs'
def test_valve_through(): pin = Pipe() pout = Pipe() v = Valve( side_in=pin.side_out, side_out=pout.side_in, ) v.turn_on() pin.side_in.write(b'spameggs') pin.side_in.close() roundtrip = pout.side_out.read() assert roundtrip == b'spameggs'
def test_goesthrough(): p = Pipe() p.side_in.write(b"Hello") p.side_in.close() data = p.side_out.read() assert data == b'Hello' data = p.side_out.read() assert data == b''
def test_large_chunk_interactivish(): p = Pipe() p.side_in.write(b"Hello") data = p.side_out.read(4000) assert data == b'Hello' p.side_in.close() data = p.side_out.read(4000) assert data == b''
def test_group(): with ProcessGroup() as pg: p1 = Pipe() pg.add(Process(runpy("print('spam')"), stdout=p1.side_in)) p2 = Pipe() pg.add(Process(runpy("print('eggs')"), stdout=p2.side_in)) p3 = Pipe() pg.add(Process(runpy("print('vikings')"), stdout=p3.side_in)) pg.start() p1.side_in.close() p2.side_in.close() p3.side_in.close() pg.join() assert p1.side_out.read().rstrip() == b'spam' assert p2.side_out.read().rstrip() == b'eggs' assert p3.side_out.read().rstrip() == b'vikings'
def test_valve_stop_midway(): pin = Pipe() pout = Pipe() v = Valve( side_in=pin.side_out, side_out=pout.side_in, ) v.turn_on() pin.side_in.write(b'spam') time.sleep(1.0) v.turn_off() pin.side_in.write(b'eggs') time.sleep(1.0) buf = pout.side_out.read(4000) assert buf == b'spam'
def test_pipe_output(): pi = Pipe() proc = Process(runpy(r'print("hello")'), stdout=pi.side_in) proc.start() data = pi.side_out.readline() # Pipe is closed but process might still be live proc.join() # Commenting this out causes data to be None? assert proc.return_code == 0 assert data in (b'hello\n', b'hello\r\n')
def test_eof(): p = Pipe() p.side_in.write(b"spam") data = p.side_out.read(4) # read() or read(-1) read until EOF assert data == b'spam' p.side_in.close() data = p.side_out.read() assert data == b''
def test_pipe_input(): pi = Pipe() proc = Process(runpy(r'import sys; sys.exit(input() == "spam")'), stdin=pi.side_out) proc.start() pi.side_in.write(b"spam\n") pi.side_in.close() # Pipe is closed but process might still be live proc.join() assert proc.return_code == 1
def test_change_output(): pin = Pipe() p1 = Pipe() p2 = Pipe() qc = QuickConnect( side_in=pin.side_out, side_out=p1.side_in, ) pin.side_in.write(b'spam') time.sleep(1.0) qc.side_out = p2.side_in pin.side_in.write(b'eggs') out1 = p1.side_out.read(4000) out2 = p2.side_out.read(4000) assert out1 == b'spam' assert out2 == b'eggs'
def test_chunk_eof(): p = Pipe() p.side_in.write(b"Hello") data = p.side_out.read(3) assert data == b'Hel' data = p.side_out.read(2) assert data == b'lo' p.side_in.close() data = p.side_out.read(5) assert data == b''
def test_partial_output(): pi = Pipe() proc = Process( runpy(r'print("foo", flush=True); input(); print("bar", flush=True)'), stdout=pi.side_in, ) proc.start() data = pi.side_out.readline() proc.terminate() proc.join() assert data in (b'foo\n', b'foo\r\n')
def test_inner_pipe_reversed_order(): pi = Pipe() prod = Process(runpy(r'print("eggs")'), stdout=pi.side_in) cons = Process(runpy(r'import sys; sys.exit(input() == "eggs")'), stdin=pi.side_out) cons.start() prod.start() cons.join() prod.join() assert prod.return_code == 0 assert cons.return_code == 1
def test_iter(): p = Pipe() p.side_in.write(b"Hello") p.side_in.close() riter = iter(p.side_out) data = next(riter) assert data == b'Hello' with pytest.raises(StopIteration): next(riter)
def test_argv_roundtrip(st): # This is for the benefit of Windows and other platforms that don't actually # pass processes a paramaterized argv pi = Pipe() proc = Process(runpy(r'import sys; print(sys.argv[1])') + [st], stdout=pi.side_in) proc.start() data = pi.side_out.readline() # Pipe is closed but process might still be live proc.join() # Commenting this out causes data to be None? assert proc.return_code == 0 assert data.rstrip(b'\r\n') == st.encode('ascii')
def test_change_input(): p1 = Pipe() p2 = Pipe() pout = Pipe() qc = QuickConnect(side_in=p1.side_out, side_out=pout.side_in, keepopen=False) p1.side_in.write(b'spam') p2.side_in.write(b'foo') time.sleep(1.0) qc.side_in = p2.side_out p1.side_in.write(b'eggs') p2.side_in.write(b'bar') p1.side_in.close() p2.side_in.close() roundtrip = pout.side_out.read() assert roundtrip == b'spamfoobar'
def test_error_output(): # NOTE: POSIX clears the pipe when a process has non-zero return pi = Pipe() proc = Process( runpy(r'import sys; print("bar", flush=True); sys.exit(42)'), stdout=pi.side_in, ) proc.start() pi.side_in.close( ) # Remove our reference on this end of the pipe, now that the child has one proc.join() data = pi.side_out.read() assert data in (b'bar\n', b'bar\r\n')
def test_terminated_output(): # NOTE: POSIX clears the pipe when a process has non-zero return pi = Pipe() proc = Process( runpy(r'print("foo", flush=True); input(); print("bar", flush=True)'), stdout=pi.side_in, ) proc.start() pi.side_in.close( ) # Remove our reference on this end of the pipe, now that the child has one proc.terminate() proc.join() data = pi.side_out.read() assert data == b''
def test_lswc_gh10(): """ Tests ls|wc """ # From https://github.com/xonsh/slug/issues/10 with ProcessGroup() as pg: pipe = Pipe() pg.add(Process(['ls'], stdout=pipe.side_in)) pg.add(Process(['wc'], stdin=pipe.side_out)) pg.start() pipe.side_in.close() pipe.side_out.close() pg.join()
def test_pause_unpause(): import time pi = Pipe() proc = Process( runpy('import time\nwhile True: print(time.time(), flush=True)'), stdout=pi.side_in, ) proc.start() pi.side_in.close( ) # Remove our reference on this end of the pipe, now that the child has one below = [ float(ts.decode('utf-8').rstrip('\r\n')) for ts in iter_for_time(1, pi.side_out) if ts ] proc.pause() time.sleep(1) proc.unpause() above = [ float(ts.decode('utf-8').rstrip('\r\n')) for ts in iter_for_time(1, pi.side_out) if ts ] proc.terminate() # We don't have a non-blocking way to flush, so we're just hoping that 1s is # enough time to get what was stashed. timestamps = below + above # Calculate the mean, assume its in the paused area, and calculate the gap # in the timestamps from that pause. mean = sum(timestamps) / len(timestamps) below = [ts for ts in timestamps if ts < mean] above = [ts for ts in timestamps if ts > mean] pause_begin = max(below) pause_end = min(above) gap = pause_end - pause_begin assert gap > 0.9 # 0.9 for leeway