Compare commits

...

2 Commits
ci ... master

Author SHA1 Message Date
jpic
5a38535cdb Readme updates 2021-11-11 22:14:48 +01:00
jpic
51cd4ae712 Add demo and command dump 2021-11-11 21:54:32 +01:00
5 changed files with 95 additions and 13 deletions

View File

@ -7,8 +7,7 @@ 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:
- provide an acceptable asyncio subprocess wrapper for my syntaxic taste, - stream stderr and stdout in real time while capturing it,
- 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,
@ -16,6 +15,13 @@ 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
===== =====
@ -96,8 +102,9 @@ 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 has been Shlax used to be the name of a much more ambitious poc-project, that you can
extracted in two projects with clear boundaries, namely `sysplan still find in the ``OLD`` branch of this repository. It has been extracted in
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, but Shlax <https://yourlabs.io/oss/podplan>`_ which are still in alpha state, although
as it is feature complete and stable. Shlax as it now, is feature complete and stable.

BIN
demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

24
demo.py Normal file
View File

@ -0,0 +1,24 @@
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())

View File

@ -1,6 +1,7 @@
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
@ -54,7 +55,6 @@ 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,6 +75,18 @@ 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()

View File

@ -37,12 +37,10 @@ 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',
write=Mock(), quiet=True,
).wait() ).wait()
assert proc.rc != 0 assert proc.rc != 0
proc.write.assert_called_once_with( assert proc.err == 'sh: line 1: NON: command not found'
b'sh: line 1: NON: command not found\x1b[0m\n'
)
@pytest.mark.asyncio @pytest.mark.asyncio
@ -70,6 +68,16 @@ 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 '
@ -78,6 +86,16 @@ 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
@ -87,6 +105,17 @@ 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
@ -108,6 +137,16 @@ 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 '
@ -140,7 +179,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_once_with(b'h\x1b[38;5;51mi\x1b[0m\n') proc.write.assert_called_with(b'h\x1b[38;5;51mi\x1b[0m\n')
@pytest.mark.asyncio @pytest.mark.asyncio
@ -155,4 +194,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_once_with(b'h\x1b[31mi\n') proc.write.assert_called_with(b'h\x1b[31mi\n')