pysfi 0.1.12__py3-none-any.whl → 0.1.14__py3-none-any.whl
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.
- {pysfi-0.1.12.dist-info → pysfi-0.1.14.dist-info}/METADATA +1 -1
- pysfi-0.1.14.dist-info/RECORD +68 -0
- {pysfi-0.1.12.dist-info → pysfi-0.1.14.dist-info}/entry_points.txt +3 -0
- sfi/__init__.py +19 -2
- sfi/alarmclock/__init__.py +3 -0
- sfi/alarmclock/alarmclock.py +23 -40
- sfi/bumpversion/__init__.py +3 -1
- sfi/bumpversion/bumpversion.py +64 -15
- sfi/cleanbuild/__init__.py +3 -0
- sfi/cleanbuild/cleanbuild.py +5 -1
- sfi/cli.py +25 -4
- sfi/condasetup/__init__.py +1 -0
- sfi/condasetup/condasetup.py +91 -76
- sfi/docdiff/__init__.py +1 -0
- sfi/docdiff/docdiff.py +3 -2
- sfi/docscan/__init__.py +1 -1
- sfi/docscan/docscan.py +78 -23
- sfi/docscan/docscan_gui.py +152 -48
- sfi/filedate/filedate.py +12 -5
- sfi/img2pdf/img2pdf.py +453 -0
- sfi/llmclient/llmclient.py +31 -8
- sfi/llmquantize/llmquantize.py +76 -37
- sfi/llmserver/__init__.py +1 -0
- sfi/llmserver/llmserver.py +63 -13
- sfi/makepython/makepython.py +1145 -201
- sfi/pdfsplit/pdfsplit.py +45 -12
- sfi/pyarchive/__init__.py +1 -0
- sfi/pyarchive/pyarchive.py +908 -278
- sfi/pyembedinstall/pyembedinstall.py +88 -89
- sfi/pylibpack/pylibpack.py +561 -463
- sfi/pyloadergen/pyloadergen.py +372 -218
- sfi/pypack/pypack.py +510 -959
- sfi/pyprojectparse/pyprojectparse.py +337 -40
- sfi/pysourcepack/__init__.py +1 -0
- sfi/pysourcepack/pysourcepack.py +210 -131
- sfi/quizbase/quizbase_gui.py +2 -2
- sfi/taskkill/taskkill.py +168 -59
- sfi/which/which.py +11 -3
- pysfi-0.1.12.dist-info/RECORD +0 -62
- sfi/workflowengine/workflowengine.py +0 -444
- {pysfi-0.1.12.dist-info → pysfi-0.1.14.dist-info}/WHEEL +0 -0
- /sfi/{workflowengine → img2pdf}/__init__.py +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
sfi/__init__.py,sha256=eGpIupcAG_5RqNCm908TwzDk-31Rhdn0iM-5Y2OwmIM,685
|
|
2
|
+
sfi/cli.py,sha256=o-sFO6I3X3DMO2I_Gzc9LziWBdm2ruHXpjbraXGzZi0,933
|
|
3
|
+
sfi/alarmclock/__init__.py,sha256=AUnUxITgFPYgSjn9ug2DEejrz5HDP56zZKYu-6JW1vY,72
|
|
4
|
+
sfi/alarmclock/alarmclock.py,sha256=ixVkbg548smUivRsqyI3YSZ81BWIrKawnuezAp3BzyE,11635
|
|
5
|
+
sfi/bumpversion/__init__.py,sha256=ExAxydqhwVCrA3YRid3JYult88HKsSTqYPt6996x0UU,122
|
|
6
|
+
sfi/bumpversion/bumpversion.py,sha256=Uxj0S8ZGmIRhfCx2ziWkVe5gHv74r68UPkiYzZcAwxo,21288
|
|
7
|
+
sfi/cleanbuild/__init__.py,sha256=_bXrBcQkHQTE2484-UMWAWz8kq-2Yjn9hleLcjF-pJg,98
|
|
8
|
+
sfi/cleanbuild/cleanbuild.py,sha256=Ilk4zBE48y6tUC3C5qp1pXdQ6CF0NZlra4ZbdiU5s2k,5270
|
|
9
|
+
sfi/condasetup/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
10
|
+
sfi/condasetup/condasetup.py,sha256=tuRzRi4NKmIfqN4Gq5w-VmY7BnleizPhAbYta8AzH4c,4976
|
|
11
|
+
sfi/docdiff/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
12
|
+
sfi/docdiff/docdiff.py,sha256=YR8e-gkbvjH0Bjt5fVVW0-8b7lbinQs6TRo4B5XgEjE,7783
|
|
13
|
+
sfi/docscan/__init__.py,sha256=eXv_iPeT_xOxv5lv4-axJQV28IISYYTqb6IRy3QTmIU,121
|
|
14
|
+
sfi/docscan/docscan.py,sha256=hXSOrfMikyg-Zsi_mpZvn31ea1J0un90ftwpRoXxNdA,42765
|
|
15
|
+
sfi/docscan/docscan_gui.py,sha256=KoXVHOQjUwtkxh5tI4V6mLBMl9PAP3Hp7zydJ_lppoQ,52345
|
|
16
|
+
sfi/docscan/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
sfi/docscan/lang/eng.py,sha256=GcOcT9FLcPZRdJ-MbLRYyf6vDweZTQBu_zUnEFzRY84,8529
|
|
18
|
+
sfi/docscan/lang/zhcn.py,sha256=1SZwQjZF3oi9FsnzuZB-9v7P64sGm5oNmVjuL-rhcEQ,8885
|
|
19
|
+
sfi/filedate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
sfi/filedate/filedate.py,sha256=eOR6LCwYosO6qyThPNeSCNImiU19UTQaqNWTkFKHAfs,6227
|
|
21
|
+
sfi/gittool/__init__.py,sha256=Xqxw7UUX-TKkWOCB1QHq8AdIKTkU7x87Xr-E0yVmObA,24
|
|
22
|
+
sfi/gittool/gittool.py,sha256=BBE6gm9qP1fAWLqKprmsf7bOFgDvBvia8_bMaXc7dR4,11960
|
|
23
|
+
sfi/img2pdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
sfi/img2pdf/img2pdf.py,sha256=Rx8N9ZG2MrbzfiGAaZGEFlNGuOpP_gwNY5MmOSGTv3w,15198
|
|
25
|
+
sfi/llmclient/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
sfi/llmclient/llmclient.py,sha256=zvaT-HEiL3CM3uEpvzuseLPEqFQe6RO4_fhnA2djHo0,22681
|
|
27
|
+
sfi/llmquantize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
+
sfi/llmquantize/llmquantize.py,sha256=crbZ4qnq6BAAkba_dBVYVhI9hhd9yBQ7XlNsWIrAes8,18888
|
|
29
|
+
sfi/llmserver/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
30
|
+
sfi/llmserver/llmserver.py,sha256=Fm4Go7wif4xMGomMFDsyJnYMafXsWemGkr-VfaeYa6w,13530
|
|
31
|
+
sfi/makepython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
sfi/makepython/makepython.py,sha256=GrzQTJyyB2PXugJK3yLxP1V5I_bSymW0qfkeqZvxRPA,43154
|
|
33
|
+
sfi/pdfsplit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
sfi/pdfsplit/pdfsplit.py,sha256=LOz7PuLlLBiXlFTjJej9G0UvJ03GZ9H-Gghy8ErvJzE,6576
|
|
35
|
+
sfi/pyarchive/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
36
|
+
sfi/pyarchive/pyarchive.py,sha256=OnPbIRA0C9JdeyNsVZ6rJg7ExItKyJz4jVw5W4c92DA,38293
|
|
37
|
+
sfi/pyembedinstall/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
sfi/pyembedinstall/pyembedinstall.py,sha256=LHnuvr63DXuntdS6a_7uQynOfarK-30WBUerSzawSHE,24171
|
|
39
|
+
sfi/pylibpack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
|
+
sfi/pylibpack/pylibpack.py,sha256=AJbMY4aHJq0YhyUXryBargNqCUTeSwaUuc3SiCcjcko,57685
|
|
41
|
+
sfi/pylibpack/rules/numpy.json,sha256=ee4gA5NBudFi3MaJA-QlBKQwiQAUb-eluF8HNVkl7Vk,384
|
|
42
|
+
sfi/pylibpack/rules/pymupdf.json,sha256=Hkzh8dvXKCzKx4aeHbu5E0qwgfbwQxZH2VLtQZzlMO4,153
|
|
43
|
+
sfi/pylibpack/rules/pyqt5.json,sha256=JKGnVSUMfXGR5XK1sbL1F6cAsEhl7hK12QkrulAB00M,374
|
|
44
|
+
sfi/pylibpack/rules/pyside2.json,sha256=uSSteT-3wDohWwQ36Z5mSOaSbxrR4565In4uZj_eR4w,557
|
|
45
|
+
sfi/pylibpack/rules/scipy.json,sha256=vTSi3W5BGWcwMkaDnyD6Yg7ijZdicPEUMw4fnRTnNf4,468
|
|
46
|
+
sfi/pylibpack/rules/shiboken2.json,sha256=9Pl3eslvergyjlyHNknkyN0oZlcH3049WULe5WjsmKM,515
|
|
47
|
+
sfi/pyloadergen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
sfi/pyloadergen/pyloadergen.py,sha256=VWJzc0opmMVthHE_RGbWeLe-DBaMn94gZo2yYLkf8cI,45696
|
|
49
|
+
sfi/pypack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
sfi/pypack/pypack.py,sha256=vDT_lVkq0eHeFCCR3pwjZGnF0EyQZDesM6ze--Tl194,22894
|
|
51
|
+
sfi/pyprojectparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
sfi/pyprojectparse/pyprojectparse.py,sha256=qBHj8l-_ONxD4VA5KvCfXdvS3fbjfOls9gRX83I0TDE,30641
|
|
53
|
+
sfi/pysourcepack/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
|
|
54
|
+
sfi/pysourcepack/pysourcepack.py,sha256=5_KrI2Y1TKKcoYfsFpTNXWguj6n8CKdca8lgoBCsL8k,12160
|
|
55
|
+
sfi/quizbase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
+
sfi/quizbase/quizbase.py,sha256=3tPUuYexZ9TVsNPPO_Itmr5OvyHSgY5OSUZwPoQt9zg,30605
|
|
57
|
+
sfi/quizbase/quizbase_gui.py,sha256=m_Lj3au1a8gEv5x7KOTjomiP1NpXHUgHSPE4lLv63hY,34733
|
|
58
|
+
sfi/regexvalidate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
+
sfi/regexvalidate/regex_help.html,sha256=3ltx3nh-Y5kkbHy5D67KfWtLig3u5XEhIlPHdHLEuTE,12436
|
|
60
|
+
sfi/regexvalidate/regexvalidate.py,sha256=5C_M2EKt9Jlonq03v9zrqtsFfAKK3D1vF1kBxD6iUpE,18600
|
|
61
|
+
sfi/taskkill/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
+
sfi/taskkill/taskkill.py,sha256=wM9g8sWJVTy4GxXe26rKdax2lIBI-uH9wP5wRenriH4,11606
|
|
63
|
+
sfi/which/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
+
sfi/which/which.py,sha256=2YbGgSiT1ySapKVV1ESoPf4P-JU8vvzmsZY39NiVr6k,2596
|
|
65
|
+
pysfi-0.1.14.dist-info/METADATA,sha256=1fFxs9DSKrmVtp4oKAD5HWOQa9qV9hvxKSDyLNPTaKI,4198
|
|
66
|
+
pysfi-0.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
67
|
+
pysfi-0.1.14.dist-info/entry_points.txt,sha256=iPXOCvVm9O0_qaqFnlFFlwklWsK7K4LygikeAhnkf2I,1259
|
|
68
|
+
pysfi-0.1.14.dist-info/RECORD,,
|
|
@@ -8,9 +8,11 @@ docscan = sfi.docscan.docscan:main
|
|
|
8
8
|
docscan-gui = sfi.docscan.docscan_gui:main
|
|
9
9
|
filedate = sfi.filedate.filedate:main
|
|
10
10
|
gitt = sfi.gittool.gittool:main
|
|
11
|
+
img2pdf = sfi.img2pdf.img2pdf:main
|
|
11
12
|
llmcli = sfi.llmclient.llmclient:main
|
|
12
13
|
llmqnt = sfi.llmquantize.llmquantize:main
|
|
13
14
|
llmsvr = sfi.llmserver.llmserver:main
|
|
15
|
+
makepython = sfi.makepython.makepython:main
|
|
14
16
|
mkp = sfi.makepython.makepython:main
|
|
15
17
|
pdfsplit = sfi.pdfsplit.pdfsplit:main
|
|
16
18
|
pyarchive = sfi.pyarchive.pyarchive:main
|
|
@@ -19,6 +21,7 @@ pylibpack = sfi.pylibpack.pylibpack:main
|
|
|
19
21
|
pyloadergen = sfi.pyloadergen.pyloadergen:main
|
|
20
22
|
pyp = sfi.pypack.pypack:main
|
|
21
23
|
pypack = sfi.pypack.pypack:main
|
|
24
|
+
pypp = sfi.pyprojectparse.pyprojectparse:main
|
|
22
25
|
pyprojectparse = sfi.pyprojectparse.pyprojectparse:main
|
|
23
26
|
pysourcepack = sfi.pysourcepack.pysourcepack:main
|
|
24
27
|
quizbase = sfi.quizbase.quizbase:main
|
sfi/__init__.py
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
"""Single File commands for Interactive python.
|
|
1
|
+
"""Single File commands for Interactive python.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
pysfi (Python Single File Interactive) is a collection of Python utility
|
|
4
|
+
tools designed to streamline development workflows and enhance productivity.
|
|
5
|
+
|
|
6
|
+
This package provides:
|
|
7
|
+
- Document processing utilities (docscan, docdiff, pdfsplit, img2pdf)
|
|
8
|
+
- Development tools (makepython, bumpversion, cleanbuild)
|
|
9
|
+
- System management utilities (filedate, taskkill, gittool)
|
|
10
|
+
- AI/LLM tools (llmclient, llmserver, llmquantize)
|
|
11
|
+
- And many more specialized utilities
|
|
12
|
+
|
|
13
|
+
Each module can be used independently as a command-line tool
|
|
14
|
+
or imported as a Python library.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
__version__ = "0.1.14"
|
|
20
|
+
__all__ = ["__version__"]
|
sfi/alarmclock/__init__.py
CHANGED
sfi/alarmclock/alarmclock.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import argparse
|
|
4
3
|
import logging
|
|
5
4
|
import random
|
|
6
5
|
import sys
|
|
@@ -8,7 +7,6 @@ from dataclasses import dataclass
|
|
|
8
7
|
from datetime import datetime, timedelta, timezone
|
|
9
8
|
from functools import partial
|
|
10
9
|
|
|
11
|
-
import qdarkstyle
|
|
12
10
|
from PySide2.QtCore import QSize, Qt, QTime, QTimer
|
|
13
11
|
from PySide2.QtGui import QCloseEvent
|
|
14
12
|
from PySide2.QtWidgets import (
|
|
@@ -28,7 +26,7 @@ __version__ = "0.1.3"
|
|
|
28
26
|
__build_date__ = "2026-01-22"
|
|
29
27
|
|
|
30
28
|
|
|
31
|
-
@dataclass
|
|
29
|
+
@dataclass
|
|
32
30
|
class AlarmClockConfig:
|
|
33
31
|
"""Configuration for the alarm clock application."""
|
|
34
32
|
|
|
@@ -87,9 +85,9 @@ class DigitalClock(QLabel):
|
|
|
87
85
|
logger.debug(f"Updated time: {current}")
|
|
88
86
|
|
|
89
87
|
# Add blink effect
|
|
90
|
-
self._color = random.choice(
|
|
91
|
-
|
|
92
|
-
)
|
|
88
|
+
self._color = random.choice([
|
|
89
|
+
c for c in config.DIGITAL_BORDER_COLORS if c != self._color
|
|
90
|
+
])
|
|
93
91
|
self.setStyleSheet(f"""
|
|
94
92
|
font: {config.DIGITAL_FONT};
|
|
95
93
|
color: {config.DIGITAL_COLOR};
|
|
@@ -112,6 +110,7 @@ class BlinkDialog(QDialog):
|
|
|
112
110
|
self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog,
|
|
113
111
|
)
|
|
114
112
|
self.setFixedSize(QSize(400, 240))
|
|
113
|
+
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
|
|
115
114
|
|
|
116
115
|
layout = QVBoxLayout()
|
|
117
116
|
msg_label = QLabel(config.BLINK_CONTENT)
|
|
@@ -122,32 +121,28 @@ class BlinkDialog(QDialog):
|
|
|
122
121
|
msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
123
122
|
|
|
124
123
|
close_button = QPushButton("Close Alarm")
|
|
125
|
-
close_button.clicked.connect(self.
|
|
124
|
+
close_button.clicked.connect(self.close_alarm)
|
|
126
125
|
|
|
127
126
|
layout.addWidget(msg_label)
|
|
128
127
|
layout.addWidget(close_button)
|
|
129
128
|
self.setLayout(layout)
|
|
130
129
|
|
|
131
|
-
# Prevent user from closing dialog by other means, ensure button click only
|
|
132
|
-
self.setWindowFlag(Qt.WindowCloseButtonHint, False)
|
|
133
|
-
|
|
134
130
|
# Blink control variables and timer
|
|
135
131
|
self.blink_timer = QTimer(self)
|
|
136
132
|
self.blink_timer.timeout.connect(self.update_blink)
|
|
137
133
|
self.blink_state = False
|
|
138
134
|
self.blink_type = config.BLINK_TYPE
|
|
139
|
-
|
|
140
|
-
# Initialize style
|
|
141
135
|
self.bg_color = random.choice(config.BLINK_BG_COLORS)
|
|
142
|
-
self.origin_style = self.styleSheet()
|
|
143
136
|
self.blink_timer.start(config.BLINK_INTERVAL)
|
|
144
137
|
|
|
145
138
|
def update_blink(self) -> None:
|
|
146
139
|
"""Timer timeout, update blink state."""
|
|
147
140
|
if self.blink_type == "color":
|
|
148
141
|
# Color blink logic
|
|
149
|
-
colors = [
|
|
150
|
-
|
|
142
|
+
colors = [c for c in config.BLINK_BG_COLORS if c != self.bg_color]
|
|
143
|
+
new_color = random.choice(colors)
|
|
144
|
+
self.setStyleSheet(f"background-color: {new_color}")
|
|
145
|
+
self.bg_color = new_color
|
|
151
146
|
elif self.blink_type == "opacity":
|
|
152
147
|
# Opacity blink logic - Note: Some systems may not fully support window opacity
|
|
153
148
|
new_opacity = 0.3 if self.blink_state else 1.0
|
|
@@ -155,10 +150,14 @@ class BlinkDialog(QDialog):
|
|
|
155
150
|
|
|
156
151
|
self.blink_state = not self.blink_state # Toggle state
|
|
157
152
|
|
|
153
|
+
def close_alarm(self) -> None:
|
|
154
|
+
"""Close alarm dialog and stop blinking."""
|
|
155
|
+
self.stop_blinking()
|
|
156
|
+
self.accept()
|
|
157
|
+
|
|
158
158
|
def stop_blinking(self) -> None:
|
|
159
|
-
"""Stop blinking
|
|
159
|
+
"""Stop blinking."""
|
|
160
160
|
self.blink_timer.stop()
|
|
161
|
-
self.setStyleSheet(self.origin_style) # Restore original style
|
|
162
161
|
self.setWindowOpacity(1.0) # Ensure opacity is restored
|
|
163
162
|
|
|
164
163
|
def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802
|
|
@@ -173,13 +172,6 @@ class AlarmClock(QMainWindow):
|
|
|
173
172
|
def __init__(self) -> None:
|
|
174
173
|
super().__init__()
|
|
175
174
|
self.setWindowTitle(f"{config.ALARM_CLOCK_TITLE} v{__version__}")
|
|
176
|
-
self.setGeometry(
|
|
177
|
-
QApplication.desktop().screenGeometry().center().x() - self.width() // 4,
|
|
178
|
-
QApplication.desktop().screenGeometry().center().y() - self.height() // 2,
|
|
179
|
-
self.width(),
|
|
180
|
-
self.height(),
|
|
181
|
-
)
|
|
182
|
-
self.adjustSize()
|
|
183
175
|
|
|
184
176
|
# Set window style
|
|
185
177
|
self.setStyleSheet("""
|
|
@@ -277,6 +269,13 @@ class AlarmClock(QMainWindow):
|
|
|
277
269
|
self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
|
|
278
270
|
main_layout.addWidget(self.status_label)
|
|
279
271
|
|
|
272
|
+
# Center window on screen
|
|
273
|
+
self.adjustSize()
|
|
274
|
+
screen = QApplication.desktop().screenGeometry()
|
|
275
|
+
x = (screen.width() - self.width()) // 2
|
|
276
|
+
y = (screen.height() - self.height()) // 2
|
|
277
|
+
self.move(x, y)
|
|
278
|
+
|
|
280
279
|
# Alarm timer
|
|
281
280
|
self.alarm_timer = QTimer()
|
|
282
281
|
self.alarm_timer.timeout.connect(self.check_alarm)
|
|
@@ -330,11 +329,6 @@ class AlarmClock(QMainWindow):
|
|
|
330
329
|
dialog.exec_()
|
|
331
330
|
|
|
332
331
|
self.status_label.setText("⏰ Alarm Rang! ⏰")
|
|
333
|
-
self.status_label.setStyleSheet(
|
|
334
|
-
"color: #ff5555; font-size: 18px; font-weight: bold;",
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
# Add blink effect
|
|
338
332
|
self.status_label.setStyleSheet("""
|
|
339
333
|
color: #ff0000;
|
|
340
334
|
font-size: 18px;
|
|
@@ -350,18 +344,7 @@ class AlarmClock(QMainWindow):
|
|
|
350
344
|
|
|
351
345
|
|
|
352
346
|
def main() -> None:
|
|
353
|
-
parser = argparse.ArgumentParser(
|
|
354
|
-
prog="alarmclock", description="Digital Alarm Clock"
|
|
355
|
-
)
|
|
356
|
-
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode")
|
|
357
|
-
parser.add_argument("-v", "--version", action="version", version=f"{__version__}")
|
|
358
|
-
|
|
359
|
-
args = parser.parse_args()
|
|
360
|
-
if args.debug:
|
|
361
|
-
logger.setLevel(logging.DEBUG)
|
|
362
|
-
|
|
363
347
|
app = QApplication(sys.argv)
|
|
364
|
-
app.setStyleSheet(qdarkstyle.load_stylesheet_pyside2())
|
|
365
348
|
window = AlarmClock()
|
|
366
349
|
window.show()
|
|
367
350
|
sys.exit(app.exec_())
|
sfi/bumpversion/__init__.py
CHANGED
sfi/bumpversion/bumpversion.py
CHANGED
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from enum import Enum
|
|
13
13
|
from pathlib import Path
|
|
14
|
+
from re import Pattern
|
|
14
15
|
from typing import Final
|
|
15
16
|
|
|
16
17
|
try:
|
|
@@ -21,7 +22,8 @@ except ImportError:
|
|
|
21
22
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
# Semantic versioning regex pattern
|
|
26
|
+
VERSION_PATTERN: Final[Pattern[str]] = re.compile(
|
|
25
27
|
r"(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?"
|
|
26
28
|
)
|
|
27
29
|
|
|
@@ -69,7 +71,12 @@ class Version:
|
|
|
69
71
|
buildmetadata=match.group("buildmetadata"),
|
|
70
72
|
)
|
|
71
73
|
|
|
72
|
-
def bump(
|
|
74
|
+
def bump(
|
|
75
|
+
self,
|
|
76
|
+
part: VersionPart,
|
|
77
|
+
reset_prerelease: bool = True,
|
|
78
|
+
prerelease: str | None = None,
|
|
79
|
+
) -> Version:
|
|
73
80
|
"""Return a new version with specified part bumped."""
|
|
74
81
|
# Determine the new prerelease value
|
|
75
82
|
new_prerelease = None
|
|
@@ -121,7 +128,9 @@ class FileParser:
|
|
|
121
128
|
"""Parser for different file formats to extract and update version numbers."""
|
|
122
129
|
|
|
123
130
|
@staticmethod
|
|
124
|
-
def _write_with_original_line_ending(
|
|
131
|
+
def _write_with_original_line_ending(
|
|
132
|
+
file_path: Path, content: str, original_content: str
|
|
133
|
+
) -> None:
|
|
125
134
|
"""Write content to file while preserving original line ending style.
|
|
126
135
|
|
|
127
136
|
Args:
|
|
@@ -167,7 +176,9 @@ class FileParser:
|
|
|
167
176
|
new_content = re.sub(pattern, new_version_str, content)
|
|
168
177
|
|
|
169
178
|
# Write back preserving original line endings
|
|
170
|
-
FileParser._write_with_original_line_ending(
|
|
179
|
+
FileParser._write_with_original_line_ending(
|
|
180
|
+
file_path, new_content, original_bytes.decode("utf-8")
|
|
181
|
+
)
|
|
171
182
|
|
|
172
183
|
@staticmethod
|
|
173
184
|
def parse_init_py(file_path: Path) -> tuple[Version, list[str]]:
|
|
@@ -285,7 +296,9 @@ class BumpversionManager:
|
|
|
285
296
|
if projects_file.exists():
|
|
286
297
|
with projects_file.open("r", encoding="utf-8") as f:
|
|
287
298
|
projects_data = json.load(f)
|
|
288
|
-
self.subproject_paths = {
|
|
299
|
+
self.subproject_paths = {
|
|
300
|
+
self.root_path / p for p in projects_data.get("subprojects", [])
|
|
301
|
+
}
|
|
289
302
|
except Exception:
|
|
290
303
|
pass
|
|
291
304
|
|
|
@@ -313,10 +326,17 @@ class BumpversionManager:
|
|
|
313
326
|
# Filter out unwanted directories
|
|
314
327
|
excluded_dirs = {"__pycache__", ".venv", "venv", "env", ".git", "dist", "build"}
|
|
315
328
|
init_files = [
|
|
316
|
-
f
|
|
329
|
+
f
|
|
330
|
+
for f in init_files
|
|
331
|
+
if not any(part in excluded_dirs for part in f.parts)
|
|
332
|
+
and "tests" not in f.parts
|
|
317
333
|
]
|
|
318
334
|
# Exclude files inside subprojects defined in projects.json
|
|
319
|
-
init_files = [
|
|
335
|
+
init_files = [
|
|
336
|
+
f
|
|
337
|
+
for f in init_files
|
|
338
|
+
if not any(str(f).startswith(str(sp)) for sp in self.subproject_paths)
|
|
339
|
+
]
|
|
320
340
|
detected_files.extend(init_files)
|
|
321
341
|
for f in init_files:
|
|
322
342
|
logger.info(f"Found: {f}")
|
|
@@ -442,7 +462,9 @@ class BumpversionManager:
|
|
|
442
462
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
443
463
|
return False
|
|
444
464
|
|
|
445
|
-
def _git_commit(
|
|
465
|
+
def _git_commit(
|
|
466
|
+
self, version: Version, message: str | None, files: list[Path]
|
|
467
|
+
) -> None:
|
|
446
468
|
"""Commit version changes to git."""
|
|
447
469
|
commit_message = message or f"chore: bump version to {version}"
|
|
448
470
|
|
|
@@ -458,7 +480,9 @@ class BumpversionManager:
|
|
|
458
480
|
tag_name = f"v{version}"
|
|
459
481
|
|
|
460
482
|
try:
|
|
461
|
-
subprocess.run(
|
|
483
|
+
subprocess.run(
|
|
484
|
+
["git", "tag", "-a", tag_name, "-m", f"Version {version}"], check=True
|
|
485
|
+
)
|
|
462
486
|
logger.info(f"Git tag created: {tag_name}")
|
|
463
487
|
except subprocess.CalledProcessError as e:
|
|
464
488
|
logger.error(f"Git tag failed: {e}")
|
|
@@ -466,14 +490,39 @@ class BumpversionManager:
|
|
|
466
490
|
|
|
467
491
|
def main() -> None:
|
|
468
492
|
"""Main entry point for bumpversion command."""
|
|
469
|
-
parser = argparse.ArgumentParser(
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
parser.add_argument(
|
|
473
|
-
|
|
493
|
+
parser = argparse.ArgumentParser(
|
|
494
|
+
prog="bumpversion", description="Automated version number management tool"
|
|
495
|
+
)
|
|
496
|
+
parser.add_argument(
|
|
497
|
+
"part",
|
|
498
|
+
type=str,
|
|
499
|
+
choices=["major", "minor", "patch"],
|
|
500
|
+
help="Version part to bump",
|
|
501
|
+
)
|
|
502
|
+
parser.add_argument(
|
|
503
|
+
"--files",
|
|
504
|
+
"-f",
|
|
505
|
+
type=str,
|
|
506
|
+
nargs="*",
|
|
507
|
+
help="Specific files to update (default: auto-detect)",
|
|
508
|
+
)
|
|
509
|
+
parser.add_argument(
|
|
510
|
+
"--prerelease",
|
|
511
|
+
"-p",
|
|
512
|
+
type=str,
|
|
513
|
+
help="Set prerelease tag (e.g., alpha, beta, rc1)",
|
|
514
|
+
)
|
|
515
|
+
parser.add_argument(
|
|
516
|
+
"--commit", "-c", action="store_true", help="Commit changes to git"
|
|
517
|
+
)
|
|
474
518
|
parser.add_argument("--tag", "-t", action="store_true", help="Create git tag")
|
|
475
519
|
parser.add_argument("--message", "-m", type=str, help="Custom commit message")
|
|
476
|
-
parser.add_argument(
|
|
520
|
+
parser.add_argument(
|
|
521
|
+
"--dry-run",
|
|
522
|
+
"-n",
|
|
523
|
+
action="store_true",
|
|
524
|
+
help="Show what would be done without making changes",
|
|
525
|
+
)
|
|
477
526
|
parser.add_argument("--debug", "-d", action="store_true", help="Enable debug mode")
|
|
478
527
|
args = parser.parse_args()
|
|
479
528
|
|
sfi/cleanbuild/cleanbuild.py
CHANGED
|
@@ -13,6 +13,10 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
13
13
|
from dataclasses import dataclass, field
|
|
14
14
|
from functools import cached_property
|
|
15
15
|
from pathlib import Path
|
|
16
|
+
from typing import Final
|
|
17
|
+
|
|
18
|
+
# Folders to clean
|
|
19
|
+
_BUILD_FOLDERS: Final[tuple[str, ...]] = ("build", "dist", ".venv", "node_modules")
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
|
@@ -20,7 +24,7 @@ logger = logging.getLogger(__name__)
|
|
|
20
24
|
|
|
21
25
|
CWD = Path.cwd()
|
|
22
26
|
PROJECT_ROOT = Path(__file__).parent.parent.parent
|
|
23
|
-
|
|
27
|
+
|
|
24
28
|
|
|
25
29
|
@dataclass(frozen=True)
|
|
26
30
|
class SearchResult:
|
sfi/cli.py
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
2
4
|
|
|
3
|
-
from sfi import __version__ as VERSION
|
|
5
|
+
from sfi import __version__ as VERSION
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
"""Main entry point for the pysfi CLI tool.
|
|
4
10
|
|
|
11
|
+
Parses command line arguments and handles version printing.
|
|
5
12
|
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
This is the central entry point for all pysfi subcommands.
|
|
14
|
+
Currently supports:
|
|
15
|
+
- Version information display
|
|
16
|
+
|
|
17
|
+
Future enhancements will include routing to specific
|
|
18
|
+
module commands based on subcommands.
|
|
19
|
+
"""
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="pysfi",
|
|
22
|
+
description="Python Single File Interactive - A collection of Python utility tools",
|
|
23
|
+
epilog="Use 'pysfi <command> --help' for more information on a specific command",
|
|
24
|
+
)
|
|
8
25
|
parser.add_argument(
|
|
9
|
-
"-v",
|
|
26
|
+
"-v",
|
|
27
|
+
"--version",
|
|
28
|
+
action="version",
|
|
29
|
+
version=f"%(prog)s v{VERSION}",
|
|
30
|
+
help="Print version and exit",
|
|
10
31
|
)
|
|
11
32
|
parser.parse_args()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|