runall 0.1.0__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.
@@ -0,0 +1,45 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Packaging and build artifacts
7
+ build/
8
+ dist/
9
+ wheels/
10
+ *.egg-info/
11
+ *.egg
12
+ .eggs/
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+
19
+ # Test, coverage, and tooling caches
20
+ .pytest_cache/
21
+ .mypy_cache/
22
+ .ruff_cache/
23
+ .tox/
24
+ .nox/
25
+ .coverage
26
+ .coverage.*
27
+ htmlcov/
28
+
29
+ # Environment files and secrets
30
+ .env
31
+ .env.*
32
+ !.env.example
33
+
34
+ # Editor and OS files
35
+ .idea/
36
+ .vscode/
37
+ *.swp
38
+ .DS_Store
39
+ Thumbs.db
40
+
41
+ # Runtime output
42
+ /output/
43
+ /err/
44
+
45
+ PUBLISHING.md
@@ -0,0 +1 @@
1
+ 3.14
runall-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Roberto Fernandes Tavares Neto
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.
runall-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,160 @@
1
+ Metadata-Version: 2.5
2
+ Name: runall
3
+ Version: 0.1.0
4
+ Summary: Run functions registered by @runthat decorator with a set of progress bars.
5
+ Project-URL: Homepage, https://github.com/tavaresneto/pyrunall
6
+ Project-URL: Repository, https://github.com/tavaresneto/pyrunall
7
+ Project-URL: Issues, https://github.com/tavaresneto/pyrunall/issues
8
+ Author: Roberto Fernandes Tavares Neto
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: textual>=8.2.8
13
+ Description-Content-Type: text/markdown
14
+
15
+ # runall
16
+
17
+ `runall` is a Python package that runs shell commands over a set of input files
18
+ and displays the progress in a terminal user interface (TUI). Commands are
19
+ declared as ordinary Python functions registered with the `@runthat` decorator.
20
+ For every function, the interface shows a progress bar (the bar of the function
21
+ that is currently running is highlighted in orange) and a scrolling list of the
22
+ files that have been processed.
23
+
24
+ ## Features
25
+
26
+ - Registers any number of command-building functions with a single decorator.
27
+ - Selects input files with a glob pattern.
28
+ - Displays one progress bar per function and a running list of processed files.
29
+ - Writes the standard output of every command to a CSV file per function.
30
+ - Logs each executed command and its standard error output to a separate file.
31
+
32
+ ## Requirements
33
+
34
+ - Python 3.10 or later
35
+ - [Textual](https://textual.textualize.io/) 8.2.8 or later (installed automatically)
36
+ - [uv](https://docs.astral.sh/uv/) (recommended for development)
37
+
38
+ ## Installation
39
+
40
+ ```sh
41
+ pip install runall
42
+ ```
43
+
44
+ ```sh
45
+ uv add runall
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ 1. Create a configuration dictionary (see [Configuration](#configuration)).
51
+ 2. Decorate one or more functions with `@runthat(config=config)`. Each function
52
+ receives the path of an input file and returns the shell command to execute.
53
+ 3. Start the interface with `RunAllApp(config).run()`.
54
+
55
+ For every input file, `runall` executes each registered function in the order in
56
+ which it was defined. The commands run sequentially.
57
+
58
+ ### Example
59
+
60
+ The complete example below is available in `examples/demo.py`. It registers three
61
+ functions that each wait five seconds and then print one line:
62
+
63
+ ```python
64
+ # from runall.config import CONFIG
65
+ from runall.tui import RunAllApp, runthat
66
+
67
+ config = {
68
+ "runthat": {},
69
+ "input_dir": "instances/ht0_*.txt",
70
+ "output_dir": "output",
71
+ "output_preffix": "out_",
72
+ "output_header": "execucao,arquivo",
73
+ "err_dir": "err",
74
+ }
75
+
76
+ @runthat(config=config)
77
+ def exec1(filename: str) -> str:
78
+ return f'sleep 5;echo "[1] {filename}"'
79
+
80
+
81
+ @runthat(config=config)
82
+ def exec2(filename: str) -> str:
83
+ return f'sleep 5;echo "[2] {filename}"'
84
+
85
+
86
+ @runthat(config=config)
87
+ def exec3(filename: str) -> str:
88
+ return f'sleep 5;echo "[3] {filename}"'
89
+
90
+
91
+ def main() -> None:
92
+ RunAllApp(config).run()
93
+
94
+
95
+ if __name__ == "__main__":
96
+ main()
97
+ ```
98
+
99
+ Run it from the repository root, so that the relative paths in the configuration
100
+ resolve correctly:
101
+
102
+ ```sh
103
+ uv run python examples/demo.py
104
+ ```
105
+
106
+ ## Configuration
107
+
108
+ The configuration is a plain dictionary with the following keys:
109
+
110
+ | Key | Description |
111
+ | ---------------- | -------------------------------------------------------------------------------------------- |
112
+ | `runthat` | Registry of decorated functions. Start it as an empty dictionary; `@runthat` fills it in. |
113
+ | `input_dir` | Glob pattern that selects the input files, for example `instances/ht0_*.txt`. |
114
+ | `output_dir` | Directory that receives the standard output of each function. |
115
+ | `output_preffix` | Prefix of every output file name. The key keeps its original spelling. |
116
+ | `output_header` | Header line written once at the top of each newly created output file. |
117
+ | `err_dir` | Directory that receives the executed commands and their standard error output. |
118
+
119
+ A default configuration is also available as `runall.CONFIG`.
120
+
121
+ ## Output files
122
+
123
+ For a function named `exec1` and the configuration above, `runall` writes:
124
+
125
+ - `output/out_exec1.csv`: the header line followed by the standard output of every
126
+ execution of `exec1`.
127
+ - `err/out_exec1.csv`: for every execution, the line `$ <command>` followed by
128
+ the standard error output of the command, if any.
129
+
130
+ Both files are opened in append mode, so results accumulate across runs. Remove
131
+ the output directories before a run to start from a clean state. Commands should
132
+ print lines that match the columns of `output_header`.
133
+
134
+ ## Security considerations
135
+
136
+ Commands are executed through the shell (`shell=True`), and the file name is
137
+ inserted into the command string that your function returns. Quote file names
138
+ (for example with `shlex.quote`) if they may contain spaces or shell
139
+ metacharacters, and never register commands built from untrusted input.
140
+
141
+ The exit status of a command is not treated as an error: a failing command is
142
+ recorded in the error log and processing continues with the next command.
143
+
144
+ ## Project layout
145
+
146
+ ```text
147
+ src/runall/
148
+ ├── __init__.py Public API: CONFIG, discover_input_files, runthat
149
+ ├── config.py Default configuration
150
+ ├── processor.py Input file discovery
151
+ └── tui.py The @runthat decorator and the RunAllApp interface
152
+ examples/
153
+ └── demo.py Complete usage example
154
+ instances/ Sample input files used by the example
155
+ ```
156
+
157
+ ## License
158
+
159
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
160
+ for details.
runall-0.1.0/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # runall
2
+
3
+ `runall` is a Python package that runs shell commands over a set of input files
4
+ and displays the progress in a terminal user interface (TUI). Commands are
5
+ declared as ordinary Python functions registered with the `@runthat` decorator.
6
+ For every function, the interface shows a progress bar (the bar of the function
7
+ that is currently running is highlighted in orange) and a scrolling list of the
8
+ files that have been processed.
9
+
10
+ ## Features
11
+
12
+ - Registers any number of command-building functions with a single decorator.
13
+ - Selects input files with a glob pattern.
14
+ - Displays one progress bar per function and a running list of processed files.
15
+ - Writes the standard output of every command to a CSV file per function.
16
+ - Logs each executed command and its standard error output to a separate file.
17
+
18
+ ## Requirements
19
+
20
+ - Python 3.10 or later
21
+ - [Textual](https://textual.textualize.io/) 8.2.8 or later (installed automatically)
22
+ - [uv](https://docs.astral.sh/uv/) (recommended for development)
23
+
24
+ ## Installation
25
+
26
+ ```sh
27
+ pip install runall
28
+ ```
29
+
30
+ ```sh
31
+ uv add runall
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ 1. Create a configuration dictionary (see [Configuration](#configuration)).
37
+ 2. Decorate one or more functions with `@runthat(config=config)`. Each function
38
+ receives the path of an input file and returns the shell command to execute.
39
+ 3. Start the interface with `RunAllApp(config).run()`.
40
+
41
+ For every input file, `runall` executes each registered function in the order in
42
+ which it was defined. The commands run sequentially.
43
+
44
+ ### Example
45
+
46
+ The complete example below is available in `examples/demo.py`. It registers three
47
+ functions that each wait five seconds and then print one line:
48
+
49
+ ```python
50
+ # from runall.config import CONFIG
51
+ from runall.tui import RunAllApp, runthat
52
+
53
+ config = {
54
+ "runthat": {},
55
+ "input_dir": "instances/ht0_*.txt",
56
+ "output_dir": "output",
57
+ "output_preffix": "out_",
58
+ "output_header": "execucao,arquivo",
59
+ "err_dir": "err",
60
+ }
61
+
62
+ @runthat(config=config)
63
+ def exec1(filename: str) -> str:
64
+ return f'sleep 5;echo "[1] {filename}"'
65
+
66
+
67
+ @runthat(config=config)
68
+ def exec2(filename: str) -> str:
69
+ return f'sleep 5;echo "[2] {filename}"'
70
+
71
+
72
+ @runthat(config=config)
73
+ def exec3(filename: str) -> str:
74
+ return f'sleep 5;echo "[3] {filename}"'
75
+
76
+
77
+ def main() -> None:
78
+ RunAllApp(config).run()
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()
83
+ ```
84
+
85
+ Run it from the repository root, so that the relative paths in the configuration
86
+ resolve correctly:
87
+
88
+ ```sh
89
+ uv run python examples/demo.py
90
+ ```
91
+
92
+ ## Configuration
93
+
94
+ The configuration is a plain dictionary with the following keys:
95
+
96
+ | Key | Description |
97
+ | ---------------- | -------------------------------------------------------------------------------------------- |
98
+ | `runthat` | Registry of decorated functions. Start it as an empty dictionary; `@runthat` fills it in. |
99
+ | `input_dir` | Glob pattern that selects the input files, for example `instances/ht0_*.txt`. |
100
+ | `output_dir` | Directory that receives the standard output of each function. |
101
+ | `output_preffix` | Prefix of every output file name. The key keeps its original spelling. |
102
+ | `output_header` | Header line written once at the top of each newly created output file. |
103
+ | `err_dir` | Directory that receives the executed commands and their standard error output. |
104
+
105
+ A default configuration is also available as `runall.CONFIG`.
106
+
107
+ ## Output files
108
+
109
+ For a function named `exec1` and the configuration above, `runall` writes:
110
+
111
+ - `output/out_exec1.csv`: the header line followed by the standard output of every
112
+ execution of `exec1`.
113
+ - `err/out_exec1.csv`: for every execution, the line `$ <command>` followed by
114
+ the standard error output of the command, if any.
115
+
116
+ Both files are opened in append mode, so results accumulate across runs. Remove
117
+ the output directories before a run to start from a clean state. Commands should
118
+ print lines that match the columns of `output_header`.
119
+
120
+ ## Security considerations
121
+
122
+ Commands are executed through the shell (`shell=True`), and the file name is
123
+ inserted into the command string that your function returns. Quote file names
124
+ (for example with `shlex.quote`) if they may contain spaces or shell
125
+ metacharacters, and never register commands built from untrusted input.
126
+
127
+ The exit status of a command is not treated as an error: a failing command is
128
+ recorded in the error log and processing continues with the next command.
129
+
130
+ ## Project layout
131
+
132
+ ```text
133
+ src/runall/
134
+ ├── __init__.py Public API: CONFIG, discover_input_files, runthat
135
+ ├── config.py Default configuration
136
+ ├── processor.py Input file discovery
137
+ └── tui.py The @runthat decorator and the RunAllApp interface
138
+ examples/
139
+ └── demo.py Complete usage example
140
+ instances/ Sample input files used by the example
141
+ ```
142
+
143
+ ## License
144
+
145
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE) file
146
+ for details.
@@ -0,0 +1,70 @@
1
+ """Demonstration of ``runall`` with three simulated commands.
2
+
3
+ Run this script from the repository root so that the relative paths in the
4
+ configuration resolve correctly::
5
+
6
+ uv run python examples/demo.py
7
+
8
+ Each registered function waits five seconds and then prints a line that
9
+ identifies the function and the input file. The results are appended to
10
+ ``output/`` and the executed commands to ``err/``.
11
+ """
12
+
13
+ # from runall.config import CONFIG
14
+ from runall.tui import RunAllApp, runthat
15
+
16
+ config = {
17
+ "runthat": {},
18
+ "input_dir": "instances/ht0_*.txt",
19
+ "output_dir": "output",
20
+ "output_preffix": "out_",
21
+ "output_header": "execucao,arquivo",
22
+ "err_dir": "err",
23
+ }
24
+
25
+ @runthat(config=config)
26
+ def exec1(filename: str) -> str:
27
+ """Build the first simulated command.
28
+
29
+ Args:
30
+ filename: Path of the input file.
31
+
32
+ Returns:
33
+ A shell command that sleeps and then prints a line tagged ``[1]``.
34
+ """
35
+ return f'sleep 5;echo "[1] {filename}"'
36
+
37
+
38
+ @runthat(config=config)
39
+ def exec2(filename: str) -> str:
40
+ """Build the second simulated command.
41
+
42
+ Args:
43
+ filename: Path of the input file.
44
+
45
+ Returns:
46
+ A shell command that sleeps and then prints a line tagged ``[2]``.
47
+ """
48
+ return f'sleep 5;echo "[2] {filename}"'
49
+
50
+
51
+ @runthat(config=config)
52
+ def exec3(filename: str) -> str:
53
+ """Build the third simulated command.
54
+
55
+ Args:
56
+ filename: Path of the input file.
57
+
58
+ Returns:
59
+ A shell command that sleeps and then prints a line tagged ``[3]``.
60
+ """
61
+ return f'sleep 5;echo "[3] {filename}"'
62
+
63
+
64
+ def main() -> None:
65
+ """Launch the terminal interface with the registered commands."""
66
+ RunAllApp(config).run()
67
+
68
+
69
+ if __name__ == "__main__":
70
+ main()
@@ -0,0 +1 @@
1
+ foo
@@ -0,0 +1 @@
1
+ bar
@@ -0,0 +1 @@
1
+ baz
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "runall"
7
+ version = "0.1.0"
8
+ description = "Run functions registered by @runthat decorator with a set of progress bars."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Roberto Fernandes Tavares Neto" }]
13
+ requires-python = ">=3.10"
14
+ dependencies = [
15
+ "textual>=8.2.8"
16
+ ]
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/tavaresneto/pyrunall"
20
+ Repository = "https://github.com/tavaresneto/pyrunall"
21
+ Issues = "https://github.com/tavaresneto/pyrunall/issues"
22
+
23
+ [tool.hatch.build.targets.wheel]
24
+ packages = ["src/runall"]
@@ -0,0 +1,20 @@
1
+ """Run shell commands over a set of input files with a terminal progress display.
2
+
3
+ The ``runall`` package lets a user register Python functions with the
4
+ :func:`runthat` decorator. Each registered function receives the path of an
5
+ input file and returns a shell command. The command is executed for every
6
+ input file that matches a glob pattern, while a Textual-based terminal user
7
+ interface (:class:`runall.tui.RunAllApp`) displays one progress bar per
8
+ registered function and a running list of the processed files.
9
+
10
+ Public names:
11
+ CONFIG: A default configuration dictionary.
12
+ discover_input_files: Resolves the input files described by a configuration.
13
+ runthat: Decorator that registers a function in a configuration.
14
+ """
15
+
16
+ from .config import CONFIG
17
+ from .processor import discover_input_files
18
+ from .tui import runthat
19
+
20
+ __all__ = ["CONFIG", "discover_input_files", "runthat"]
@@ -0,0 +1,28 @@
1
+ """Default configuration for the ``runall`` package.
2
+
3
+ A configuration is a plain dictionary. The same structure may be defined by
4
+ the user and passed to :func:`runall.tui.runthat` and
5
+ :class:`runall.tui.RunAllApp`.
6
+ """
7
+
8
+ CONFIG = {
9
+ "runthat": {},
10
+ "input_dir": "instances/ht0_*.txt",
11
+ "output_dir": "output",
12
+ "output_preffix": "out_",
13
+ "output_header": "execucao,arquivo",
14
+ "err_dir": "err",
15
+ }
16
+ """Default configuration dictionary.
17
+
18
+ Keys:
19
+ runthat: Registry that maps function names to the wrapped callables. It is
20
+ populated by the :func:`runall.tui.runthat` decorator.
21
+ input_dir: Glob pattern that selects the input files.
22
+ output_dir: Directory that receives the standard output of each command.
23
+ output_preffix: Prefix of every output file name. The key name keeps its
24
+ historical spelling.
25
+ output_header: Header line written once at the top of each new output file.
26
+ err_dir: Directory that receives the executed commands and their standard
27
+ error output.
28
+ """
@@ -0,0 +1,22 @@
1
+ """Discovery of the input files that ``runall`` processes."""
2
+
3
+ import glob
4
+
5
+ from .config import CONFIG
6
+
7
+
8
+ def discover_input_files(config: dict | None = None) -> list[str]:
9
+ """Return the input files that match the configured glob pattern.
10
+
11
+ Args:
12
+ config: Configuration dictionary whose ``"input_dir"`` entry holds the
13
+ glob pattern. When ``None``, the default
14
+ :data:`runall.config.CONFIG` is used.
15
+
16
+ Returns:
17
+ The matching paths in lexicographic order. The list is empty when no
18
+ file matches the pattern.
19
+ """
20
+ if config is None:
21
+ config = CONFIG
22
+ return sorted(glob.glob(config["input_dir"]))