Compare commits
No commits in common. "master" and "ci" have entirely different histories.
19
README.rst
19
README.rst
@ -7,7 +7,8 @@ Why?
|
|||||||
In Python we now have async subprocesses which allows to execute several
|
In Python we now have async subprocesses which allows to execute several
|
||||||
subprocesses at the same time. The purpose of this library is to:
|
subprocesses at the same time. The purpose of this library is to:
|
||||||
|
|
||||||
- stream stderr and stdout in real time while capturing it,
|
- provide an acceptable asyncio subprocess wrapper for my syntaxic taste,
|
||||||
|
- can stream stderr and stdout in real time while capturing it,
|
||||||
- real time output must be prefixed for when you execute several commands at
|
- real time output must be prefixed for when you execute several commands at
|
||||||
the time so that you know which line is for which process, like with
|
the time so that you know which line is for which process, like with
|
||||||
docker-compose logs,
|
docker-compose logs,
|
||||||
@ -15,13 +16,6 @@ subprocesses at the same time. The purpose of this library is to:
|
|||||||
|
|
||||||
This code was copy/pasted between projects and finally extracted on its own.
|
This code was copy/pasted between projects and finally extracted on its own.
|
||||||
|
|
||||||
Demo
|
|
||||||
====
|
|
||||||
|
|
||||||
.. image:: https://yourlabs.io/oss/shlax/-/raw/master/demo.png
|
|
||||||
|
|
||||||
You will find the demo script in demo.py in this repository.
|
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
@ -102,9 +96,8 @@ will be applied line by line:
|
|||||||
Where is the rest?
|
Where is the rest?
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Shlax used to be the name of a much more ambitious poc-project, that you can
|
Shlax used to be the name of a much more ambitious poc-project that has been
|
||||||
still find in the ``OLD`` branch of this repository. It has been extracted in
|
extracted in two projects with clear boundaries, namely `sysplan
|
||||||
two projects with clear boundaries, namely `sysplan
|
|
||||||
<https://yourlabs.io/oss/sysplan>`_ and `podplan
|
<https://yourlabs.io/oss/sysplan>`_ and `podplan
|
||||||
<https://yourlabs.io/oss/podplan>`_ which are still in alpha state, although
|
<https://yourlabs.io/oss/podplan>`_ which are still in alpha state, but Shlax
|
||||||
Shlax as it now, is feature complete and stable.
|
as it is feature complete and stable.
|
||||||
|
|||||||
24
demo.py
24
demo.py
@ -1,24 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from shlax import Subprocess
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
colors = {
|
|
||||||
'^(.*).txt$': '{green}\\1.txt',
|
|
||||||
'^(.*).py$': '{bred}\\1.py',
|
|
||||||
}
|
|
||||||
await asyncio.gather(
|
|
||||||
Subprocess(
|
|
||||||
'for i in $(find .. | head); do echo $i; sleep .2; done',
|
|
||||||
regexps=colors,
|
|
||||||
prefix='parent',
|
|
||||||
).wait(),
|
|
||||||
Subprocess(
|
|
||||||
'for i in $(find . | head); do echo $i; sleep .3; done',
|
|
||||||
regexps=colors,
|
|
||||||
prefix='cwd',
|
|
||||||
).wait(),
|
|
||||||
)
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import functools
|
import functools
|
||||||
import re
|
import re
|
||||||
import shlex
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .colors import colors
|
from .colors import colors
|
||||||
@ -55,6 +54,7 @@ class Subprocess:
|
|||||||
if len(args) == 1 and ' ' in args[0]:
|
if len(args) == 1 and ' ' in args[0]:
|
||||||
args = ['sh', '-euc', args[0]]
|
args = ['sh', '-euc', args[0]]
|
||||||
|
|
||||||
|
self.cmd = ' '.join(args)
|
||||||
self.args = args
|
self.args = args
|
||||||
self.quiet = quiet if quiet is not None else False
|
self.quiet = quiet if quiet is not None else False
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
@ -75,18 +75,6 @@ class Subprocess:
|
|||||||
self.regexps[search] = replace
|
self.regexps[search] = replace
|
||||||
|
|
||||||
async def start(self, wait=True):
|
async def start(self, wait=True):
|
||||||
if not self.quiet:
|
|
||||||
self.output(
|
|
||||||
self.colors.bgray.encode()
|
|
||||||
+ b'+ '
|
|
||||||
+ shlex.join([
|
|
||||||
arg.replace('\n', '\\n')
|
|
||||||
for arg in self.args
|
|
||||||
]).encode()
|
|
||||||
+ self.colors.reset.encode(),
|
|
||||||
highlight=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get a reference to the event loop as we plan to use
|
# Get a reference to the event loop as we plan to use
|
||||||
# low-level APIs.
|
# low-level APIs.
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
|||||||
@ -37,10 +37,12 @@ async def test_wait_unbound():
|
|||||||
async def test_rc_1():
|
async def test_rc_1():
|
||||||
proc = await Proc(
|
proc = await Proc(
|
||||||
'NON EXISTING COMMAND',
|
'NON EXISTING COMMAND',
|
||||||
quiet=True,
|
write=Mock(),
|
||||||
).wait()
|
).wait()
|
||||||
assert proc.rc != 0
|
assert proc.rc != 0
|
||||||
assert proc.err == 'sh: line 1: NON: command not found'
|
proc.write.assert_called_once_with(
|
||||||
|
b'sh: line 1: NON: command not found\x1b[0m\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -68,16 +70,6 @@ async def test_prefix():
|
|||||||
).wait()
|
).wait()
|
||||||
|
|
||||||
assert write.mock_calls == [
|
assert write.mock_calls == [
|
||||||
call(
|
|
||||||
Proc.prefix_colors[0].encode()
|
|
||||||
+ b'test_prefix '
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'| '
|
|
||||||
+ Proc.colors.bgray.encode()
|
|
||||||
+ b'+ sh -euc \'echo hi\''
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'\n'
|
|
||||||
),
|
|
||||||
call(
|
call(
|
||||||
Proc.prefix_colors[0].encode()
|
Proc.prefix_colors[0].encode()
|
||||||
+ b'test_prefix '
|
+ b'test_prefix '
|
||||||
@ -86,16 +78,6 @@ async def test_prefix():
|
|||||||
+ Proc.colors.reset.encode()
|
+ Proc.colors.reset.encode()
|
||||||
+ b'\n'
|
+ b'\n'
|
||||||
),
|
),
|
||||||
call(
|
|
||||||
Proc.prefix_colors[1].encode()
|
|
||||||
+ b'test_prefix_1 '
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'| '
|
|
||||||
+ Proc.colors.bgray.encode()
|
|
||||||
+ b'+ sh -euc \'echo hi\''
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'\n'
|
|
||||||
),
|
|
||||||
call(
|
call(
|
||||||
Proc.prefix_colors[1].encode()
|
Proc.prefix_colors[1].encode()
|
||||||
# padding has been added because of output1
|
# padding has been added because of output1
|
||||||
@ -105,17 +87,6 @@ async def test_prefix():
|
|||||||
+ Proc.colors.reset.encode()
|
+ Proc.colors.reset.encode()
|
||||||
+ b'\n'
|
+ b'\n'
|
||||||
),
|
),
|
||||||
call(
|
|
||||||
Proc.prefix_colors[0].encode()
|
|
||||||
# padding has been added because of output1
|
|
||||||
+ b' test_prefix '
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'| '
|
|
||||||
+ Proc.colors.bgray.encode()
|
|
||||||
+ b'+ sh -euc \'echo hi\''
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'\n'
|
|
||||||
),
|
|
||||||
call(
|
call(
|
||||||
Proc.prefix_colors[0].encode()
|
Proc.prefix_colors[0].encode()
|
||||||
# padding has been added because of output1
|
# padding has been added because of output1
|
||||||
@ -137,16 +108,6 @@ async def test_prefix_multiline():
|
|||||||
prefix='test_prefix',
|
prefix='test_prefix',
|
||||||
).wait()
|
).wait()
|
||||||
assert proc.write.mock_calls == [
|
assert proc.write.mock_calls == [
|
||||||
call(
|
|
||||||
Proc.prefix_colors[0].encode()
|
|
||||||
+ b'test_prefix '
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'| '
|
|
||||||
+ Proc.colors.bgray.encode()
|
|
||||||
+ b'+ sh -euc \'echo -e "a\\nb"\''
|
|
||||||
+ Proc.colors.reset.encode()
|
|
||||||
+ b'\n'
|
|
||||||
),
|
|
||||||
call(
|
call(
|
||||||
Proc.prefix_colors[0].encode()
|
Proc.prefix_colors[0].encode()
|
||||||
+ b'test_prefix '
|
+ b'test_prefix '
|
||||||
@ -179,7 +140,7 @@ async def test_highlight():
|
|||||||
r'h([\w\d-]+)': 'h{cyan}\\1',
|
r'h([\w\d-]+)': 'h{cyan}\\1',
|
||||||
}
|
}
|
||||||
).wait()
|
).wait()
|
||||||
proc.write.assert_called_with(b'h\x1b[38;5;51mi\x1b[0m\n')
|
proc.write.assert_called_once_with(b'h\x1b[38;5;51mi\x1b[0m\n')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -194,4 +155,4 @@ async def test_highlight_if_not_colored():
|
|||||||
r'h([\w\d-]+)': 'h{cyan}\\1',
|
r'h([\w\d-]+)': 'h{cyan}\\1',
|
||||||
}
|
}
|
||||||
).wait()
|
).wait()
|
||||||
proc.write.assert_called_with(b'h\x1b[31mi\n')
|
proc.write.assert_called_once_with(b'h\x1b[31mi\n')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user