logrunner 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logrunner-0.0.4/LICENSE +21 -0
- logrunner-0.0.4/PKG-INFO +52 -0
- logrunner-0.0.4/README.md +29 -0
- logrunner-0.0.4/pyproject.toml +61 -0
- logrunner-0.0.4/setup.cfg +4 -0
- logrunner-0.0.4/src/logrunner/__init__.py +90 -0
- logrunner-0.0.4/src/logrunner.egg-info/PKG-INFO +52 -0
- logrunner-0.0.4/src/logrunner.egg-info/SOURCES.txt +10 -0
- logrunner-0.0.4/src/logrunner.egg-info/dependency_links.txt +1 -0
- logrunner-0.0.4/src/logrunner.egg-info/requires.txt +4 -0
- logrunner-0.0.4/src/logrunner.egg-info/top_level.txt +1 -0
- logrunner-0.0.4/tests/test_logrunner.py +17 -0
logrunner-0.0.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2026] [Chris 'KageKirin' Helmich]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
logrunner-0.0.4/PKG-INFO
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logrunner
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: logrunner.run([cmd]) wraps subprocess.run([cmd]) to allow continuous output as well as capture of stdout
|
|
5
|
+
Author-email: kagekirin <kagekirin@gmail.com>
|
|
6
|
+
Maintainer-email: kagekirin <kagekirin@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/KageKirin/py-logrunner
|
|
9
|
+
Project-URL: Documentation, https://github.com/KageKirin/py-logrunner
|
|
10
|
+
Project-URL: Repository, https://github.com/KageKirin/py-logrunner.git
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=9.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# Logrunner
|
|
25
|
+
|
|
26
|
+
`logrunner.run([cmd])` wraps `subprocess.run([cmd])` to allow continuous output as well as capture of stdout and stderr.
|
|
27
|
+
|
|
28
|
+
This function is intended for usage in CI and other process monitoring applications where
|
|
29
|
+
the process outputs need to be written for real-time error tracking
|
|
30
|
+
and captured for post-processing text analysis.
|
|
31
|
+
|
|
32
|
+
## ⚡ Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import logrunner
|
|
36
|
+
|
|
37
|
+
(ret, out, err) = logrunner.run(["echo", "hello"])
|
|
38
|
+
# returns (0, "hello", "")
|
|
39
|
+
|
|
40
|
+
(ret, out, err) = logrunner.run(["ls", "/nonexistentpath"])
|
|
41
|
+
# return (1, "", "No such file or directory")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 🔧 Building
|
|
45
|
+
|
|
46
|
+
```shell
|
|
47
|
+
uv build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🤝 Collaborate with My Project
|
|
51
|
+
|
|
52
|
+
Please refer to the [collaboration guidelines](./COLLABORATION.md) for details.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Logrunner
|
|
2
|
+
|
|
3
|
+
`logrunner.run([cmd])` wraps `subprocess.run([cmd])` to allow continuous output as well as capture of stdout and stderr.
|
|
4
|
+
|
|
5
|
+
This function is intended for usage in CI and other process monitoring applications where
|
|
6
|
+
the process outputs need to be written for real-time error tracking
|
|
7
|
+
and captured for post-processing text analysis.
|
|
8
|
+
|
|
9
|
+
## ⚡ Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import logrunner
|
|
13
|
+
|
|
14
|
+
(ret, out, err) = logrunner.run(["echo", "hello"])
|
|
15
|
+
# returns (0, "hello", "")
|
|
16
|
+
|
|
17
|
+
(ret, out, err) = logrunner.run(["ls", "/nonexistentpath"])
|
|
18
|
+
# return (1, "", "No such file or directory")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 🔧 Building
|
|
22
|
+
|
|
23
|
+
```shell
|
|
24
|
+
uv build
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 🤝 Collaborate with My Project
|
|
28
|
+
|
|
29
|
+
Please refer to the [collaboration guidelines](./COLLABORATION.md) for details.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["wheel", "setuptools", "attrs>=17.1", "versioningit"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "logrunner"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "logrunner.run([cmd]) wraps subprocess.run([cmd]) to allow continuous output as well as capture of stdout"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">= 3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
|
|
14
|
+
authors = [
|
|
15
|
+
{name = "kagekirin", email = "kagekirin@gmail.com"},
|
|
16
|
+
]
|
|
17
|
+
maintainers = [
|
|
18
|
+
{name = "kagekirin", email = "kagekirin@gmail.com"},
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Topic :: Software Development :: Libraries",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/KageKirin/py-logrunner"
|
|
33
|
+
Documentation = "https://github.com/KageKirin/py-logrunner"
|
|
34
|
+
Repository = "https://github.com/KageKirin/py-logrunner.git"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=9.0",
|
|
39
|
+
"ruff",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.pytest]
|
|
43
|
+
minversion = "9.0"
|
|
44
|
+
addopts = ["-ra", "-q"]
|
|
45
|
+
pythonpath = ["src"]
|
|
46
|
+
testpaths = [
|
|
47
|
+
"tests",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src/"]
|
|
53
|
+
include = ["logrunner"]
|
|
54
|
+
|
|
55
|
+
[tool.versioningit.next-version]
|
|
56
|
+
method = "smallest"
|
|
57
|
+
|
|
58
|
+
[tool.versioningit.format]
|
|
59
|
+
distance = "{next_version}.dev{distance}+{vcs}{rev}"
|
|
60
|
+
dirty = "{base_version}+d{build_date:%Y%m%d}"
|
|
61
|
+
distance-dirty = "{next_version}.dev{distance}+{vcs}{rev}.d{build_date:%Y%m%d}"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
## logrunner
|
|
2
|
+
|
|
3
|
+
# namespace: logrunner
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import sys
|
|
7
|
+
import selectors
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run(
|
|
12
|
+
args,
|
|
13
|
+
*,
|
|
14
|
+
stdin=None,
|
|
15
|
+
cwd=None,
|
|
16
|
+
check=False,
|
|
17
|
+
encoding=None,
|
|
18
|
+
errors=None,
|
|
19
|
+
text=None,
|
|
20
|
+
env=None,
|
|
21
|
+
**other_popen_kwargs,
|
|
22
|
+
):
|
|
23
|
+
|
|
24
|
+
stdoutbuf = io.StringIO()
|
|
25
|
+
stderrbuf = io.StringIO()
|
|
26
|
+
|
|
27
|
+
kwargs = {
|
|
28
|
+
"stdout": subprocess.PIPE,
|
|
29
|
+
"stderr": subprocess.PIPE, # STDOUT to pipe STDERR into STDOUT for simultaneous capture
|
|
30
|
+
"bufsize": 1, # bufsize = 1 means output is line buffered
|
|
31
|
+
"universal_newlines": True, # required for line buffering
|
|
32
|
+
## forwarding the remaining params
|
|
33
|
+
"stdin": stdin,
|
|
34
|
+
"cwd": cwd,
|
|
35
|
+
# timeout=timeout,
|
|
36
|
+
"errors": errors,
|
|
37
|
+
"text": text,
|
|
38
|
+
"env": env,
|
|
39
|
+
**other_popen_kwargs,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Start subprocess
|
|
43
|
+
process = subprocess.Popen(args, **kwargs)
|
|
44
|
+
|
|
45
|
+
# Callback function for process STDOUT
|
|
46
|
+
def handle_stdout(stream, mask):
|
|
47
|
+
# NOTE: Because the process' output is line buffered, there's only ever one line to read when this function is called
|
|
48
|
+
line = stream.readline()
|
|
49
|
+
stdoutbuf.write(line)
|
|
50
|
+
sys.stdout.write(line)
|
|
51
|
+
|
|
52
|
+
# Callback function for process STDERR
|
|
53
|
+
def handle_stderr(stream, mask):
|
|
54
|
+
# NOTE: Because the process' output is line buffered, there's only ever one line to read when this function is called
|
|
55
|
+
line = stream.readline()
|
|
56
|
+
stderrbuf.write(line)
|
|
57
|
+
sys.stderr.write(line)
|
|
58
|
+
|
|
59
|
+
# Register callback for an "available for read" event from subprocess' stdout stream
|
|
60
|
+
selector = selectors.DefaultSelector()
|
|
61
|
+
selector.register(process.stdout, selectors.EVENT_READ, handle_stdout)
|
|
62
|
+
selector.register(process.stderr, selectors.EVENT_READ, handle_stderr)
|
|
63
|
+
|
|
64
|
+
# Loop until subprocess is terminated
|
|
65
|
+
while process.poll() is None:
|
|
66
|
+
# Wait for events and handle them with their registered callbacks
|
|
67
|
+
events = selector.select()
|
|
68
|
+
for key, mask in events:
|
|
69
|
+
callback = key.data
|
|
70
|
+
callback(key.fileobj, mask)
|
|
71
|
+
|
|
72
|
+
# Get process return code
|
|
73
|
+
returncode = process.wait()
|
|
74
|
+
selector.close()
|
|
75
|
+
|
|
76
|
+
# Store buffered output
|
|
77
|
+
stdoutput = stdoutbuf.getvalue()
|
|
78
|
+
stdoutbuf.close()
|
|
79
|
+
stderrput = stderrbuf.getvalue()
|
|
80
|
+
stderrbuf.close()
|
|
81
|
+
|
|
82
|
+
if check and returncode != 0:
|
|
83
|
+
raise subprocess.CalledProcessError(
|
|
84
|
+
returncode=returncode,
|
|
85
|
+
cmd=" ".join(args),
|
|
86
|
+
output=stdoutput,
|
|
87
|
+
stderr=stderrput,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return (returncode, stdoutput, stderrput)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: logrunner
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: logrunner.run([cmd]) wraps subprocess.run([cmd]) to allow continuous output as well as capture of stdout
|
|
5
|
+
Author-email: kagekirin <kagekirin@gmail.com>
|
|
6
|
+
Maintainer-email: kagekirin <kagekirin@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/KageKirin/py-logrunner
|
|
9
|
+
Project-URL: Documentation, https://github.com/KageKirin/py-logrunner
|
|
10
|
+
Project-URL: Repository, https://github.com/KageKirin/py-logrunner.git
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=9.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# Logrunner
|
|
25
|
+
|
|
26
|
+
`logrunner.run([cmd])` wraps `subprocess.run([cmd])` to allow continuous output as well as capture of stdout and stderr.
|
|
27
|
+
|
|
28
|
+
This function is intended for usage in CI and other process monitoring applications where
|
|
29
|
+
the process outputs need to be written for real-time error tracking
|
|
30
|
+
and captured for post-processing text analysis.
|
|
31
|
+
|
|
32
|
+
## ⚡ Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import logrunner
|
|
36
|
+
|
|
37
|
+
(ret, out, err) = logrunner.run(["echo", "hello"])
|
|
38
|
+
# returns (0, "hello", "")
|
|
39
|
+
|
|
40
|
+
(ret, out, err) = logrunner.run(["ls", "/nonexistentpath"])
|
|
41
|
+
# return (1, "", "No such file or directory")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 🔧 Building
|
|
45
|
+
|
|
46
|
+
```shell
|
|
47
|
+
uv build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🤝 Collaborate with My Project
|
|
51
|
+
|
|
52
|
+
Please refer to the [collaboration guidelines](./COLLABORATION.md) for details.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/logrunner/__init__.py
|
|
5
|
+
src/logrunner.egg-info/PKG-INFO
|
|
6
|
+
src/logrunner.egg-info/SOURCES.txt
|
|
7
|
+
src/logrunner.egg-info/dependency_links.txt
|
|
8
|
+
src/logrunner.egg-info/requires.txt
|
|
9
|
+
src/logrunner.egg-info/top_level.txt
|
|
10
|
+
tests/test_logrunner.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
logrunner
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import logrunner
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
def test():
|
|
5
|
+
(ret, out, err) = logrunner.run(["echo", "hello"])
|
|
6
|
+
assert ret == 0
|
|
7
|
+
assert out.strip() == "hello"
|
|
8
|
+
assert err == ""
|
|
9
|
+
|
|
10
|
+
def test_error():
|
|
11
|
+
(ret, out, err) = logrunner.run(["ls", "/nonexistentpath"])
|
|
12
|
+
assert ret != 0
|
|
13
|
+
assert out == ""
|
|
14
|
+
assert "No such file or directory" in err
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
pytest.main()
|