snakemake-logger-plugin-rich 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.
- snakemake_logger_plugin_rich-0.1.0/.github/workflows/release.yml +137 -0
- snakemake_logger_plugin_rich-0.1.0/.gitignore +163 -0
- snakemake_logger_plugin_rich-0.1.0/.python-version +1 -0
- snakemake_logger_plugin_rich-0.1.0/CHANGELOG.md +5 -0
- snakemake_logger_plugin_rich-0.1.0/PKG-INFO +15 -0
- snakemake_logger_plugin_rich-0.1.0/README.md +5 -0
- snakemake_logger_plugin_rich-0.1.0/pyproject.toml +15 -0
- snakemake_logger_plugin_rich-0.1.0/src/snakemake_logger_plugin_rich/__init__.py +46 -0
- snakemake_logger_plugin_rich-0.1.0/src/snakemake_logger_plugin_rich/handler.py +139 -0
- snakemake_logger_plugin_rich-0.1.0/uv.lock +128 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "[0-9]+.[0-9]+.[0-9]+"
|
|
7
|
+
- "[0-9]+.[0-9]+.[0-9]+a[0-9]+"
|
|
8
|
+
- "[0-9]+.[0-9]+.[0-9]+b[0-9]+"
|
|
9
|
+
- "[0-9]+.[0-9]+.[0-9]+rc[0-9]+"
|
|
10
|
+
|
|
11
|
+
env:
|
|
12
|
+
PACKAGE_NAME: "snakemake-logger-plugin-rich"
|
|
13
|
+
OWNER: "cademirch"
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
details:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
outputs:
|
|
19
|
+
new_version: ${{ steps.release.outputs.new_version }}
|
|
20
|
+
suffix: ${{ steps.release.outputs.suffix }}
|
|
21
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v2
|
|
24
|
+
|
|
25
|
+
- name: Extract tag and Details
|
|
26
|
+
id: release
|
|
27
|
+
run: |
|
|
28
|
+
if [ "${{ github.ref_type }}" = "tag" ]; then
|
|
29
|
+
TAG_NAME=${GITHUB_REF#refs/tags/}
|
|
30
|
+
NEW_VERSION=$(echo $TAG_NAME | awk -F'-' '{print $1}')
|
|
31
|
+
SUFFIX=$(echo $TAG_NAME | grep -oP '[a-z]+[0-9]+' || echo "")
|
|
32
|
+
echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
|
|
33
|
+
echo "suffix=$SUFFIX" >> "$GITHUB_OUTPUT"
|
|
34
|
+
echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT"
|
|
35
|
+
echo "Version is $NEW_VERSION"
|
|
36
|
+
echo "Suffix is $SUFFIX"
|
|
37
|
+
echo "Tag name is $TAG_NAME"
|
|
38
|
+
else
|
|
39
|
+
echo "No tag found"
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
check_pypi:
|
|
44
|
+
needs: details
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- name: Fetch information from PyPI
|
|
48
|
+
run: |
|
|
49
|
+
response=$(curl -s https://pypi.org/pypi/${{ env.PACKAGE_NAME }}/json || echo "{}")
|
|
50
|
+
latest_previous_version=$(echo $response | grep -oP '"releases":\{"\K[^"]+' | sort -rV | head -n 1)
|
|
51
|
+
if [ -z "$latest_previous_version" ]; then
|
|
52
|
+
echo "Package not found on PyPI."
|
|
53
|
+
latest_previous_version="0.0.0"
|
|
54
|
+
fi
|
|
55
|
+
echo "Latest version on PyPI: $latest_previous_version"
|
|
56
|
+
echo "latest_previous_version=$latest_previous_version" >> $GITHUB_ENV
|
|
57
|
+
|
|
58
|
+
- name: Compare versions and exit if not newer
|
|
59
|
+
run: |
|
|
60
|
+
NEW_VERSION=${{ needs.details.outputs.new_version }}
|
|
61
|
+
LATEST_VERSION=$latest_previous_version
|
|
62
|
+
if [ "$(printf '%s\n' "$LATEST_VERSION" "$NEW_VERSION" | sort -rV | head -n 1)" != "$NEW_VERSION" ] || [ "$NEW_VERSION" == "$LATEST_VERSION" ]; then
|
|
63
|
+
echo "The new version $NEW_VERSION is not greater than the latest version $LATEST_VERSION on PyPI."
|
|
64
|
+
exit 1
|
|
65
|
+
else
|
|
66
|
+
echo "The new version $NEW_VERSION is greater than the latest version $LATEST_VERSION on PyPI."
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
setup_and_build:
|
|
70
|
+
needs: [details, check_pypi]
|
|
71
|
+
runs-on: ubuntu-latest
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v2
|
|
74
|
+
|
|
75
|
+
- name: Install uv
|
|
76
|
+
uses: astral-sh/setup-uv@v3
|
|
77
|
+
|
|
78
|
+
- name: "Set up Python"
|
|
79
|
+
uses: actions/setup-python@v5
|
|
80
|
+
with:
|
|
81
|
+
python-version-file: "pyproject.toml"
|
|
82
|
+
|
|
83
|
+
- name: Install the project
|
|
84
|
+
run: uv sync --all-extras --dev
|
|
85
|
+
|
|
86
|
+
- name: Build source and wheel distribution
|
|
87
|
+
run: |
|
|
88
|
+
uv build
|
|
89
|
+
|
|
90
|
+
- name: Upload artifacts
|
|
91
|
+
uses: actions/upload-artifact@v3
|
|
92
|
+
with:
|
|
93
|
+
name: dist
|
|
94
|
+
path: dist/
|
|
95
|
+
|
|
96
|
+
pypi_publish:
|
|
97
|
+
name: Upload release to PyPI
|
|
98
|
+
needs: [setup_and_build, details]
|
|
99
|
+
runs-on: ubuntu-latest
|
|
100
|
+
environment:
|
|
101
|
+
name: release
|
|
102
|
+
permissions:
|
|
103
|
+
id-token: write
|
|
104
|
+
steps:
|
|
105
|
+
- name: Download artifacts
|
|
106
|
+
uses: actions/download-artifact@v3
|
|
107
|
+
with:
|
|
108
|
+
name: dist
|
|
109
|
+
path: dist/
|
|
110
|
+
|
|
111
|
+
- name: Publish distribution to PyPI
|
|
112
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
113
|
+
|
|
114
|
+
github_release:
|
|
115
|
+
name: Create GitHub Release
|
|
116
|
+
needs: [setup_and_build, details]
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
permissions:
|
|
119
|
+
contents: write
|
|
120
|
+
steps:
|
|
121
|
+
- name: Checkout Code
|
|
122
|
+
uses: actions/checkout@v3
|
|
123
|
+
with:
|
|
124
|
+
fetch-depth: 0
|
|
125
|
+
|
|
126
|
+
- name: Download artifacts
|
|
127
|
+
uses: actions/download-artifact@v3
|
|
128
|
+
with:
|
|
129
|
+
name: dist
|
|
130
|
+
path: dist/
|
|
131
|
+
|
|
132
|
+
- name: Create GitHub Release
|
|
133
|
+
id: create_release
|
|
134
|
+
env:
|
|
135
|
+
GH_TOKEN: ${{ github.token }}
|
|
136
|
+
run: |
|
|
137
|
+
gh release create ${{ needs.details.outputs.tag_name }} dist/* --title ${{ needs.details.outputs.tag_name }} --generate-notes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
### Generated by gibo (https://github.com/simonwhitaker/gibo)
|
|
2
|
+
### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Python.gitignore
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# poetry
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
105
|
+
#poetry.lock
|
|
106
|
+
|
|
107
|
+
# pdm
|
|
108
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
109
|
+
#pdm.lock
|
|
110
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
111
|
+
# in version control.
|
|
112
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
113
|
+
.pdm.toml
|
|
114
|
+
|
|
115
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
116
|
+
__pypackages__/
|
|
117
|
+
|
|
118
|
+
# Celery stuff
|
|
119
|
+
celerybeat-schedule
|
|
120
|
+
celerybeat.pid
|
|
121
|
+
|
|
122
|
+
# SageMath parsed files
|
|
123
|
+
*.sage.py
|
|
124
|
+
|
|
125
|
+
# Environments
|
|
126
|
+
.env
|
|
127
|
+
.venv
|
|
128
|
+
env/
|
|
129
|
+
venv/
|
|
130
|
+
ENV/
|
|
131
|
+
env.bak/
|
|
132
|
+
venv.bak/
|
|
133
|
+
|
|
134
|
+
# Spyder project settings
|
|
135
|
+
.spyderproject
|
|
136
|
+
.spyproject
|
|
137
|
+
|
|
138
|
+
# Rope project settings
|
|
139
|
+
.ropeproject
|
|
140
|
+
|
|
141
|
+
# mkdocs documentation
|
|
142
|
+
/site
|
|
143
|
+
|
|
144
|
+
# mypy
|
|
145
|
+
.mypy_cache/
|
|
146
|
+
.dmypy.json
|
|
147
|
+
dmypy.json
|
|
148
|
+
|
|
149
|
+
# Pyre type checker
|
|
150
|
+
.pyre/
|
|
151
|
+
|
|
152
|
+
# pytype static type analyzer
|
|
153
|
+
.pytype/
|
|
154
|
+
|
|
155
|
+
# Cython debug symbols
|
|
156
|
+
cython_debug/
|
|
157
|
+
|
|
158
|
+
# PyCharm
|
|
159
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
160
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
161
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
162
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
163
|
+
#.idea/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: snakemake-logger-plugin-rich
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Log plugin for snakemake using Rich
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: rich>=13.9.4
|
|
7
|
+
Requires-Dist: snakemake-interface-executor-plugins>=9.3.2
|
|
8
|
+
Requires-Dist: snakemake-interface-logger-plugins>=0.1.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Snakemake Rich Logger Plugin
|
|
12
|
+
|
|
13
|
+
Proof of concept logger plugin that uses [rich](https://github.com/Textualize/rich) for terminal styling and progress bar.
|
|
14
|
+
|
|
15
|
+
Log formatting still needs lots of work but the progress bar works well.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "snakemake-logger-plugin-rich"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Log plugin for snakemake using Rich"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"rich>=13.9.4",
|
|
9
|
+
"snakemake-interface-executor-plugins>=9.3.2",
|
|
10
|
+
"snakemake-interface-logger-plugins>=0.1.0",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[build-system]
|
|
14
|
+
requires = ["hatchling"]
|
|
15
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from snakemake_interface_logger_plugins.base import LoggerPluginBase
|
|
2
|
+
from snakemake_logger_plugin_rich.handler import RichLogHandler
|
|
3
|
+
from logging import Handler, Formatter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LoggerPlugin(LoggerPluginBase):
|
|
7
|
+
def __post__init(self) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Any additional setup after initialization.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def create_handler(
|
|
13
|
+
self,
|
|
14
|
+
quiet,
|
|
15
|
+
printshellcmds: bool,
|
|
16
|
+
printreason: bool,
|
|
17
|
+
debug_dag: bool,
|
|
18
|
+
nocolor: bool,
|
|
19
|
+
stdout: bool,
|
|
20
|
+
debug: bool,
|
|
21
|
+
mode,
|
|
22
|
+
show_failed_logs: bool,
|
|
23
|
+
dryrun: bool,
|
|
24
|
+
) -> Handler:
|
|
25
|
+
"""
|
|
26
|
+
Creates and returns an instance of RichLogHandler, configured with plugin settings.
|
|
27
|
+
"""
|
|
28
|
+
# Initialize RichLogHandler with plugin settings
|
|
29
|
+
handler = RichLogHandler(
|
|
30
|
+
quiet=quiet,
|
|
31
|
+
printshellcmds=printshellcmds,
|
|
32
|
+
printreason=printreason,
|
|
33
|
+
debug_dag=debug_dag,
|
|
34
|
+
nocolor=nocolor,
|
|
35
|
+
stdout=stdout,
|
|
36
|
+
debug=debug,
|
|
37
|
+
mode=mode,
|
|
38
|
+
show_failed_logs=show_failed_logs,
|
|
39
|
+
dryrun=dryrun,
|
|
40
|
+
show_time=True,
|
|
41
|
+
show_path=False,
|
|
42
|
+
markup=False,
|
|
43
|
+
)
|
|
44
|
+
FORMAT = Formatter("%(message)s")
|
|
45
|
+
handler.setFormatter(FORMAT)
|
|
46
|
+
return handler
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from rich.logging import RichHandler
|
|
3
|
+
from rich.progress import (
|
|
4
|
+
BarColumn,
|
|
5
|
+
MofNCompleteColumn,
|
|
6
|
+
Progress,
|
|
7
|
+
TextColumn,
|
|
8
|
+
TimeElapsedColumn,
|
|
9
|
+
TimeRemainingColumn,
|
|
10
|
+
)
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from typing import Optional
|
|
13
|
+
from snakemake_interface_executor_plugins.settings import ExecMode
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RichLogHandler(RichHandler):
|
|
17
|
+
"""
|
|
18
|
+
A custom Rich handler for Snakemake logging with a persistent progress bar at the bottom.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
quiet=None,
|
|
24
|
+
printshellcmds: Optional[bool] = None,
|
|
25
|
+
printreason: Optional[bool] = None,
|
|
26
|
+
debug_dag: Optional[bool] = None,
|
|
27
|
+
nocolor: Optional[bool] = None,
|
|
28
|
+
stdout: Optional[bool] = None,
|
|
29
|
+
debug: Optional[bool] = None,
|
|
30
|
+
mode=None,
|
|
31
|
+
show_failed_logs: Optional[bool] = None,
|
|
32
|
+
dryrun: Optional[bool] = None,
|
|
33
|
+
*args,
|
|
34
|
+
**kwargs,
|
|
35
|
+
):
|
|
36
|
+
console = Console(stderr=not stdout)
|
|
37
|
+
super().__init__(*args, **kwargs, console=console)
|
|
38
|
+
|
|
39
|
+
# Store additional configurations
|
|
40
|
+
self.quiet = quiet
|
|
41
|
+
self.printshellcmds = printshellcmds
|
|
42
|
+
self.printreason = printreason
|
|
43
|
+
self.debug_dag = debug_dag
|
|
44
|
+
self.nocolor = nocolor
|
|
45
|
+
self.stdout = stdout
|
|
46
|
+
self.debug = debug
|
|
47
|
+
self.mode = mode
|
|
48
|
+
self.show_failed_logs = show_failed_logs
|
|
49
|
+
self.dryrun = dryrun
|
|
50
|
+
self.stream = True
|
|
51
|
+
|
|
52
|
+
# Initialize the progress bar only if mode is not SUBPROCESS and not dryrun
|
|
53
|
+
if self.mode != ExecMode.SUBPROCESS and not self.dryrun:
|
|
54
|
+
self.progress = Progress(
|
|
55
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
56
|
+
BarColumn(),
|
|
57
|
+
MofNCompleteColumn(),
|
|
58
|
+
TextColumn("•"),
|
|
59
|
+
TimeElapsedColumn(),
|
|
60
|
+
TextColumn("•"),
|
|
61
|
+
TimeRemainingColumn(),
|
|
62
|
+
console=console,
|
|
63
|
+
auto_refresh=True,
|
|
64
|
+
)
|
|
65
|
+
self.progress_task = None
|
|
66
|
+
self.total_steps = 1 # To avoid division errors if not set
|
|
67
|
+
else:
|
|
68
|
+
self.progress = None
|
|
69
|
+
self.progress_task = None
|
|
70
|
+
|
|
71
|
+
def start_progress_bar(self, total_steps):
|
|
72
|
+
"""
|
|
73
|
+
Initialize and start the progress bar for a Snakemake job if progress is enabled.
|
|
74
|
+
"""
|
|
75
|
+
if self.progress:
|
|
76
|
+
self.total_steps = total_steps
|
|
77
|
+
self.progress_task = self.progress.add_task(
|
|
78
|
+
"Processing...", total=total_steps
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def get_level(self, record: logging.LogRecord) -> str:
|
|
82
|
+
"""
|
|
83
|
+
Gets snakemake log level from a log record. If there is no snakemake log level,
|
|
84
|
+
returns the log record's level name.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
record (logging.LogRecord)
|
|
88
|
+
Returns:
|
|
89
|
+
str: The log level
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
level = record.__dict__.get("level", None)
|
|
93
|
+
|
|
94
|
+
if level is None:
|
|
95
|
+
level = record.levelname
|
|
96
|
+
|
|
97
|
+
return level.lower()
|
|
98
|
+
|
|
99
|
+
def update_progress(self, done_steps):
|
|
100
|
+
"""
|
|
101
|
+
Update the progress bar with the number of completed steps.
|
|
102
|
+
"""
|
|
103
|
+
if self.progress and self.progress_task is not None:
|
|
104
|
+
self.progress.update(self.progress_task, completed=done_steps)
|
|
105
|
+
|
|
106
|
+
def emit(self, record):
|
|
107
|
+
"""
|
|
108
|
+
Emit log messages with Rich formatting and update the progress bar if necessary.
|
|
109
|
+
"""
|
|
110
|
+
message = self.format(record)
|
|
111
|
+
level = self.get_level(record)
|
|
112
|
+
|
|
113
|
+
if level == "progress" and self.progress:
|
|
114
|
+
done_steps = getattr(record, "done", 0)
|
|
115
|
+
total_steps = getattr(record, "total", self.total_steps)
|
|
116
|
+
if self.progress_task is None:
|
|
117
|
+
# Start progress bar if this is the first progress log
|
|
118
|
+
self.total_steps = total_steps
|
|
119
|
+
self.progress_task = self.progress.add_task(
|
|
120
|
+
"Processing...", total=total_steps
|
|
121
|
+
)
|
|
122
|
+
self.progress.start()
|
|
123
|
+
# Update the progress bar
|
|
124
|
+
self.progress.update(self.progress_task, completed=done_steps)
|
|
125
|
+
if level == "run_info":
|
|
126
|
+
record.message.replace("\n", "")
|
|
127
|
+
else:
|
|
128
|
+
super().emit(record)
|
|
129
|
+
|
|
130
|
+
def close(self):
|
|
131
|
+
"""
|
|
132
|
+
Ensure progress bar is stopped and cleaned up on handler close.
|
|
133
|
+
"""
|
|
134
|
+
if self.progress:
|
|
135
|
+
self.progress.stop()
|
|
136
|
+
if self.progress_task is not None:
|
|
137
|
+
self.progress.remove_task(self.progress_task)
|
|
138
|
+
self.progress_task = None
|
|
139
|
+
super().close()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
requires-python = ">=3.12"
|
|
3
|
+
|
|
4
|
+
[[package]]
|
|
5
|
+
name = "argparse-dataclass"
|
|
6
|
+
version = "2.0.0"
|
|
7
|
+
source = { registry = "https://pypi.org/simple" }
|
|
8
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1a/ff/a2e4e328075ddef2ac3c9431eb12247e4ba707a70324894f1e6b4f43c286/argparse_dataclass-2.0.0.tar.gz", hash = "sha256:09ab641c914a2f12882337b9c3e5086196dbf2ee6bf0ef67895c74002cc9297f", size = 6395 }
|
|
9
|
+
wheels = [
|
|
10
|
+
{ url = "https://files.pythonhosted.org/packages/b3/66/e6c0a808950ba5a4042e2fcedd577fc7401536c7db063de4d7c36be06f84/argparse_dataclass-2.0.0-py3-none-any.whl", hash = "sha256:3ffc8852a88d9d98d1364b4441a712491320afb91fb56049afd8a51d74bb52d2", size = 8762 },
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[[package]]
|
|
14
|
+
name = "configargparse"
|
|
15
|
+
version = "1.7"
|
|
16
|
+
source = { registry = "https://pypi.org/simple" }
|
|
17
|
+
sdist = { url = "https://files.pythonhosted.org/packages/70/8a/73f1008adfad01cb923255b924b1528727b8270e67cb4ef41eabdc7d783e/ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1", size = 43817 }
|
|
18
|
+
wheels = [
|
|
19
|
+
{ url = "https://files.pythonhosted.org/packages/6f/b3/b4ac838711fd74a2b4e6f746703cf9dd2cf5462d17dac07e349234e21b97/ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b", size = 25489 },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[[package]]
|
|
23
|
+
name = "markdown-it-py"
|
|
24
|
+
version = "3.0.0"
|
|
25
|
+
source = { registry = "https://pypi.org/simple" }
|
|
26
|
+
dependencies = [
|
|
27
|
+
{ name = "mdurl" },
|
|
28
|
+
]
|
|
29
|
+
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
|
30
|
+
wheels = [
|
|
31
|
+
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[[package]]
|
|
35
|
+
name = "mdurl"
|
|
36
|
+
version = "0.1.2"
|
|
37
|
+
source = { registry = "https://pypi.org/simple" }
|
|
38
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
|
39
|
+
wheels = [
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[[package]]
|
|
44
|
+
name = "pygments"
|
|
45
|
+
version = "2.18.0"
|
|
46
|
+
source = { registry = "https://pypi.org/simple" }
|
|
47
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
|
|
48
|
+
wheels = [
|
|
49
|
+
{ url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[[package]]
|
|
53
|
+
name = "rich"
|
|
54
|
+
version = "13.9.4"
|
|
55
|
+
source = { registry = "https://pypi.org/simple" }
|
|
56
|
+
dependencies = [
|
|
57
|
+
{ name = "markdown-it-py" },
|
|
58
|
+
{ name = "pygments" },
|
|
59
|
+
]
|
|
60
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
|
61
|
+
wheels = [
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[[package]]
|
|
66
|
+
name = "snakemake-interface-common"
|
|
67
|
+
version = "1.17.4"
|
|
68
|
+
source = { registry = "https://pypi.org/simple" }
|
|
69
|
+
dependencies = [
|
|
70
|
+
{ name = "argparse-dataclass" },
|
|
71
|
+
{ name = "configargparse" },
|
|
72
|
+
]
|
|
73
|
+
sdist = { url = "https://files.pythonhosted.org/packages/09/d4/dd2694226160c126e3ce2a27222088bde3ef07cd554d31d63398cc48f810/snakemake_interface_common-1.17.4.tar.gz", hash = "sha256:c2142e1b93cbc18c2cf41d15968ba8688f60b077c8284e5de057cccfc215d4d3", size = 9897 }
|
|
74
|
+
wheels = [
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/3d/fe/c318657e6a4b8ab5b3eafa07cd1c360a732c6b37ba6085f3c82339ebbbdc/snakemake_interface_common-1.17.4-py3-none-any.whl", hash = "sha256:1d757cce0300a73d48b906d1ade38706853169320a5d27b963869888d130c354", size = 14011 },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[[package]]
|
|
79
|
+
name = "snakemake-interface-executor-plugins"
|
|
80
|
+
version = "9.3.2"
|
|
81
|
+
source = { registry = "https://pypi.org/simple" }
|
|
82
|
+
dependencies = [
|
|
83
|
+
{ name = "argparse-dataclass" },
|
|
84
|
+
{ name = "snakemake-interface-common" },
|
|
85
|
+
{ name = "throttler" },
|
|
86
|
+
]
|
|
87
|
+
sdist = { url = "https://files.pythonhosted.org/packages/6f/77/f3e7a7abff8f15bd50f168eef1e3153167a681bfd51a454a3adb0918cb8d/snakemake_interface_executor_plugins-9.3.2.tar.gz", hash = "sha256:19c50dc82989ff25d10386cfb3c99da9d2dc980d95ecd30bbb431374dcd390b3", size = 16714 }
|
|
88
|
+
wheels = [
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/7c/84/0b7602c54d97b2cd3b40fc4c80633d42afcf69e441d9f25c928376051be8/snakemake_interface_executor_plugins-9.3.2-py3-none-any.whl", hash = "sha256:9c52c4b0f74b9056ebbb1b6229459281fef002b678baac00aee3b3ef36e92ba5", size = 22389 },
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[[package]]
|
|
93
|
+
name = "snakemake-interface-logger-plugins"
|
|
94
|
+
version = "0.1.0"
|
|
95
|
+
source = { registry = "https://pypi.org/simple" }
|
|
96
|
+
dependencies = [
|
|
97
|
+
{ name = "snakemake-interface-common" },
|
|
98
|
+
]
|
|
99
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e0/bf/3a2ec00e575f5d9937af6304f22695901dfb28141e6f14657baad98665b1/snakemake_interface_logger_plugins-0.1.0.tar.gz", hash = "sha256:87091a471cae9807a73e4b89983e9eed39db03427c448fe6c09b42b3d9cfed4d", size = 5906 }
|
|
100
|
+
wheels = [
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/46/2f/8cdfa7306c38861517eb2ecef198f85b32eb6dec9ecf53209efaf70a9b1b/snakemake_interface_logger_plugins-0.1.0-py3-none-any.whl", hash = "sha256:c18ed71726ff9f463a3b914ba130eab032ffcc6daca38c106fa0a9d5e9086dc1", size = 4706 },
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[[package]]
|
|
105
|
+
name = "snakemake-logger-plugin-rich"
|
|
106
|
+
version = "0.1.0"
|
|
107
|
+
source = { editable = "." }
|
|
108
|
+
dependencies = [
|
|
109
|
+
{ name = "rich" },
|
|
110
|
+
{ name = "snakemake-interface-executor-plugins" },
|
|
111
|
+
{ name = "snakemake-interface-logger-plugins" },
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
[package.metadata]
|
|
115
|
+
requires-dist = [
|
|
116
|
+
{ name = "rich", specifier = ">=13.9.4" },
|
|
117
|
+
{ name = "snakemake-interface-executor-plugins", specifier = ">=9.3.2" },
|
|
118
|
+
{ name = "snakemake-interface-logger-plugins", specifier = ">=0.1.0" },
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "throttler"
|
|
123
|
+
version = "1.2.2"
|
|
124
|
+
source = { registry = "https://pypi.org/simple" }
|
|
125
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b4/22/638451122136d5280bc477c8075ea448b9ebdfbd319f0f120edaecea2038/throttler-1.2.2.tar.gz", hash = "sha256:d54db406d98e1b54d18a9ba2b31ab9f093ac64a0a59d730c1cf7bb1cdfc94a58", size = 7970 }
|
|
126
|
+
wheels = [
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/df/d4/36bf6010b184286000b2334622bfb3446a40c22c1d2a9776bff025cb0fe5/throttler-1.2.2-py3-none-any.whl", hash = "sha256:fc6ae612a2529e01110b32335af40375258b98e3b81232ec77cd07f51bf71392", size = 7609 },
|
|
128
|
+
]
|