def test_process_with_watch_immediately(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) with p.make_process( "write3", sys.executable, args="executables/write_three_lines.py", working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, watch_immediately=True, ) as w: out, err, close = self.gather_output(w) exit_code = w.exit_code["write3"] self.assertEqual(3, len(out)) self.assertEqual(1, len(err)) self.assertEqual(1, len(close)) self.assertEqual("write3", out[0].name) self.assertEqual("write3", out[1].name) self.assertEqual("write3", out[2].name) self.assertEqual("write3", err[0].name) self.assertEqual("write3", close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, err[0].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertLessEqual(err[0].timestamp, close[0].timestamp) self.assertEqual(b"Version: " + cast_bytes(sys.version.partition(" ")[0]) + b"\n", out[0].data) self.assertEqual(b"Executable: " + cast_bytes(sys.executable) + b"\n", out[1].data) self.assertEqual(b"Args: \n", out[2].data) self.assertEqual(b"done", err[0].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def chat_with_a_client(sock, addr, instance_globals, container): connection = ServerCommandConnection(sock) instance = {'globals': instance_globals, 'connection': connection} try: sock.send(b'Papa is home. Type "help" for commands.\n> ') while True: one_line = connection.readline() args = [] acc = '' if one_line: for arg in one_line.split(' '): if arg: if arg[-1] == '\\': acc += arg[:-1] + ' ' else: acc += arg args.append(acc.strip()) acc = '' if acc: args.append(acc) try: command = lookup_command(args) except Error as e: reply = 'Error: {0}\n'.format(e) else: try: reply = command(sock, args, instance) or '\n' except CloseSocket as e: if e.final_message: send_with_retry(sock, cast_bytes(e.final_message)) break except papa.utils.Error as e: reply = 'Error: {0}\n'.format(e) except Exception as e: reply = 'Error: {0}\n'.format(e) if reply[-1] != '\n': reply += '\n> ' else: reply += '> ' reply = cast_bytes(reply) else: reply = b'> ' send_with_retry(sock, reply) except socket.error: pass try: sock.close() except socket.error: pass if container: thread_object = container[0] instance_globals['active_threads'].remove(thread_object) instance_globals['inactive_threads'].append((addr, thread_object))
def test_process_with_none_executable(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process( 'write3', None, args=[sys.executable, 'executables/write_three_lines.py'], working_dir=here, uid=os.environ['LOGNAME'], env=os.environ) self.assertIn('pid', reply1) self.assertIsInstance(reply1['pid'], int) reply = p.list_processes() self.assertEqual(1, len(list(reply.keys()))) self.assertEqual('write3', list(reply.keys())[0]) self.assertIn('pid', list(reply.values())[0]) reply2 = p.make_process('write3', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ) self.assertEqual(reply1['pid'], reply2['pid']) self.assertRaises(papa.Error, p.watch_processes, 'not_there') with p.watch_processes('write*') as w: select.select([w], [], []) self.assertTrue(w.ready) out, err, close = self.gather_output(w) exit_code = w.exit_code['write3'] self.assertEqual(3, len(out)) self.assertEqual(1, len(err)) self.assertEqual(1, len(close)) self.assertEqual('write3', out[0].name) self.assertEqual('write3', out[1].name) self.assertEqual('write3', out[2].name) self.assertEqual('write3', err[0].name) self.assertEqual('write3', close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, err[0].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertLessEqual(err[0].timestamp, close[0].timestamp) self.assertEqual( b'Version: ' + cast_bytes(sys.version.partition(' ')[0]) + b'\n', out[0].data) self.assertEqual( b'Executable: ' + cast_bytes(sys.executable) + b'\n', out[1].data) self.assertEqual(b'Args: \n', out[2].data) self.assertEqual(b'done', err[0].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def test_process_with_none_executable(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process( "write3", None, args=[sys.executable, "executables/write_three_lines.py"], working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, ) self.assertIn("pid", reply1) self.assertIsInstance(reply1["pid"], int) reply = p.list_processes() self.assertEqual(1, len(list(reply.keys()))) self.assertEqual("write3", list(reply.keys())[0]) self.assertIn("pid", list(reply.values())[0]) reply2 = p.make_process( "write3", sys.executable, args="executables/write_three_lines.py", working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, ) self.assertEqual(reply1["pid"], reply2["pid"]) self.assertRaises(papa.Error, p.watch_processes, "not_there") with p.watch_processes("write*") as w: select.select([w], [], []) self.assertTrue(w.ready) out, err, close = self.gather_output(w) exit_code = w.exit_code["write3"] self.assertEqual(3, len(out)) self.assertEqual(1, len(err)) self.assertEqual(1, len(close)) self.assertEqual("write3", out[0].name) self.assertEqual("write3", out[1].name) self.assertEqual("write3", out[2].name) self.assertEqual("write3", err[0].name) self.assertEqual("write3", close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, err[0].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertLessEqual(err[0].timestamp, close[0].timestamp) self.assertEqual(b"Version: " + cast_bytes(sys.version.partition(" ")[0]) + b"\n", out[0].data) self.assertEqual(b"Executable: " + cast_bytes(sys.executable) + b"\n", out[1].data) self.assertEqual(b"Args: \n", out[2].data) self.assertEqual(b"done", err[0].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def _do_watch(sock, procs, instance): instance_globals = instance['globals'] all_processes = instance_globals['processes'] connection = instance['connection'] poller = Poller(sock) delay = .1 while True: data = [] for name, proc in procs.items(): t, l = proc['p'].watch() if l: for item in l: if item.type == OutputQueue.CLOSED: data.append( cast_bytes('closed:{0}:{1}:{2}'.format( name, item.timestamp, item.data))) proc['closed'] = True else: data.append( cast_bytes('{0}:{1}:{2}:{3}'.format( 'out' if item.type == OutputQueue.STDOUT else 'err', name, item.timestamp, len(item.data)))) data.append(item.data) proc['t'] = t if data: delay = .05 data.append(b'] ') out = b'\n'.join(data) send_with_retry(sock, out) one_line = connection.readline().lower() closed = [] for name, proc in procs.items(): t = proc['t'] if t: proc['p'].remove_output(t) if proc['closed']: closed.append(name) if closed: with instance_globals['lock']: for name in closed: closed_proc = procs.pop(name, None) if closed_proc and 'p' in closed_proc: log.info('Removed process %s', closed_proc['p']) all_processes.pop(name, None) if not procs: return 'Nothing left to watch' if one_line == 'q': return 'Stopped watching' else: if poller.poll(delay): return 'Client closed connection' if delay < 1.0: delay += .05
def _do_watch(sock, procs, instance): instance_globals = instance['globals'] all_processes = instance_globals['processes'] connection = instance['connection'] poller = Poller(sock) delay = .1 while True: data = [] for name, proc in procs.items(): t, l = proc['p'].watch() if l: for item in l: if item.type == OutputQueue.CLOSED: data.append(cast_bytes('closed:{0}:{1}:{2}'.format(name, item.timestamp, item.data))) proc['closed'] = True else: data.append(cast_bytes('{0}:{1}:{2}:{3}'.format('out' if item.type == OutputQueue.STDOUT else 'err', name, item.timestamp, len(item.data)))) data.append(item.data) proc['t'] = t if data: delay = .05 data.append(b'] ') out = b'\n'.join(data) send_with_retry(sock, out) one_line = connection.readline().lower() closed = [] for name, proc in procs.items(): t = proc['t'] if t: proc['p'].remove_output(t) if proc['closed']: closed.append(name) if closed: with instance_globals['lock']: for name in closed: closed_proc = procs.pop(name, None) if closed_proc and 'p' in closed_proc: log.info('Removed process %s', closed_proc['p']) all_processes.pop(name, None) if not procs: return 'Nothing left to watch' if one_line == 'q': return 'Stopped watching' else: if poller.poll(delay): return 'Client closed connection' if delay < 1.0: delay += .05
def watch_command(sock, args, instance): """Watch a process""" instance_globals = instance['globals'] all_processes = instance_globals['processes'] with instance_globals['lock']: procs = dict((name, {'p': proc, 't': 0, 'closed': False}) for name, proc in wildcard_iter(all_processes, args, True)) if not procs: raise utils.Error('Nothing to watch') send_with_retry(sock, cast_bytes('Watching {0}\n'.format(len(procs)))) return _do_watch(sock, procs, instance)
def process_command(sock, args, instance): """Create a process. You need to specify a name, followed by name=value pairs for the process options, followed by the command and args to execute. The name must not contain spaces. Process options are: uid - the username or user ID to use when starting the process gid - the group name or group ID to use when starting the process working_dir - must be an absolute path if specified output - size of each output buffer (default is 1m) You can also specify environment variables by prefixing the name with 'env.' and rlimits by prefixing the name with 'rlimit.' Examples: make process sf uid=1001 gid=2000 working_dir=/sf/bin/ output=1m /sf/bin/uwsgi --ini uwsgi-live.ini --socket fd://27 --stats 127.0.0.1:8090 make process nginx /usr/local/nginx/sbin/nginx """ if not args: raise Error('Process requires a name') name = args.pop(0) env = {} rlimits = {} kwargs = {} for key, value in extract_name_value_pairs(args).items(): if key.startswith('env.'): env[key[4:]] = value elif key.startswith('rlimit.'): key = key[7:] try: rlimits[getattr(resource, 'RLIMIT_%s' % key.upper())] = int(value) except AttributeError: raise utils.Error('Unknown rlimit "%s"' % key) except ValueError: raise utils.Error( 'The rlimit value for "%s" must be an integer, not "%s"' % (key, value)) else: kwargs[key] = value watch = int(kwargs.pop('watch', 0)) p = Process(name, args, env, rlimits, instance, **kwargs) with instance['globals']['lock']: result = p.spawn() if watch: send_with_retry(sock, cast_bytes('{0}\n'.format(result))) return _do_watch(sock, {name: { 'p': result, 't': 0, 'closed': False }}, instance) return str(result)
def test_process_with_err_redirected_to_out(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process('write3', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ, stderr=papa.STDOUT) self.assertIn('pid', reply1) self.assertIsInstance(reply1['pid'], int) reply = p.list_processes() self.assertEqual(1, len(list(reply.keys()))) self.assertEqual('write3', list(reply.keys())[0]) self.assertIn('pid', list(reply.values())[0]) with p.watch_processes('write*') as w: out, err, close = self.gather_output(w) exit_code = w.exit_code['write3'] self.assertEqual(3, len(out)) self.assertEqual(0, len(err)) self.assertEqual(1, len(close)) self.assertEqual('write3', out[0].name) self.assertEqual('write3', out[1].name) self.assertEqual('write3', out[2].name) self.assertEqual('write3', close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertEqual( b'Version: ' + cast_bytes(sys.version.partition(' ')[0]) + b'\n', out[0].data) self.assertEqual( b'Executable: ' + cast_bytes(sys.executable) + b'\n', out[1].data) self.assertEqual(b'Args: \ndone', out[2].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def test_process_with_err_redirected_to_out(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process( "write3", sys.executable, args="executables/write_three_lines.py", working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, stderr=papa.STDOUT, ) self.assertIn("pid", reply1) self.assertIsInstance(reply1["pid"], int) reply = p.list_processes() self.assertEqual(1, len(list(reply.keys()))) self.assertEqual("write3", list(reply.keys())[0]) self.assertIn("pid", list(reply.values())[0]) with p.watch_processes("write*") as w: out, err, close = self.gather_output(w) exit_code = w.exit_code["write3"] self.assertEqual(3, len(out)) self.assertEqual(0, len(err)) self.assertEqual(1, len(close)) self.assertEqual("write3", out[0].name) self.assertEqual("write3", out[1].name) self.assertEqual("write3", out[2].name) self.assertEqual("write3", close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertEqual(b"Version: " + cast_bytes(sys.version.partition(" ")[0]) + b"\n", out[0].data) self.assertEqual(b"Executable: " + cast_bytes(sys.executable) + b"\n", out[1].data) self.assertEqual(b"Args: \ndone", out[2].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def watch_command(sock, args, instance): """Watch a process""" instance_globals = instance['globals'] all_processes = instance_globals['processes'] with instance_globals['lock']: procs = dict((name, { 'p': proc, 't': 0, 'closed': False }) for name, proc in wildcard_iter(all_processes, args, True)) if not procs: raise utils.Error('Nothing to watch') send_with_retry(sock, cast_bytes('Watching {0}\n'.format(len(procs)))) return _do_watch(sock, procs, instance)
def test_process_with_watch_immediately(self): with papa.Papa() as p: self.assertDictEqual({}, p.list_processes()) with p.make_process('write3', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ, watch_immediately=True) as w: out, err, close = self.gather_output(w) exit_code = w.exit_code['write3'] self.assertEqual(3, len(out)) self.assertEqual(1, len(err)) self.assertEqual(1, len(close)) self.assertEqual('write3', out[0].name) self.assertEqual('write3', out[1].name) self.assertEqual('write3', out[2].name) self.assertEqual('write3', err[0].name) self.assertEqual('write3', close[0].name) self.assertLess(out[0].timestamp, out[1].timestamp) self.assertLess(out[1].timestamp, out[2].timestamp) self.assertLessEqual(out[2].timestamp, err[0].timestamp) self.assertLessEqual(out[2].timestamp, close[0].timestamp) self.assertLessEqual(err[0].timestamp, close[0].timestamp) self.assertEqual( b'Version: ' + cast_bytes(sys.version.partition(' ')[0]) + b'\n', out[0].data) self.assertEqual( b'Executable: ' + cast_bytes(sys.executable) + b'\n', out[1].data) self.assertEqual(b'Args: \n', out[2].data) self.assertEqual(b'done', err[0].data) self.assertEqual(0, close[0].data) self.assertEqual(0, exit_code) self.assertDictEqual({}, p.list_processes())
def process_command(sock, args, instance): """Create a process. You need to specify a name, followed by name=value pairs for the process options, followed by the command and args to execute. The name must not contain spaces. Process options are: uid - the username or user ID to use when starting the process gid - the group name or group ID to use when starting the process working_dir - must be an absolute path if specified output - size of each output buffer (default is 1m) You can also specify environment variables by prefixing the name with 'env.' and rlimits by prefixing the name with 'rlimit.' Examples: make process sf uid=1001 gid=2000 working_dir=/sf/bin/ output=1m /sf/bin/uwsgi --ini uwsgi-live.ini --socket fd://27 --stats 127.0.0.1:8090 make process nginx /usr/local/nginx/sbin/nginx """ if not args: raise Error('Process requires a name') name = args.pop(0) env = {} rlimits = {} kwargs = {} for key, value in extract_name_value_pairs(args).items(): if key.startswith('env.'): env[key[4:]] = value elif key.startswith('rlimit.'): key = key[7:] try: rlimits[getattr(resource, 'RLIMIT_%s' % key.upper())] = int(value) except AttributeError: raise utils.Error('Unknown rlimit "%s"' % key) except ValueError: raise utils.Error('The rlimit value for "%s" must be an integer, not "%s"' % (key, value)) else: kwargs[key] = value watch = int(kwargs.pop('watch', 0)) p = Process(name, args, env, rlimits, instance, **kwargs) with instance['globals']['lock']: result = p.spawn() if watch: send_with_retry(sock, cast_bytes('{0}\n'.format(result))) return _do_watch(sock, {name: {'p': result, 't': 0, 'closed': False}}, instance) return str(result)
def test_multiple_watchers(self): with papa.Papa() as p: f = p.fileno() self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process( "write3.0", sys.executable, args="executables/write_three_lines.py", working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, ) self.assertIn("pid", reply1) self.assertIsInstance(reply1["pid"], int) reply2 = p.make_process( "write3.1", sys.executable, args="executables/write_three_lines.py", working_dir=here, uid=os.environ["LOGNAME"], env=os.environ, ) self.assertIn("pid", reply2) self.assertIsInstance(reply2["pid"], int) reply = p.list_processes() self.assertEqual(2, len(list(reply.keys()))) self.assertEqual(["write3.0", "write3.1"], sorted(reply.keys())) self.assertIn("pid", list(reply.values())[0]) self.assertIn("pid", list(reply.values())[1]) self.assertNotEqual(list(reply.values())[0]["pid"], list(reply.values())[1]["pid"]) w1 = p.watch_processes("write3.0") self.assertEqual(f, w1.fileno()) w2 = p.watch_processes("write3.1") self.assertNotEqual(f, w2.fileno()) p.set("p1", "t1") self.assertNotEqual(f, p.fileno()) self.assertNotEqual(p.fileno(), w1.fileno()) self.assertNotEqual(p.fileno(), w2.fileno()) out1, err1, close1 = self.gather_output(w1) out2, err2, close2 = self.gather_output(w2) w1.close() w2.close() self.assertEqual(3, len(out1)) self.assertEqual(1, len(err1)) self.assertEqual(1, len(close1)) self.assertEqual("write3.0", out1[0].name) self.assertEqual("write3.0", out1[1].name) self.assertEqual("write3.0", out1[2].name) self.assertEqual("write3.0", err1[0].name) self.assertEqual("write3.0", close1[0].name) self.assertLess(out1[0].timestamp, out1[1].timestamp) self.assertLess(out1[1].timestamp, out1[2].timestamp) self.assertLessEqual(out1[2].timestamp, err1[0].timestamp) self.assertLessEqual(out1[2].timestamp, close1[0].timestamp) self.assertLessEqual(err1[0].timestamp, close1[0].timestamp) self.assertEqual(b"Version: " + cast_bytes(sys.version.partition(" ")[0]) + b"\n", out1[0].data) self.assertEqual(b"Executable: " + cast_bytes(sys.executable) + b"\n", out1[1].data) self.assertEqual(b"Args: \n", out1[2].data) self.assertEqual(b"done", err1[0].data) self.assertEqual(0, close1[0].data) self.assertEqual(3, len(out2)) self.assertEqual(1, len(err2)) self.assertEqual(1, len(close2)) self.assertEqual("write3.1", out2[0].name) self.assertEqual("write3.1", out2[1].name) self.assertEqual("write3.1", out2[2].name) self.assertEqual("write3.1", err2[0].name) self.assertEqual("write3.1", close2[0].name) self.assertLess(out2[0].timestamp, out2[1].timestamp) self.assertLess(out2[1].timestamp, out2[2].timestamp) self.assertLessEqual(out2[2].timestamp, err2[0].timestamp) self.assertLessEqual(out2[2].timestamp, close2[0].timestamp) self.assertLessEqual(err2[0].timestamp, close2[0].timestamp) self.assertEqual(b"Version: " + cast_bytes(sys.version.partition(" ")[0]) + b"\n", out2[0].data) self.assertEqual(b"Executable: " + cast_bytes(sys.executable) + b"\n", out2[1].data) self.assertEqual(b"Args: \n", out2[2].data) self.assertEqual(b"done", err2[0].data) self.assertEqual(0, close2[0].data) self.assertDictEqual({}, p.list_processes()) self.assertEqual("t1", p.get("p1"))
def test_multiple_watchers(self): with papa.Papa() as p: f = p.fileno() self.assertDictEqual({}, p.list_processes()) reply1 = p.make_process('write3.0', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ) self.assertIn('pid', reply1) self.assertIsInstance(reply1['pid'], int) reply2 = p.make_process('write3.1', sys.executable, args='executables/write_three_lines.py', working_dir=here, uid=os.environ['LOGNAME'], env=os.environ) self.assertIn('pid', reply2) self.assertIsInstance(reply2['pid'], int) reply = p.list_processes() self.assertEqual(2, len(list(reply.keys()))) self.assertEqual(['write3.0', 'write3.1'], sorted(reply.keys())) self.assertIn('pid', list(reply.values())[0]) self.assertIn('pid', list(reply.values())[1]) self.assertNotEqual( list(reply.values())[0]['pid'], list(reply.values())[1]['pid']) w1 = p.watch_processes('write3.0') self.assertEqual(f, w1.fileno()) w2 = p.watch_processes('write3.1') self.assertNotEqual(f, w2.fileno()) p.set('p1', 't1') self.assertNotEqual(f, p.fileno()) self.assertNotEqual(p.fileno(), w1.fileno()) self.assertNotEqual(p.fileno(), w2.fileno()) out1, err1, close1 = self.gather_output(w1) out2, err2, close2 = self.gather_output(w2) w1.close() w2.close() self.assertEqual(3, len(out1)) self.assertEqual(1, len(err1)) self.assertEqual(1, len(close1)) self.assertEqual('write3.0', out1[0].name) self.assertEqual('write3.0', out1[1].name) self.assertEqual('write3.0', out1[2].name) self.assertEqual('write3.0', err1[0].name) self.assertEqual('write3.0', close1[0].name) self.assertLess(out1[0].timestamp, out1[1].timestamp) self.assertLess(out1[1].timestamp, out1[2].timestamp) self.assertLessEqual(out1[2].timestamp, err1[0].timestamp) self.assertLessEqual(out1[2].timestamp, close1[0].timestamp) self.assertLessEqual(err1[0].timestamp, close1[0].timestamp) self.assertEqual( b'Version: ' + cast_bytes(sys.version.partition(' ')[0]) + b'\n', out1[0].data) self.assertEqual( b'Executable: ' + cast_bytes(sys.executable) + b'\n', out1[1].data) self.assertEqual(b'Args: \n', out1[2].data) self.assertEqual(b'done', err1[0].data) self.assertEqual(0, close1[0].data) self.assertEqual(3, len(out2)) self.assertEqual(1, len(err2)) self.assertEqual(1, len(close2)) self.assertEqual('write3.1', out2[0].name) self.assertEqual('write3.1', out2[1].name) self.assertEqual('write3.1', out2[2].name) self.assertEqual('write3.1', err2[0].name) self.assertEqual('write3.1', close2[0].name) self.assertLess(out2[0].timestamp, out2[1].timestamp) self.assertLess(out2[1].timestamp, out2[2].timestamp) self.assertLessEqual(out2[2].timestamp, err2[0].timestamp) self.assertLessEqual(out2[2].timestamp, close2[0].timestamp) self.assertLessEqual(err2[0].timestamp, close2[0].timestamp) self.assertEqual( b'Version: ' + cast_bytes(sys.version.partition(' ')[0]) + b'\n', out2[0].data) self.assertEqual( b'Executable: ' + cast_bytes(sys.executable) + b'\n', out2[1].data) self.assertEqual(b'Args: \n', out2[2].data) self.assertEqual(b'done', err2[0].data) self.assertEqual(0, close2[0].data) self.assertDictEqual({}, p.list_processes()) self.assertEqual('t1', p.get('p1'))