pysfi 0.1.7__py3-none-any.whl → 0.1.11__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.7.dist-info → pysfi-0.1.11.dist-info}/METADATA +11 -9
- pysfi-0.1.11.dist-info/RECORD +60 -0
- pysfi-0.1.11.dist-info/entry_points.txt +28 -0
- sfi/__init__.py +1 -1
- sfi/alarmclock/alarmclock.py +40 -40
- sfi/bumpversion/__init__.py +1 -1
- sfi/cleanbuild/cleanbuild.py +155 -0
- sfi/condasetup/condasetup.py +116 -0
- sfi/docscan/__init__.py +1 -1
- sfi/docscan/docscan.py +407 -103
- sfi/docscan/docscan_gui.py +1282 -596
- sfi/docscan/lang/eng.py +152 -0
- sfi/docscan/lang/zhcn.py +170 -0
- sfi/filedate/filedate.py +185 -112
- sfi/gittool/__init__.py +2 -0
- sfi/gittool/gittool.py +401 -0
- sfi/llmclient/llmclient.py +592 -0
- sfi/llmquantize/llmquantize.py +480 -0
- sfi/llmserver/llmserver.py +335 -0
- sfi/makepython/makepython.py +31 -30
- sfi/pdfsplit/pdfsplit.py +173 -173
- sfi/pyarchive/pyarchive.py +418 -0
- sfi/pyembedinstall/pyembedinstall.py +629 -0
- sfi/pylibpack/__init__.py +0 -0
- sfi/pylibpack/pylibpack.py +1457 -0
- sfi/pylibpack/rules/numpy.json +22 -0
- sfi/pylibpack/rules/pymupdf.json +10 -0
- sfi/pylibpack/rules/pyqt5.json +19 -0
- sfi/pylibpack/rules/pyside2.json +23 -0
- sfi/pylibpack/rules/scipy.json +23 -0
- sfi/pylibpack/rules/shiboken2.json +24 -0
- sfi/pyloadergen/pyloadergen.py +512 -227
- sfi/pypack/__init__.py +0 -0
- sfi/pypack/pypack.py +1142 -0
- sfi/pyprojectparse/__init__.py +0 -0
- sfi/pyprojectparse/pyprojectparse.py +500 -0
- sfi/pysourcepack/pysourcepack.py +308 -0
- sfi/quizbase/__init__.py +0 -0
- sfi/quizbase/quizbase.py +828 -0
- sfi/quizbase/quizbase_gui.py +987 -0
- sfi/regexvalidate/__init__.py +0 -0
- sfi/regexvalidate/regex_help.html +284 -0
- sfi/regexvalidate/regexvalidate.py +468 -0
- sfi/taskkill/taskkill.py +0 -2
- sfi/workflowengine/__init__.py +0 -0
- sfi/workflowengine/workflowengine.py +444 -0
- pysfi-0.1.7.dist-info/RECORD +0 -31
- pysfi-0.1.7.dist-info/entry_points.txt +0 -15
- sfi/embedinstall/embedinstall.py +0 -418
- sfi/projectparse/projectparse.py +0 -152
- sfi/pypacker/fspacker.py +0 -91
- {pysfi-0.1.7.dist-info → pysfi-0.1.11.dist-info}/WHEEL +0 -0
- /sfi/{embedinstall → docscan/lang}/__init__.py +0 -0
- /sfi/{projectparse → llmquantize}/__init__.py +0 -0
- /sfi/{pypacker → pyembedinstall}/__init__.py +0 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import atexit
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import pathlib
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from types import SimpleNamespace
|
|
11
|
+
from typing import ClassVar
|
|
12
|
+
|
|
13
|
+
from PySide2.QtCore import QProcess, QTextStream, QUrl
|
|
14
|
+
from PySide2.QtGui import (
|
|
15
|
+
QBrush,
|
|
16
|
+
QColor,
|
|
17
|
+
QDesktopServices,
|
|
18
|
+
QFont,
|
|
19
|
+
QMoveEvent,
|
|
20
|
+
QResizeEvent,
|
|
21
|
+
QTextCharFormat,
|
|
22
|
+
QTextCursor,
|
|
23
|
+
)
|
|
24
|
+
from PySide2.QtWidgets import (
|
|
25
|
+
QApplication,
|
|
26
|
+
QFileDialog,
|
|
27
|
+
QGroupBox,
|
|
28
|
+
QHBoxLayout,
|
|
29
|
+
QLabel,
|
|
30
|
+
QLineEdit,
|
|
31
|
+
QMainWindow,
|
|
32
|
+
QPushButton,
|
|
33
|
+
QSpinBox,
|
|
34
|
+
QTextEdit,
|
|
35
|
+
QVBoxLayout,
|
|
36
|
+
QWidget,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
CONFIG_FILE = Path.home() / ".sfi" / "llmserver.json"
|
|
40
|
+
logging.basicConfig(level="INFO", format="%(message)s")
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LLMServerConfig(SimpleNamespace):
|
|
45
|
+
"""Llama local model server configuration."""
|
|
46
|
+
|
|
47
|
+
TITLE: str = "Llama Local Model Server"
|
|
48
|
+
WIN_SIZE: ClassVar[list[int]] = [800, 800]
|
|
49
|
+
WIN_POS: ClassVar[list[int]] = [200, 200]
|
|
50
|
+
MODEL_PATH: str = ""
|
|
51
|
+
|
|
52
|
+
URL: str = "http://127.0.0.1"
|
|
53
|
+
LISTEN_PORT: int = 8080
|
|
54
|
+
LISTEN_PORT_RNG: ClassVar[list[int]] = [1024, 65535]
|
|
55
|
+
THREAD_COUNT_RNG: ClassVar[list[int]] = [1, 24]
|
|
56
|
+
THREAD_COUNT: int = 4
|
|
57
|
+
|
|
58
|
+
def __init__(self) -> None:
|
|
59
|
+
if CONFIG_FILE.exists():
|
|
60
|
+
logger.info("Loading configuration from %s", CONFIG_FILE)
|
|
61
|
+
self.__dict__.update(json.loads(CONFIG_FILE.read_text()))
|
|
62
|
+
else:
|
|
63
|
+
logger.info("Using default configuration")
|
|
64
|
+
|
|
65
|
+
def save(self) -> None:
|
|
66
|
+
"""Save configuration."""
|
|
67
|
+
CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
CONFIG_FILE.write_text(json.dumps(vars(self), indent=4))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
conf = LLMServerConfig()
|
|
72
|
+
atexit.register(conf.save)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LlamaServerGUI(QMainWindow):
|
|
76
|
+
"""Llama local model server GUI."""
|
|
77
|
+
|
|
78
|
+
# Color constants
|
|
79
|
+
COLOR_ERROR = QColor(255, 0, 0)
|
|
80
|
+
COLOR_WARNING = QColor(255, 165, 0)
|
|
81
|
+
COLOR_INFO = QColor(0, 0, 0)
|
|
82
|
+
|
|
83
|
+
# Process constants
|
|
84
|
+
PROCESS_TERMINATE_TIMEOUT_MS = 2000
|
|
85
|
+
|
|
86
|
+
def __init__(self) -> None:
|
|
87
|
+
super().__init__()
|
|
88
|
+
self.setWindowTitle(conf.TITLE)
|
|
89
|
+
self.setGeometry(*conf.WIN_POS, *conf.WIN_SIZE)
|
|
90
|
+
|
|
91
|
+
self.process: QProcess
|
|
92
|
+
self.init_ui()
|
|
93
|
+
self.setup_process()
|
|
94
|
+
|
|
95
|
+
model_path = conf.MODEL_PATH
|
|
96
|
+
if model_path:
|
|
97
|
+
self.model_path_input.setText(str(model_path))
|
|
98
|
+
else:
|
|
99
|
+
self.model_path_input.setPlaceholderText("Choose model file...")
|
|
100
|
+
|
|
101
|
+
def init_ui(self) -> None:
|
|
102
|
+
"""Initialize user interface."""
|
|
103
|
+
# Main layout
|
|
104
|
+
main_widget = QWidget()
|
|
105
|
+
main_layout = QVBoxLayout() # type: ignore
|
|
106
|
+
|
|
107
|
+
# Configuration panel
|
|
108
|
+
config_group = QGroupBox("Server Configuration")
|
|
109
|
+
config_layout = QVBoxLayout(config_group)
|
|
110
|
+
|
|
111
|
+
# Model path selection
|
|
112
|
+
model_path_layout = QHBoxLayout() # type: ignore
|
|
113
|
+
model_path_layout.addWidget(QLabel("Model Path:"))
|
|
114
|
+
self.model_path_input = QLineEdit()
|
|
115
|
+
|
|
116
|
+
model_path_layout.addWidget(self.model_path_input)
|
|
117
|
+
self.load_model_btn = QPushButton("Browse...")
|
|
118
|
+
self.load_model_btn.clicked.connect(self.on_load_model) # type: ignore
|
|
119
|
+
model_path_layout.addWidget(self.load_model_btn)
|
|
120
|
+
config_layout.addLayout(model_path_layout)
|
|
121
|
+
|
|
122
|
+
# Server parameters
|
|
123
|
+
params_layout = QHBoxLayout() # type: ignore
|
|
124
|
+
params_layout.addStretch(1)
|
|
125
|
+
params_layout.addWidget(QLabel("Port:"))
|
|
126
|
+
self.port_spin = QSpinBox()
|
|
127
|
+
self.port_spin.setRange(*conf.LISTEN_PORT_RNG)
|
|
128
|
+
self.port_spin.setValue(conf.LISTEN_PORT)
|
|
129
|
+
params_layout.addWidget(self.port_spin)
|
|
130
|
+
self.port_spin.valueChanged.connect(self.on_config_changed) # type: ignore
|
|
131
|
+
|
|
132
|
+
params_layout.addWidget(QLabel("Threads:"))
|
|
133
|
+
self.threads_spin = QSpinBox()
|
|
134
|
+
self.threads_spin.setRange(*conf.THREAD_COUNT_RNG)
|
|
135
|
+
self.threads_spin.setValue(conf.THREAD_COUNT)
|
|
136
|
+
params_layout.addWidget(self.threads_spin)
|
|
137
|
+
config_layout.addLayout(params_layout)
|
|
138
|
+
self.threads_spin.valueChanged.connect(self.on_config_changed) # type: ignore
|
|
139
|
+
|
|
140
|
+
config_group.setLayout(config_layout)
|
|
141
|
+
main_layout.addWidget(config_group)
|
|
142
|
+
|
|
143
|
+
# Control buttons
|
|
144
|
+
control_layout = QHBoxLayout() # type: ignore
|
|
145
|
+
self.start_btn = QPushButton("Start Server")
|
|
146
|
+
self.start_btn.clicked.connect(self.toggle_server) # type: ignore
|
|
147
|
+
self.browser_btn = QPushButton("Start Browser")
|
|
148
|
+
self.browser_btn.setEnabled(False)
|
|
149
|
+
self.browser_btn.clicked.connect(self.on_start_browser) # type: ignore
|
|
150
|
+
control_layout.addWidget(self.start_btn)
|
|
151
|
+
control_layout.addWidget(self.browser_btn)
|
|
152
|
+
main_layout.addLayout(control_layout)
|
|
153
|
+
|
|
154
|
+
# Output display
|
|
155
|
+
output_group = QGroupBox("Server Output")
|
|
156
|
+
output_layout = QVBoxLayout(output_group)
|
|
157
|
+
self.output_area = QTextEdit("")
|
|
158
|
+
self.output_area.setReadOnly(True)
|
|
159
|
+
self.output_area.setLineWrapMode(QTextEdit.NoWrap) # type: ignore
|
|
160
|
+
|
|
161
|
+
# Set colors for different message types
|
|
162
|
+
self.error_format = self.create_text_format(self.COLOR_ERROR)
|
|
163
|
+
self.warning_format = self.create_text_format(self.COLOR_WARNING)
|
|
164
|
+
self.info_format = self.create_text_format(self.COLOR_INFO)
|
|
165
|
+
|
|
166
|
+
output_layout.addWidget(self.output_area)
|
|
167
|
+
output_group.setLayout(output_layout)
|
|
168
|
+
main_layout.addWidget(output_group)
|
|
169
|
+
|
|
170
|
+
main_widget.setLayout(main_layout)
|
|
171
|
+
self.setCentralWidget(main_widget)
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def create_text_format(color: QColor) -> QTextCharFormat:
|
|
175
|
+
"""Create text format.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
color: Text color.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Text format.
|
|
182
|
+
"""
|
|
183
|
+
text_format = QTextCharFormat()
|
|
184
|
+
text_format.setForeground(QBrush(color))
|
|
185
|
+
return text_format
|
|
186
|
+
|
|
187
|
+
def setup_process(self) -> None:
|
|
188
|
+
"""Initialize process."""
|
|
189
|
+
self.process = QProcess(self)
|
|
190
|
+
self.process.readyReadStandardOutput.connect(self.handle_stdout) # type: ignore
|
|
191
|
+
self.process.readyReadStandardError.connect(self.handle_stderr) # type: ignore
|
|
192
|
+
self.process.finished.connect(self.on_process_finished) # type: ignore
|
|
193
|
+
|
|
194
|
+
def on_config_changed(self) -> None:
|
|
195
|
+
"""Configuration changed."""
|
|
196
|
+
conf.MODEL_PATH = self.model_path_input.text().strip()
|
|
197
|
+
conf.LISTEN_PORT = self.port_spin.value()
|
|
198
|
+
conf.THREAD_COUNT = self.threads_spin.value()
|
|
199
|
+
# Configuration will be saved on exit via atexit.register
|
|
200
|
+
|
|
201
|
+
def on_load_model(self) -> None:
|
|
202
|
+
"""Select model file."""
|
|
203
|
+
initial_dir = (
|
|
204
|
+
conf.MODEL_PATH if os.path.exists(conf.MODEL_PATH) else str(Path.home())
|
|
205
|
+
)
|
|
206
|
+
path, _ = QFileDialog.getOpenFileName(
|
|
207
|
+
self,
|
|
208
|
+
"Select Model File",
|
|
209
|
+
initial_dir,
|
|
210
|
+
"Model Files (*.bin *.gguf)",
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if path:
|
|
214
|
+
conf.MODEL_PATH = path
|
|
215
|
+
self.model_path_input.setText(os.path.normpath(path))
|
|
216
|
+
|
|
217
|
+
def toggle_server(self) -> None:
|
|
218
|
+
"""Start or stop server."""
|
|
219
|
+
if self.process.state() == QProcess.Running:
|
|
220
|
+
self.stop_server()
|
|
221
|
+
else:
|
|
222
|
+
self.start_server()
|
|
223
|
+
|
|
224
|
+
def start_server(self) -> None:
|
|
225
|
+
"""Start server."""
|
|
226
|
+
model_path = pathlib.Path(self.model_path_input.text().strip())
|
|
227
|
+
if not model_path.exists():
|
|
228
|
+
self.append_output(
|
|
229
|
+
"Error: Invalid model file path\n",
|
|
230
|
+
self.error_format,
|
|
231
|
+
)
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
os.chdir(str(model_path.parent))
|
|
235
|
+
cmd = [
|
|
236
|
+
"llama-server",
|
|
237
|
+
"--model",
|
|
238
|
+
model_path.name,
|
|
239
|
+
"--port",
|
|
240
|
+
str(self.port_spin.value()),
|
|
241
|
+
"--threads",
|
|
242
|
+
str(self.threads_spin.value()),
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
self.append_output(f"Start: {' '.join(cmd)}\n", self.info_format)
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
self.process.start(cmd[0], cmd[1:])
|
|
249
|
+
self.update_ui_state(running=True)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
self.append_output(f"Start failed: {e!s}\n", self.error_format)
|
|
252
|
+
|
|
253
|
+
def stop_server(self) -> None:
|
|
254
|
+
"""Stop server."""
|
|
255
|
+
if self.process.state() == QProcess.Running:
|
|
256
|
+
self.append_output("Stopping server...\n", self.info_format)
|
|
257
|
+
self.process.terminate()
|
|
258
|
+
if not self.process.waitForFinished(self.PROCESS_TERMINATE_TIMEOUT_MS):
|
|
259
|
+
self.process.kill()
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def on_start_browser() -> None:
|
|
263
|
+
"""Start browser."""
|
|
264
|
+
QDesktopServices.openUrl(QUrl(f"{conf.URL}:{conf.LISTEN_PORT}"))
|
|
265
|
+
|
|
266
|
+
def on_process_finished(self, exit_code: int, exit_status: int) -> None:
|
|
267
|
+
"""Process finished."""
|
|
268
|
+
self.append_output(
|
|
269
|
+
f"\nServer stopped, Exit code: {exit_code}, Status: {exit_status}\n",
|
|
270
|
+
self.info_format,
|
|
271
|
+
)
|
|
272
|
+
self.update_ui_state(running=False)
|
|
273
|
+
|
|
274
|
+
def handle_stdout(self) -> None:
|
|
275
|
+
"""Handle standard output."""
|
|
276
|
+
data = self.process.readAllStandardOutput()
|
|
277
|
+
text = QTextStream(data).readAll()
|
|
278
|
+
self.append_output(text, self.info_format)
|
|
279
|
+
|
|
280
|
+
def handle_stderr(self) -> None:
|
|
281
|
+
"""Handle standard error."""
|
|
282
|
+
data = self.process.readAllStandardError()
|
|
283
|
+
text = QTextStream(data).readAll()
|
|
284
|
+
self.append_output(text, self.error_format)
|
|
285
|
+
|
|
286
|
+
def append_output(
|
|
287
|
+
self,
|
|
288
|
+
text: str,
|
|
289
|
+
text_format: QTextCharFormat | None = None,
|
|
290
|
+
) -> None:
|
|
291
|
+
"""Append output."""
|
|
292
|
+
cursor: QTextCursor = self.output_area.textCursor()
|
|
293
|
+
cursor.movePosition(QTextCursor.MoveOperation.End)
|
|
294
|
+
|
|
295
|
+
if text_format:
|
|
296
|
+
cursor.setCharFormat(text_format)
|
|
297
|
+
|
|
298
|
+
cursor.insertText(text)
|
|
299
|
+
|
|
300
|
+
self.output_area.setTextCursor(cursor)
|
|
301
|
+
self.output_area.ensureCursorVisible()
|
|
302
|
+
|
|
303
|
+
def update_ui_state(self, *, running: bool) -> None:
|
|
304
|
+
"""Update UI state."""
|
|
305
|
+
self.model_path_input.setEnabled(not running)
|
|
306
|
+
self.load_model_btn.setEnabled(not running)
|
|
307
|
+
self.port_spin.setEnabled(not running)
|
|
308
|
+
self.threads_spin.setEnabled(not running)
|
|
309
|
+
self.browser_btn.setEnabled(running)
|
|
310
|
+
|
|
311
|
+
if running:
|
|
312
|
+
self.start_btn.setText("Stop Server")
|
|
313
|
+
else:
|
|
314
|
+
self.start_btn.setText("Start Server")
|
|
315
|
+
|
|
316
|
+
def moveEvent(self, event: QMoveEvent) -> None: # noqa: N802
|
|
317
|
+
"""Handle window move event."""
|
|
318
|
+
top_left = self.geometry().topLeft()
|
|
319
|
+
conf.WIN_POS = [top_left.x(), top_left.y()]
|
|
320
|
+
return super().moveEvent(event)
|
|
321
|
+
|
|
322
|
+
def resizeEvent(self, event: QResizeEvent) -> None: # noqa: N802
|
|
323
|
+
"""Handle window resize event."""
|
|
324
|
+
geometry = self.geometry()
|
|
325
|
+
conf.WIN_SIZE = [geometry.width(), geometry.height()]
|
|
326
|
+
return super().resizeEvent(event)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def main() -> None:
|
|
330
|
+
"""Main entry point."""
|
|
331
|
+
app = QApplication(sys.argv)
|
|
332
|
+
app.setFont(QFont("Consolas", 12))
|
|
333
|
+
window = LlamaServerGUI()
|
|
334
|
+
window.show()
|
|
335
|
+
sys.exit(app.exec_())
|
sfi/makepython/makepython.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
import json
|
|
5
4
|
import logging
|
|
6
5
|
import os
|
|
7
6
|
import shutil
|
|
@@ -35,8 +34,12 @@ def parse_pyproject_toml(directory: Path) -> dict:
|
|
|
35
34
|
logger.error(f"No pyproject.toml found in {directory}")
|
|
36
35
|
return {}
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
try:
|
|
38
|
+
with project_toml.open("rb") as f:
|
|
39
|
+
return tomllib.load(f)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
logger.error(f"Error parsing pyproject.toml: {e}")
|
|
42
|
+
return {}
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
def _get_build_command_from_toml(directory: Path) -> str | None:
|
|
@@ -59,12 +62,11 @@ def _get_build_command_from_toml(directory: Path) -> str | None:
|
|
|
59
62
|
logger.error(f"Unknown build-backend: {build_backend}")
|
|
60
63
|
return None
|
|
61
64
|
|
|
62
|
-
logger.error("No `build-system` or `build-backend` found in pyproject.toml
|
|
63
|
-
logger.error(json.dumps(project_data, indent=2, ensure_ascii=False, sort_keys=True))
|
|
65
|
+
logger.error("No `build-system` or `build-backend` found in pyproject.toml")
|
|
64
66
|
return None
|
|
65
67
|
|
|
66
68
|
|
|
67
|
-
def _get_build_command(directory: Path):
|
|
69
|
+
def _get_build_command(directory: Path) -> str | None:
|
|
68
70
|
"""Get build command from directory."""
|
|
69
71
|
project_path = directory / "pyproject.toml"
|
|
70
72
|
if project_path.is_file():
|
|
@@ -87,39 +89,38 @@ class Command:
|
|
|
87
89
|
cmds: list[str] | Callable[..., Any] | None = None
|
|
88
90
|
|
|
89
91
|
|
|
90
|
-
def _clean(root_dir: Path):
|
|
92
|
+
def _clean(root_dir: Path = cwd):
|
|
91
93
|
_run_command(["rm", "-rf", "dist", "build", "*.egg-info"], root_dir)
|
|
92
94
|
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
96
|
+
def main() -> None:
|
|
97
|
+
# Get build command
|
|
98
|
+
build_command = _get_build_command(cwd) or ""
|
|
99
|
+
commands = [
|
|
100
|
+
Command(name="build", alias="b", cmds=[build_command, "build"]),
|
|
101
|
+
Command(name="bumpversion", alias="bump", cmds=["bumpversion", "patch", "--tag"]),
|
|
102
|
+
Command(name="clean", alias="c", cmds=_clean),
|
|
103
|
+
Command(name="publish", alias="p"), # No preset commands
|
|
104
|
+
Command(name="test", alias="t", cmds=lambda: os.system("pytest")),
|
|
105
|
+
Command(name="test-benchmark", alias="tb", cmds=lambda: os.system("pytest -m benchmark")),
|
|
106
|
+
Command(name="test-coverage", alias="tc", cmds=lambda: os.system("pytest --cov=sfi")),
|
|
107
|
+
Command(name="token", alias="tk", cmds=lambda: _set_token(build_command)),
|
|
108
|
+
]
|
|
109
|
+
command_dict = {command.name: command for command in commands}
|
|
110
|
+
command_dict.update({command.alias: command for command in commands})
|
|
111
|
+
choices = [command.alias for command in commands]
|
|
112
|
+
choices.extend([command.name for command in commands])
|
|
113
|
+
|
|
114
|
+
# Parse args
|
|
112
115
|
parser = argparse.ArgumentParser(description="Make Python")
|
|
113
|
-
parser.add_argument("command", type=str, choices=_CHOICES, help=f"Command to run, options: {_CHOICES}")
|
|
114
116
|
parser.add_argument("--debug", "-d", action="store_true", help="Enable debug mode")
|
|
115
|
-
|
|
117
|
+
parser.add_argument("command", type=str, choices=choices, help=f"Command to run, options: {choices}")
|
|
116
118
|
args = parser.parse_args()
|
|
117
119
|
if args.debug:
|
|
118
120
|
logger.setLevel(logging.DEBUG)
|
|
119
121
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
command = _COMMAND_DICT.get(args.command)
|
|
122
|
+
logger.debug(f"Using build command: {build_command}")
|
|
123
|
+
command = command_dict.get(args.command)
|
|
123
124
|
if command:
|
|
124
125
|
if callable(command.cmds):
|
|
125
126
|
command.cmds()
|