pysfi 0.1.5__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.
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: pysfi
3
+ Version: 0.1.5
4
+ Summary: Single File commands for Interactive python.
5
+ Requires-Python: >=3.8
6
+ Requires-Dist: tomli>=2.4.0; python_version < '3.11'
7
+ Description-Content-Type: text/markdown
8
+
9
+ # pysfi
10
+
11
+ Single File commands for Interactive python.
12
+
13
+ ## Overview
14
+
15
+ pysfi is a Python project that provides single-file command-line utilities, designed to be lightweight and easy-to-use.
16
+
17
+ ## Available Commands
18
+
19
+ - **alarmclk**: Alarm clock functionality
20
+ - **[bumpversion](sfi/bumpversion/README.md)**: Automated version number management tool
21
+ - **embedinstall**: Embed installation utilities
22
+ - **[filedate](sfi/filedate/README.md)**: A file date management tool that normalizes date prefixes in filenames
23
+ - **mkp**: Make Python project utilities
24
+ - **projectparse**: Project parsing and analysis tools
25
+ - **pyloadergen**: Python loader code generation
26
+ - **pypacker**: Python packaging utilities
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ # Install using uv (recommended)
32
+ uv add pysfi
33
+
34
+ # Or using pip
35
+ pip install pysfi
36
+ ```
37
+
38
+ ## Development
39
+
40
+ ### Requirements
41
+
42
+ - Python >= 3.8
43
+ - [uv](https://github.com/astral-sh/uv) (recommended) or pip
44
+
45
+ ### Development Dependencies
46
+
47
+ ```bash
48
+ uv pip install -e ".[dev]"
49
+ ```
50
+
51
+ ### Code Standards
52
+
53
+ The project uses Ruff for code linting and formatting:
54
+
55
+ ```bash
56
+ # Check code
57
+ ruff check .
58
+
59
+ # Format code
60
+ ruff format .
61
+ ```
62
+
63
+ ## Project Structure
64
+
65
+ ```bash
66
+ pysfi/
67
+ ├── pyproject.toml # Main project configuration
68
+ ├── README.md
69
+ └── sfi/
70
+ ├── __init__.py
71
+ ├── alarmclock/ # alarmclk command module
72
+ │ ├── alarmclock.py
73
+ │ ├── pyproject.toml
74
+ │ └── __init__.py
75
+ ├── embedinstall/ # embedinstall command module
76
+ │ ├── embedinstall.py
77
+ │ ├── pyproject.toml
78
+ │ └── __init__.py
79
+ ├── filedate/ # filedate command module
80
+ │ ├── filedate.py
81
+ │ ├── pyproject.toml
82
+ │ ├── README.md # Detailed documentation
83
+ │ └── __init__.py
84
+ ├── makepython/ # mkp command module
85
+ │ ├── makepython.py
86
+ │ ├── pyproject.toml
87
+ │ └── __init__.py
88
+ ├── projectparse/ # projectparse command module
89
+ │ ├── projectparse.py
90
+ │ ├── pyproject.toml
91
+ │ └── __init__.py
92
+ ├── pyloadergen/ # pyloadergen command module
93
+ │ ├── pyloadergen.py
94
+ │ ├── pyproject.toml
95
+ │ └── __init__.py
96
+ └── pypacker/ # pypacker command module
97
+ ├── fspacker.py
98
+ └── pyproject.toml
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT License
104
+
105
+ ## Contributing
106
+
107
+ Issues and Pull Requests are welcome!
@@ -0,0 +1,19 @@
1
+ sfi/__init__.py,sha256=3q6-yp52kPMW6F1LAzTb3-GlRA86Ga4qvIJg4iK-OEA,74
2
+ sfi/alarmclock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ sfi/alarmclock/alarmclock.py,sha256=65G8OyTGpe4oQ2SFerQG1N9PVJ4KxO7WzgsTxpGm4O0,12509
4
+ sfi/bumpversion/__init__.py,sha256=gnn2L3Q-mwVkX175c0tmFtonk-driIiVHB5LRP4Mt_4,85
5
+ sfi/bumpversion/bumpversion.py,sha256=HOyHLaE0sZajrlcVZ8hsim8mPjz77qwQVSo6aIzjMXE,20735
6
+ sfi/embedinstall/embedinstall.py,sha256=N5EbTDdX4bE3W0qHGAwAUuepqFr0sbdZuPI3KWrtuUY,14936
7
+ sfi/filedate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ sfi/filedate/filedate.py,sha256=DpVp26lumE_Lz_4TgqUEX8IxtK3Y6yHSEFV8qJyegyk,3645
9
+ sfi/makepython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ sfi/makepython/makepython.py,sha256=YKBXTjvugKN_TlFfx-vOudpkZy45VhO0vq89fxo6hFU,10583
11
+ sfi/projectparse/projectparse.py,sha256=Ojg-z4lZEtjEBpJYWyznTgL307N45AxlQKnRkEH0P70,5525
12
+ sfi/pyloadergen/pyloadergen.py,sha256=Erzz1PwrEQcDSCxXG-4DZ-CZavDt6MNv7k3nET5IB9U,32423
13
+ sfi/pypacker/fspacker.py,sha256=3tlS7qiWoH_kOzsp9eSWsQ-SY7-bSTugwfB-HIL69iE,3238
14
+ sfi/taskkill/taskkill.py,sha256=6Aw4afmgfLZcQnvgG_38A1VrwazDrnNdOmY1l4kr0lc,7758
15
+ sfi/which/which.py,sha256=zVIAwZA-pGGngxkkwZ6IxDX3ozVHg7cLSYwYO9FjaIc,2439
16
+ pysfi-0.1.5.dist-info/METADATA,sha256=yll-LofKX8YmpbsO2hbw2hPRRtxngGWlpUjjQwEJt5M,2755
17
+ pysfi-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ pysfi-0.1.5.dist-info/entry_points.txt,sha256=3wUSD_M7k2pUSq9VXvdYOJ41nUD2fMN0wati4QkDz9I,429
19
+ pysfi-0.1.5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,11 @@
1
+ [console_scripts]
2
+ alarmclk = sfi.alarmclock.alarmclock:main
3
+ bumpversion = sfi.bumpversion.bumpversion:main
4
+ embedinstall = sfi.embedinstall.embedinstall:main
5
+ filedate = sfi.filedate.filedate:main
6
+ mkp = sfi.makepython.makepython:main
7
+ projectparse = sfi.projectparse.projectparse:main
8
+ pyloadergen = sfi.pyloadergen.pyloadergen:main
9
+ pypacker = sfi.pypacker.pypacker:main
10
+ taskk = sfi.taskkill.taskkill:main
11
+ wch = sfi.which.which:main
sfi/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Single File commands for Interactive python."""
2
+
3
+ __version__ = "0.1.5"
File without changes
@@ -0,0 +1,367 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import logging
5
+ import random
6
+ import sys
7
+ from datetime import datetime, timedelta, timezone
8
+ from functools import partial
9
+ from typing import ClassVar
10
+
11
+ import qdarkstyle
12
+ from PySide2.QtCore import QSize, Qt, QTime, QTimer
13
+ from PySide2.QtGui import QCloseEvent
14
+ from PySide2.QtWidgets import (
15
+ QApplication,
16
+ QCheckBox,
17
+ QDialog,
18
+ QHBoxLayout,
19
+ QLabel,
20
+ QMainWindow,
21
+ QPushButton,
22
+ QTimeEdit,
23
+ QVBoxLayout,
24
+ QWidget,
25
+ )
26
+
27
+ __version__ = "0.1.2"
28
+ __build_date__ = "2025-09-16"
29
+
30
+
31
+ class AlarmClockConfig:
32
+ """Alarm clock configuration."""
33
+
34
+ ALARM_CLOCK_TITLE = "Digital Alarm Clock"
35
+
36
+ DIGITAL_FONT: str = "bold italic 81px 'Consolas'"
37
+ DIGITAL_COLOR: str = "#ccee00"
38
+ DIGITAL_BORDER_COLORS: ClassVar[list[str]] = [
39
+ "#00aa00",
40
+ "#eecc00",
41
+ "#aa00aa",
42
+ "#c0e0b0",
43
+ ]
44
+ DIGITAL_TIMER_FORMAT: str = "%H:%M:%S"
45
+ DIGITAL_UPDATE_INTERVAL: int = 1000
46
+
47
+ BLINK_TITLE: str = "Alarm Reminder!"
48
+ BLINK_CONTENT: str = "⏰ Time's Up!"
49
+ BLINK_TYPE: str = "color" # Options: 'color' or 'opacity'
50
+ BLINK_BG_COLORS: ClassVar[list[str]] = [
51
+ "#baf1ba",
52
+ "#f8ccc3",
53
+ "#aab4f0",
54
+ "#efaec0",
55
+ ]
56
+ BLINK_INTERVAL: ClassVar[int] = 300 # ms
57
+ DELAY_STEPS: ClassVar[list[int]] = [1, 5, 10, 15, 30, 60] # minutes
58
+
59
+
60
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
61
+
62
+ conf = AlarmClockConfig()
63
+ logger = logging.getLogger(__name__)
64
+
65
+
66
+ class DigitalClock(QLabel):
67
+ """Cool digital clock display."""
68
+
69
+ def __init__(self, parent: QWidget | None = None) -> None:
70
+ super().__init__(parent)
71
+
72
+ self.setAlignment(Qt.AlignCenter) # type: ignore
73
+
74
+ self._color = conf.DIGITAL_BORDER_COLORS[0]
75
+
76
+ # Timer to update current time
77
+ self._timer = QTimer()
78
+ self._timer.timeout.connect(self.update_time) # type: ignore
79
+ self._timer.start(conf.DIGITAL_UPDATE_INTERVAL) # Update every second
80
+
81
+ self.update_time()
82
+
83
+ def update_time(self) -> None:
84
+ """Update current time display."""
85
+ current = datetime.now(timezone.utc) + timedelta(hours=8) # Beijing time
86
+ self.setText(current.strftime(conf.DIGITAL_TIMER_FORMAT))
87
+ logger.debug(f"Updated time: {current}")
88
+
89
+ # Add blink effect
90
+ self._color = random.choice(
91
+ [_ for _ in conf.DIGITAL_BORDER_COLORS if _ != self._color],
92
+ )
93
+ self.setStyleSheet(f"""
94
+ font: {conf.DIGITAL_FONT};
95
+ color: {conf.DIGITAL_COLOR};
96
+ background-color: black;
97
+ border: 2px dashed {self._color};
98
+ border-radius: 10px;
99
+ padding: 10px;
100
+ """)
101
+
102
+
103
+ class BlinkDialog(QDialog):
104
+ """Alarm reminder dialog."""
105
+
106
+ def __init__(self) -> None:
107
+ super().__init__()
108
+
109
+ self.setWindowTitle(conf.BLINK_TITLE)
110
+ self.setModal(True)
111
+ self.setWindowFlags(
112
+ self.windowFlags() | Qt.WindowStaysOnTopHint | Qt.WindowType.Dialog, # type: ignore
113
+ )
114
+ self.setFixedSize(QSize(400, 240))
115
+
116
+ layout = QVBoxLayout()
117
+ msg_label = QLabel(conf.BLINK_CONTENT)
118
+ msg_label.setStyleSheet("""
119
+ color: red;
120
+ font-size: 24px;
121
+ """)
122
+ msg_label.setAlignment(Qt.AlignmentFlag.AlignCenter) # type: ignore
123
+
124
+ close_button = QPushButton("Close Alarm")
125
+ close_button.clicked.connect(self.accept) # type: ignore
126
+
127
+ layout.addWidget(msg_label)
128
+ layout.addWidget(close_button)
129
+ self.setLayout(layout)
130
+
131
+ # Prevent user from closing dialog by other means, ensure button click only
132
+ self.setWindowFlag(Qt.WindowCloseButtonHint, False) # type: ignore
133
+
134
+ # Blink control variables and timer
135
+ self.blink_timer = QTimer(self)
136
+ self.blink_timer.timeout.connect(self.update_blink) # type: ignore
137
+ self.blink_state = False
138
+ self.blink_type = conf.BLINK_TYPE
139
+
140
+ # Initialize style
141
+ self.bg_color = random.choice(conf.BLINK_BG_COLORS)
142
+ self.origin_style = self.styleSheet()
143
+ self.blink_timer.start(conf.BLINK_INTERVAL)
144
+
145
+ def update_blink(self) -> None:
146
+ """Timer timeout, update blink state."""
147
+ if self.blink_type == "color":
148
+ # Color blink logic
149
+ colors = [_ for _ in conf.BLINK_BG_COLORS[:] if _ != self.bg_color]
150
+ self.setStyleSheet(f"background-color: {random.choice(colors)}")
151
+ elif self.blink_type == "opacity":
152
+ # Opacity blink logic - Note: Some systems may not fully support window opacity
153
+ new_opacity = 0.3 if self.blink_state else 1.0
154
+ self.setWindowOpacity(new_opacity)
155
+
156
+ self.blink_state = not self.blink_state # Toggle state
157
+
158
+ def stop_blinking(self) -> None:
159
+ """Stop blinking, restore original style."""
160
+ self.blink_timer.stop()
161
+ self.setStyleSheet(self.origin_style) # Restore original style
162
+ self.setWindowOpacity(1.0) # Ensure opacity is restored
163
+
164
+ def closeEvent(self, event: QCloseEvent) -> None: # noqa: N802
165
+ """Override close event, ensure timer stops."""
166
+ self.stop_blinking()
167
+ super().closeEvent(event)
168
+
169
+
170
+ class AlarmClock(QMainWindow):
171
+ """Digital alarm clock GUI."""
172
+
173
+ def __init__(self) -> None:
174
+ super().__init__()
175
+ self.setWindowTitle(f"{conf.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
+
184
+ # Set window style
185
+ self.setStyleSheet("""
186
+ QMainWindow {
187
+ background-color: #2b2b2b;
188
+ }
189
+ QLabel {
190
+ color: #ffffff;
191
+ font-size: 14px;
192
+ }
193
+ QPushButton {
194
+ background-color: #3a3a3a;
195
+ color: white;
196
+ border: 1px solid #5a5a5a;
197
+ padding: 8px;
198
+ border-radius: 4px;
199
+ font-size: 14px;
200
+ }
201
+ QPushButton:hover {
202
+ background-color: #4a4a4a;
203
+ }
204
+ QPushButton:disabled {
205
+ background-color: #2a2a2a;
206
+ color: #6a6a6a;
207
+ }
208
+ QCheckBox {
209
+ color: white;
210
+ font-size: 14px;
211
+ }
212
+ QTimeEdit {
213
+ background-color: #3a3a3a;
214
+ color: white;
215
+ border: 1px solid #5a5a5a;
216
+ padding: 5px;
217
+ font-size: 14px;
218
+ }
219
+ """)
220
+
221
+ # Create central widget
222
+ central_widget = QWidget()
223
+ self.setCentralWidget(central_widget)
224
+
225
+ # Create layout
226
+ main_layout = QVBoxLayout()
227
+ main_layout.setSpacing(20)
228
+ central_widget.setLayout(main_layout)
229
+
230
+ # Cool digital clock display
231
+ self.digital_clock = DigitalClock(parent=self)
232
+ main_layout.addWidget(self.digital_clock)
233
+
234
+ # Alarm time setting
235
+ time_layout = QHBoxLayout()
236
+ time_label = QLabel("Alarm Time:")
237
+ time_label.setStyleSheet("color: white; font-size: 16px;")
238
+ self.alarm_time_edit = QTimeEdit()
239
+ self.alarm_time_edit.setDisplayFormat("HH:mm:ss")
240
+ self.alarm_time_edit.setTime(
241
+ QTime.currentTime().addSecs(conf.DELAY_STEPS[0] * 60),
242
+ )
243
+
244
+ time_layout.addWidget(time_label)
245
+ time_layout.addWidget(self.alarm_time_edit)
246
+ main_layout.addLayout(time_layout)
247
+
248
+ delay_layout = QHBoxLayout()
249
+ delay_label = QLabel("Delay (min):")
250
+ delay_label.setStyleSheet("color: white; font-size: 16px;")
251
+ delay_layout.addWidget(delay_label)
252
+ for minutes in conf.DELAY_STEPS:
253
+ button = QPushButton(str(minutes))
254
+ button.setStyleSheet("color: white; font-size: 16px;")
255
+ button.clicked.connect(partial(self.set_delay, minutes)) # type: ignore
256
+ delay_layout.addWidget(button)
257
+ main_layout.addLayout(delay_layout)
258
+
259
+ # Repeat option
260
+ self.repeat_checkbox = QCheckBox("Repeat")
261
+ main_layout.addWidget(self.repeat_checkbox)
262
+
263
+ # Control buttons
264
+ button_layout = QHBoxLayout()
265
+ self.set_alarm_button = QPushButton("Set Alarm")
266
+ self.set_alarm_button.clicked.connect(self.set_alarm) # type: ignore
267
+ self.cancel_alarm_button = QPushButton("Cancel Alarm")
268
+ self.cancel_alarm_button.clicked.connect(self.cancel_alarm) # type: ignore
269
+ self.cancel_alarm_button.setEnabled(False)
270
+ button_layout.addWidget(self.set_alarm_button)
271
+ button_layout.addWidget(self.cancel_alarm_button)
272
+ main_layout.addLayout(button_layout)
273
+
274
+ # Status display
275
+ self.status_label = QLabel("Alarm not set")
276
+ self.status_label.setAlignment(Qt.AlignCenter) # type: ignore
277
+ self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
278
+ main_layout.addWidget(self.status_label)
279
+
280
+ # Alarm timer
281
+ self.alarm_timer = QTimer()
282
+ self.alarm_timer.timeout.connect(self.check_alarm) # type: ignore
283
+
284
+ # Alarm state
285
+ self.alarm_set = False
286
+ self.alarm_time: QTime = QTime() # Explicit type
287
+
288
+ def set_delay(self, minutes: int) -> None:
289
+ """Set delay alarm."""
290
+ self.alarm_time_edit.setTime(
291
+ QTime.currentTime().addSecs(minutes * 60),
292
+ )
293
+
294
+ def set_alarm(self) -> None:
295
+ """Set alarm."""
296
+ self.alarm_time = self.alarm_time_edit.time()
297
+ self.alarm_set = True
298
+ self.alarm_timer.start(1000) # Check every second
299
+ self.set_alarm_button.setEnabled(False)
300
+ self.cancel_alarm_button.setEnabled(True)
301
+ self.status_label.setText(
302
+ f"Alarm set: {self.alarm_time.toString('HH:mm:ss')}",
303
+ )
304
+ self.status_label.setStyleSheet(
305
+ "color: #00ff00; font-size: 16px; font-weight: bold;",
306
+ )
307
+
308
+ def cancel_alarm(self) -> None:
309
+ """Cancel alarm."""
310
+ self.alarm_set = False
311
+ self.alarm_timer.stop()
312
+ self.set_alarm_button.setEnabled(True)
313
+ self.cancel_alarm_button.setEnabled(False)
314
+ self.status_label.setText("Alarm cancelled")
315
+ self.status_label.setStyleSheet("color: #aaaaaa; font-size: 16px;")
316
+
317
+ def check_alarm(self) -> None:
318
+ """Check if alarm time has arrived."""
319
+ if not self.alarm_set:
320
+ return
321
+
322
+ current_time = QTime.currentTime()
323
+ if (
324
+ current_time.hour() == self.alarm_time.hour()
325
+ and current_time.minute() == self.alarm_time.minute()
326
+ and current_time.second() == self.alarm_time.second()
327
+ ):
328
+ # Show reminder message
329
+ dialog = BlinkDialog()
330
+ dialog.exec_()
331
+
332
+ 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
+ self.status_label.setStyleSheet("""
339
+ color: #ff0000;
340
+ font-size: 18px;
341
+ font-weight: bold;
342
+ background-color: #330000;
343
+ border-radius: 5px;
344
+ padding: 5px;
345
+ """)
346
+
347
+ # Cancel alarm if not repeating
348
+ if not self.repeat_checkbox.isChecked():
349
+ self.cancel_alarm()
350
+
351
+
352
+ 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
+ app = QApplication(sys.argv)
364
+ app.setStyleSheet(qdarkstyle.load_stylesheet_pyside2())
365
+ window = AlarmClock()
366
+ window.show()
367
+ sys.exit(app.exec_())
@@ -0,0 +1,3 @@
1
+ """Bumpversion - Automated version number management tool."""
2
+
3
+ __version__ = "0.1.5"