Compare commits

..

No commits in common. "master" and "ci" have entirely different histories.
master ... ci

5 changed files with 13 additions and 95 deletions

View File

@ -7,7 +7,8 @@ Why?
In Python we now have async subprocesses which allows to execute several
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
the time so that you know which line is for which process, like with
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.
Demo
====
.. image:: https://yourlabs.io/oss/shlax/-/raw/master/demo.png
You will find the demo script in demo.py in this repository.
Usage
=====
@ -102,9 +96,8 @@ will be applied line by line:
Where is the rest?
==================
Shlax used to be the name of a much more ambitious poc-project, that you can
still find in the ``OLD`` branch of this repository. It has been extracted in
two projects with clear boundaries, namely `sysplan
Shlax used to be the name of a much more ambitious poc-project that has been
extracted in two projects with clear boundaries, namely `sysplan
<https://yourlabs.io/oss/sysplan>`_ and `podplan
<https://yourlabs.io/oss/podplan>`_ which are still in alpha state, although
Shlax as it now, is feature complete and stable.
<https://yourlabs.io/oss/podplan>`_ which are still in alpha state, but Shlax
as it is feature complete and stable.

BIN
demo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 KiB

24
demo.py
View File

@ -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())

View File

@ -1,7 +1,6 @@
import asyncio
import functools
import re
import shlex
import sys
from .colors import colors
@ -55,6 +54,7 @@ class Subprocess:
if len(args) == 1 and ' ' in args[0]:
args = ['sh', '-euc', args[0]]
self.cmd = ' '.join(args)
self.args = args
self.quiet = quiet if quiet is not None else False
self.prefix = prefix
@ -75,18 +75,6 @@ class Subprocess:
self.regexps[search] = replace
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
# low-level APIs.
loop = asyncio.get_running_loop()

View File

@ -37,10 +37,12 @@ async def test_wait_unbound():
async def test_rc_1():
proc = await Proc(
'NON EXISTING COMMAND',
quiet=True,
write=Mock(),
).wait()
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
@ -68,16 +70,6 @@ async def test_prefix():
).wait()
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(
Proc.prefix_colors[0].encode()
+ b'test_prefix '
@ -86,16 +78,6 @@ async def test_prefix():
+ Proc.colors.reset.encode()
+ 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(
Proc.prefix_colors[1].encode()
# padding has been added because of output1
@ -105,17 +87,6 @@ async def test_prefix():
+ Proc.colors.reset.encode()
+ 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(
Proc.prefix_colors[0].encode()
# padding has been added because of output1
@ -137,16 +108,6 @@ async def test_prefix_multiline():
prefix='test_prefix',
).wait()
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(
Proc.prefix_colors[0].encode()
+ b'test_prefix '
@ -179,7 +140,7 @@ async def test_highlight():
r'h([\w\d-]+)': 'h{cyan}\\1',
}
).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
@ -194,4 +155,4 @@ async def test_highlight_if_not_colored():
r'h([\w\d-]+)': 'h{cyan}\\1',
}
).wait()
proc.write.assert_called_with(b'h\x1b[31mi\n')
proc.write.assert_called_once_with(b'h\x1b[31mi\n')