├── .gitignore ├── Makefile ├── README.rst ├── procs ├── __init__.py ├── chain.py └── process.py ├── runtests.py ├── tests ├── __init__.py ├── test_chain.py ├── test_data │ ├── file1 │ ├── file2 │ └── file3 ├── test_process.py └── test_run.py └── usage.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | python runtests.py -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Procs: Python, Processes, and... Pipes 2 | ====================================== 3 | 4 | Python's Subprocess module is well designed for lower functions. Pipes is designed 5 | to encourage higher functions. 6 | 7 | 8 | Ideas 9 | ----- 10 | 11 | - Simple shelling out, allow argument seperation. 12 | - command timeouts 13 | - Process monitoring 14 | - programatically compose a chain of streams. 15 | - process call timeouts 16 | 17 | Usage 18 | ----- 19 | 20 | Simple Usage:: 21 | 22 | >>> import procs 23 | 24 | >>> c = procs.run('uptime') 25 | >>> c.returncode 26 | 0 27 | >>> c.ok 28 | True 29 | >>> print c.stdout 30 | 16:08 up 1:16, 7 users, load averages: 1.02 1.90 1.75 31 | 32 | 33 | Advanced Usage:: 34 | 35 | >>> ls = procs.Process('ls /usr/bin') 36 | >>> grep = procs.Process('grep python') 37 | >>> wc = procs.Process('wc -l') 38 | >>> chain = ls | grep | wc 39 | >>> chain.run() 40 | >>> print(chain.stdout) 41 | 19 42 | 43 | >>> from procs import ProcessHandler 44 | 45 | class MyCommmand(ProcessHandler): 46 | 47 | def __init__(self): 48 | pass 49 | 50 | def on_start(self): 51 | pass 52 | 53 | def on_exit(self): 54 | pass 55 | 56 | def on_crash(self): 57 | pass 58 | -------------------------------------------------------------------------------- /procs/__init__.py: -------------------------------------------------------------------------------- 1 | from .process import Process 2 | 3 | def run(command): 4 | process = Process(command) 5 | process.run() 6 | return process 7 | -------------------------------------------------------------------------------- /procs/chain.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | 4 | class Chain(object): 5 | 6 | def __init__(self, processes): 7 | self.processes = processes 8 | 9 | def run(self): 10 | for i, proc in enumerate(self.processes, start=1): 11 | if i > 1: 12 | proc.set_stdin(self.processes[i-2].subprocess.stdout) 13 | proc.start() 14 | if i != len(self.processes): 15 | proc.wait(unread=True) 16 | else: 17 | proc.wait() 18 | 19 | @property 20 | def returncode(self): 21 | return self.processes[-1].returncode 22 | 23 | @property 24 | def stdout(self): 25 | return self.processes[-1].stdout 26 | 27 | def __or__(self, other): 28 | return Chain(self.processes + [other]) 29 | -------------------------------------------------------------------------------- /procs/process.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import subprocess 3 | from .chain import Chain 4 | 5 | 6 | class Process(object): 7 | 8 | def __init__(self, command): 9 | self.command = command 10 | self._stdin = None 11 | self._stdout = None 12 | self._stdout_text = None 13 | self._returncode = None 14 | 15 | def set_stdin(self, stdin): 16 | self._stdin = stdin 17 | 18 | def set_stdout(self, stdout): 19 | self._stdout = stdout 20 | 21 | @property 22 | def stdin(self): 23 | return 'stdin' 24 | 25 | @property 26 | def stdout(self): 27 | if self._stdout_text is not None: 28 | return self._stdout_text 29 | 30 | @property 31 | def returncode(self): 32 | if self._returncode is not None: 33 | return self._returncode 34 | 35 | @property 36 | def ok(self): 37 | if self._returncode is not None: 38 | return self.returncode is 0 39 | 40 | @property 41 | def subprocess(self): 42 | if self._subprocess is not None: 43 | return self._subprocess 44 | 45 | def start(self): 46 | self._subprocess = subprocess.Popen( 47 | args=self.command, 48 | shell=True, 49 | stdin=self._stdin if self._stdin else subprocess.PIPE, 50 | stdout=subprocess.PIPE, 51 | ) 52 | 53 | def wait(self, unread=False): 54 | self._returncode = self._subprocess.wait() 55 | if self._subprocess.stdout is not None and not unread: 56 | self._stdout_text = self._subprocess.stdout.read().decode() 57 | 58 | 59 | def run(self): 60 | self.start() 61 | self.wait() 62 | 63 | 64 | def __or__(self, other): 65 | return Chain([self, other]) 66 | 67 | 68 | def __repr__(self): 69 | return ''.format(command=self.command) 70 | 71 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethreitz-archive/procs/d8d73c06811696e0483eeff64a2ffb1a386a3b5e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_chain.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from procs import Process 4 | 5 | 6 | TEST_DIR = os.path.abspath(os.path.join( 7 | os.path.dirname(os.path.abspath(__file__)), 'test_data' 8 | )) 9 | 10 | 11 | def test_chained_procs(): 12 | ls = Process('ls {test_dir}'.format(test_dir=TEST_DIR)) 13 | grep = Process('grep 2') 14 | chain = ls | grep 15 | chain.run() 16 | assert chain.returncode == 0 17 | assert chain.stdout.strip() == 'file2' 18 | 19 | def test_multi_chained_procs(): 20 | ls = Process('ls {test_dir}'.format(test_dir=TEST_DIR)) 21 | grep = Process('grep 2') 22 | wc = Process('wc -c') 23 | chain = ls | grep | wc 24 | chain.run() 25 | assert chain.returncode == 0 26 | assert chain.stdout.strip() == '6' 27 | 28 | -------------------------------------------------------------------------------- /tests/test_data/file1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethreitz-archive/procs/d8d73c06811696e0483eeff64a2ffb1a386a3b5e/tests/test_data/file1 -------------------------------------------------------------------------------- /tests/test_data/file2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethreitz-archive/procs/d8d73c06811696e0483eeff64a2ffb1a386a3b5e/tests/test_data/file2 -------------------------------------------------------------------------------- /tests/test_data/file3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kennethreitz-archive/procs/d8d73c06811696e0483eeff64a2ffb1a386a3b5e/tests/test_data/file3 -------------------------------------------------------------------------------- /tests/test_process.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from procs import Process 4 | 5 | 6 | TEST_DIR = os.path.abspath(os.path.join( 7 | os.path.dirname(os.path.abspath(__file__)), 'test_data' 8 | )) 9 | 10 | def tests_repr(): 11 | p = Process('foo') 12 | assert repr(p) == '' 13 | 14 | 15 | def test_single_proc(): 16 | ls = Process('ls {test_dir}'.format(test_dir=TEST_DIR)) 17 | ls.run() 18 | assert ls.returncode == 0 19 | assert ls.stdout == u'file1\nfile2\nfile3\n' 20 | 21 | 22 | def test_empty_output(): 23 | cat = Process('cat /dev/null') 24 | cat.run() 25 | assert cat.stdout == u'' 26 | 27 | 28 | def test_returncode(): 29 | assert not os.path.exists('/bin/nosuchcommand') 30 | 31 | p = Process('/bin/nosuchcommand') 32 | p.run() 33 | assert p.returncode == 127 34 | 35 | 36 | def test_ok_if_returncode_0(): 37 | p = Process('ls') 38 | p.run() 39 | assert p.ok is True 40 | 41 | 42 | def test_not_ok_if_returncode_not_0(): 43 | assert not os.path.exists('/bin/nosuchcommand') 44 | p = Process('/bin/nosuchcommand') 45 | p.run() 46 | assert p.ok is False 47 | -------------------------------------------------------------------------------- /tests/test_run.py: -------------------------------------------------------------------------------- 1 | import procs 2 | 3 | 4 | def test_run_is_same_as_Process_run(): 5 | process = procs.Process('ls') 6 | process.run() 7 | p = procs.run('ls') 8 | assert p.returncode == process.returncode 9 | assert p.stdout == process.stdout 10 | 11 | -------------------------------------------------------------------------------- /usage.py: -------------------------------------------------------------------------------- 1 | import procs 2 | 3 | chain = procs.chain() 4 | 5 | uptime = chain.process('uptime') 6 | cowsay = chain.process('cowsay') 7 | 8 | chain.link(uptime.stdout, cowsay.stdin) 9 | chain.start(wait=True) 10 | 11 | print cowsay.stdout 12 | 13 | 14 | # ConnectedProcess 15 | # ActiveProcess 16 | # PassiveProcess 17 | --------------------------------------------------------------------------------