pysfi 0.1.13__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysfi
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Single File commands for Interactive python.
5
5
  Requires-Python: >=3.8
6
6
  Requires-Dist: tomli>=2.4.0; python_version < '3.11'
@@ -1,43 +1,43 @@
1
- sfi/__init__.py,sha256=dZ1dacNjP1yRdG5N4-UtVhSZGUdEzRv4usjC1kr-3jY,116
2
- sfi/cli.py,sha256=QF6-bdw8vOUMmFovjp4lYpWesHtae4oxHeEWDnt7_k8,504
3
- sfi/alarmclock/__init__.py,sha256=yyFexrNdi65kz3l68YjT5Bdlq4FFqBR80H8vV7e_D58,75
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
4
  sfi/alarmclock/alarmclock.py,sha256=ixVkbg548smUivRsqyI3YSZ81BWIrKawnuezAp3BzyE,11635
5
- sfi/bumpversion/__init__.py,sha256=ajzMFse8CnLshD5qpKEr4fze3tnup2S9GWWyd7xpC2A,127
6
- sfi/bumpversion/bumpversion.py,sha256=HOyHLaE0sZajrlcVZ8hsim8mPjz77qwQVSo6aIzjMXE,20735
7
- sfi/cleanbuild/__init__.py,sha256=V4WV0xUvTaNGxawfYqlLT98t_8FeiA7ec1NW6R0-pGE,101
8
- sfi/cleanbuild/cleanbuild.py,sha256=Fr6_cr3rj4llcEQ8yNTK-DHdSzmx1I4hYFJJHu5YEz0,5200
9
- sfi/condasetup/__init__.py,sha256=a99mtb8qROZYvqLuhmlasVCgbmAL9nzVzOJFrVSWLGE,3
10
- sfi/condasetup/condasetup.py,sha256=RlbXVYcAJYMau-ZzHOMzHrHl4r-lqNZO0bT-zWuzP_k,4581
11
- sfi/docdiff/__init__.py,sha256=a99mtb8qROZYvqLuhmlasVCgbmAL9nzVzOJFrVSWLGE,3
12
- sfi/docdiff/docdiff.py,sha256=anilgq16icu-UxdgRR7B_57G9CFJ79xBSjOm9DOQluY,7736
13
- sfi/docscan/__init__.py,sha256=HQzSYiozGNtCdC771DXce57YwdXzQiSWFQKaTnnjAQU,124
14
- sfi/docscan/docscan.py,sha256=rk8mjEI2SKNIliV-Yb41pfUmYBQ1tUhk5LHUNEjkszI,41890
15
- sfi/docscan/docscan_gui.py,sha256=c7sJXvq5201Kh7iewalc50qoMnNR_eEna0NqoEJ7igw,52335
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
16
  sfi/docscan/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  sfi/docscan/lang/eng.py,sha256=GcOcT9FLcPZRdJ-MbLRYyf6vDweZTQBu_zUnEFzRY84,8529
18
18
  sfi/docscan/lang/zhcn.py,sha256=1SZwQjZF3oi9FsnzuZB-9v7P64sGm5oNmVjuL-rhcEQ,8885
19
19
  sfi/filedate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- sfi/filedate/filedate.py,sha256=5FARcsB2Rlz2uTBxeYYjbIEJb9l1cyXj9WSoNKvSrRo,6068
20
+ sfi/filedate/filedate.py,sha256=eOR6LCwYosO6qyThPNeSCNImiU19UTQaqNWTkFKHAfs,6227
21
21
  sfi/gittool/__init__.py,sha256=Xqxw7UUX-TKkWOCB1QHq8AdIKTkU7x87Xr-E0yVmObA,24
22
22
  sfi/gittool/gittool.py,sha256=BBE6gm9qP1fAWLqKprmsf7bOFgDvBvia8_bMaXc7dR4,11960
23
23
  sfi/img2pdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- sfi/img2pdf/img2pdf.py,sha256=rR6f5bMg-HoKMvSyu2rWfDbx0kmo6F6e1dg5z3710Wo,15129
24
+ sfi/img2pdf/img2pdf.py,sha256=Rx8N9ZG2MrbzfiGAaZGEFlNGuOpP_gwNY5MmOSGTv3w,15198
25
25
  sfi/llmclient/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  sfi/llmclient/llmclient.py,sha256=zvaT-HEiL3CM3uEpvzuseLPEqFQe6RO4_fhnA2djHo0,22681
27
27
  sfi/llmquantize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- sfi/llmquantize/llmquantize.py,sha256=N4h0RwdpVZdZM3qDWbL34hLGpQ3XuPkP_F8kyhhM8ZI,18855
29
- sfi/llmserver/__init__.py,sha256=a99mtb8qROZYvqLuhmlasVCgbmAL9nzVzOJFrVSWLGE,3
28
+ sfi/llmquantize/llmquantize.py,sha256=crbZ4qnq6BAAkba_dBVYVhI9hhd9yBQ7XlNsWIrAes8,18888
29
+ sfi/llmserver/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
30
30
  sfi/llmserver/llmserver.py,sha256=Fm4Go7wif4xMGomMFDsyJnYMafXsWemGkr-VfaeYa6w,13530
31
31
  sfi/makepython/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- sfi/makepython/makepython.py,sha256=srinUE1Yr6zF-NjiUFVC1sUrpy8fYpDnCs7p648jWqc,23278
32
+ sfi/makepython/makepython.py,sha256=GrzQTJyyB2PXugJK3yLxP1V5I_bSymW0qfkeqZvxRPA,43154
33
33
  sfi/pdfsplit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- sfi/pdfsplit/pdfsplit.py,sha256=QWtW3GU28U2ZOyN5sCbH7jEMBpNbuAIzjXWOAXXW44M,6209
35
- sfi/pyarchive/__init__.py,sha256=a99mtb8qROZYvqLuhmlasVCgbmAL9nzVzOJFrVSWLGE,3
34
+ sfi/pdfsplit/pdfsplit.py,sha256=LOz7PuLlLBiXlFTjJej9G0UvJ03GZ9H-Gghy8ErvJzE,6576
35
+ sfi/pyarchive/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
36
36
  sfi/pyarchive/pyarchive.py,sha256=OnPbIRA0C9JdeyNsVZ6rJg7ExItKyJz4jVw5W4c92DA,38293
37
37
  sfi/pyembedinstall/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  sfi/pyembedinstall/pyembedinstall.py,sha256=LHnuvr63DXuntdS6a_7uQynOfarK-30WBUerSzawSHE,24171
39
39
  sfi/pylibpack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- sfi/pylibpack/pylibpack.py,sha256=qK6J2_0VLMWQsKjcZ5njsvfqCnMRmGx8RweH2olhWTo,57818
40
+ sfi/pylibpack/pylibpack.py,sha256=AJbMY4aHJq0YhyUXryBargNqCUTeSwaUuc3SiCcjcko,57685
41
41
  sfi/pylibpack/rules/numpy.json,sha256=ee4gA5NBudFi3MaJA-QlBKQwiQAUb-eluF8HNVkl7Vk,384
42
42
  sfi/pylibpack/rules/pymupdf.json,sha256=Hkzh8dvXKCzKx4aeHbu5E0qwgfbwQxZH2VLtQZzlMO4,153
43
43
  sfi/pylibpack/rules/pyqt5.json,sha256=JKGnVSUMfXGR5XK1sbL1F6cAsEhl7hK12QkrulAB00M,374
@@ -47,10 +47,10 @@ sfi/pylibpack/rules/shiboken2.json,sha256=9Pl3eslvergyjlyHNknkyN0oZlcH3049WULe5W
47
47
  sfi/pyloadergen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  sfi/pyloadergen/pyloadergen.py,sha256=VWJzc0opmMVthHE_RGbWeLe-DBaMn94gZo2yYLkf8cI,45696
49
49
  sfi/pypack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
- sfi/pypack/pypack.py,sha256=3LTCZMk8TqYhJbGv9AMLbe_Vi6bV7-cQUuHrWWO7QZU,22385
50
+ sfi/pypack/pypack.py,sha256=vDT_lVkq0eHeFCCR3pwjZGnF0EyQZDesM6ze--Tl194,22894
51
51
  sfi/pyprojectparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- sfi/pyprojectparse/pyprojectparse.py,sha256=pHuwVCpVUlRoC9pRo3r6d0DfqqoEbsREsUuYjnkhqhU,30575
53
- sfi/pysourcepack/__init__.py,sha256=a99mtb8qROZYvqLuhmlasVCgbmAL9nzVzOJFrVSWLGE,3
52
+ sfi/pyprojectparse/pyprojectparse.py,sha256=qBHj8l-_ONxD4VA5KvCfXdvS3fbjfOls9gRX83I0TDE,30641
53
+ sfi/pysourcepack/__init__.py,sha256=4W8VliAYUP1KY2gLJ_YDy2TmcXYVm-PY7XikQD_bFwA,2
54
54
  sfi/pysourcepack/pysourcepack.py,sha256=5_KrI2Y1TKKcoYfsFpTNXWguj6n8CKdca8lgoBCsL8k,12160
55
55
  sfi/quizbase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  sfi/quizbase/quizbase.py,sha256=3tPUuYexZ9TVsNPPO_Itmr5OvyHSgY5OSUZwPoQt9zg,30605
@@ -62,9 +62,7 @@ sfi/taskkill/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  sfi/taskkill/taskkill.py,sha256=wM9g8sWJVTy4GxXe26rKdax2lIBI-uH9wP5wRenriH4,11606
63
63
  sfi/which/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
64
  sfi/which/which.py,sha256=2YbGgSiT1ySapKVV1ESoPf4P-JU8vvzmsZY39NiVr6k,2596
65
- sfi/workflowengine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
- sfi/workflowengine/workflowengine.py,sha256=pPRsxWB2ZoDwcVTjsDlpiml-xZYiZBKjLcINu4TGBcE,19209
67
- pysfi-0.1.13.dist-info/METADATA,sha256=6MAsufstxqnCpDvLj0O0xA6ZjTMvMAhwlccsrvu9uT4,4198
68
- pysfi-0.1.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
- pysfi-0.1.13.dist-info/entry_points.txt,sha256=ju_Bwp3L8-5Bpcj0PSCCDL8vGsGb4ZYpjfzprSdFrfA,1215
70
- pysfi-0.1.13.dist-info/RECORD,,
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,,
@@ -12,6 +12,7 @@ img2pdf = sfi.img2pdf.img2pdf:main
12
12
  llmcli = sfi.llmclient.llmclient:main
13
13
  llmqnt = sfi.llmquantize.llmquantize:main
14
14
  llmsvr = sfi.llmserver.llmserver:main
15
+ makepython = sfi.makepython.makepython:main
15
16
  mkp = sfi.makepython.makepython:main
16
17
  pdfsplit = sfi.pdfsplit.pdfsplit:main
17
18
  pyarchive = sfi.pyarchive.pyarchive:main
sfi/__init__.py CHANGED
@@ -1,5 +1,20 @@
1
- """Single File commands for Interactive python."""
2
-
3
- from __future__ import annotations
4
-
5
- __version__ = "0.1.13"
1
+ """Single File commands for Interactive python.
2
+
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__"]
@@ -1,3 +1,3 @@
1
- """Alarm clock module for pysfi."""
2
-
3
- from __future__ import annotations
1
+ """Alarm clock module for pysfi."""
2
+
3
+ from __future__ import annotations
@@ -1,5 +1,5 @@
1
- """Bumpversion - Automated version number management tool."""
2
-
3
- from __future__ import annotations
4
-
5
- __version__ = "0.1.13"
1
+ """Bumpversion - Automated version number management tool."""
2
+
3
+ from __future__ import annotations
4
+
5
+ __version__ = "0.1.14"
@@ -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
- VERSION_PATTERN: Final = re.compile(
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(self, part: VersionPart, reset_prerelease: bool = True, prerelease: str | None = None) -> Version:
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(file_path: Path, content: str, original_content: str) -> None:
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(file_path, new_content, original_bytes.decode("utf-8"))
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 = {self.root_path / p for p in projects_data.get("subprojects", [])}
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 for f in init_files if not any(part in excluded_dirs for part in f.parts) and "tests" not in f.parts
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 = [f for f in init_files if not any(str(f).startswith(str(sp)) for sp in self.subproject_paths)]
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(self, version: Version, message: str | None, files: list[Path]) -> None:
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(["git", "tag", "-a", tag_name, "-m", f"Version {version}"], check=True)
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(prog="bumpversion", description="Automated version number management tool")
470
- parser.add_argument("part", type=str, choices=["major", "minor", "patch"], help="Version part to bump")
471
- parser.add_argument("--files", "-f", type=str, nargs="*", help="Specific files to update (default: auto-detect)")
472
- parser.add_argument("--prerelease", "-p", type=str, help="Set prerelease tag (e.g., alpha, beta, rc1)")
473
- parser.add_argument("--commit", "-c", action="store_true", help="Commit changes to git")
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("--dry-run", "-n", action="store_true", help="Show what would be done without making changes")
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
 
@@ -1,3 +1,3 @@
1
- """Cleanbuild - Clean build artifacts and temporary files."""
2
-
3
- from __future__ import annotations
1
+ """Cleanbuild - Clean build artifacts and temporary files."""
2
+
3
+ from __future__ import annotations
@@ -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
- _BUILD_FOLDERS = ("build", "dist", ".venv", "node_modules")
27
+
24
28
 
25
29
  @dataclass(frozen=True)
26
30
  class SearchResult:
sfi/cli.py CHANGED
@@ -2,15 +2,26 @@ from __future__ import annotations
2
2
 
3
3
  import argparse
4
4
 
5
- from sfi import __version__ as VERSION # noqa: N812
5
+ from sfi import __version__ as VERSION
6
6
 
7
7
 
8
8
  def main() -> None:
9
9
  """Main entry point for the pysfi CLI tool.
10
10
 
11
11
  Parses command line arguments and handles version printing.
12
+
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.
12
19
  """
13
- parser = argparse.ArgumentParser()
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
+ )
14
25
  parser.add_argument(
15
26
  "-v",
16
27
  "--version",
@@ -1 +1 @@
1
-
1
+
@@ -3,92 +3,106 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import logging
5
5
  import os
6
+ import subprocess
6
7
  from pathlib import Path
8
+ from typing import Final
7
9
 
8
10
  logging.basicConfig(level=logging.INFO, format="%(message)s")
9
11
  logger = logging.getLogger(__name__)
10
- cwd = Path.cwd()
11
-
12
- _CONDA_MIRROR_URLS: dict[str, frozenset[str]] = {
13
- "tsinghua": frozenset(
14
- [
15
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/",
16
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/",
17
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/",
18
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2/",
19
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/",
20
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/",
21
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/",
22
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/",
23
- "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/",
24
- ]
25
- ),
26
- "ustc": frozenset(
27
- [
28
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/main/",
29
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/free/",
30
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/r/",
31
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/msys2/",
32
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/pro/",
33
- "https://mirrors.ustc.edu.cn/anaconda/pkgs/dev/",
34
- "https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/",
35
- "https://mirrors.ustc.edu.cn/anaconda/cloud/bioconda/",
36
- "https://mirrors.ustc.edu.cn/anaconda/cloud/menpo/",
37
- "https://mirrors.ustc.edu.cn/anaconda/cloud/pytorch/",
38
- ]
39
- ),
40
- "bsfu": frozenset(
41
- [
42
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/main/",
43
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/free/",
44
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/r/",
45
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/msys2/",
46
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/pro/",
47
- "https://mirrors.bsfu.edu.cn/anaconda/pkgs/dev/",
48
- "https://mirrors.bsfu.edu.cn/anaconda/cloud/conda-forge/",
49
- "https://mirrors.bsfu.edu.cn/anaconda/cloud/bioconda/",
50
- "https://mirrors.bsfu.edu.cn/anaconda/cloud/menpo/",
51
- "https://mirrors.bsfu.edu.cn/anaconda/cloud/pytorch/",
52
- ]
53
- ),
54
- "aliyun": frozenset(
55
- [
56
- "https://mirrors.aliyun.com/anaconda/pkgs/main/",
57
- "https://mirrors.aliyun.com/anaconda/pkgs/free/",
58
- "https://mirrors.aliyun.com/anaconda/pkgs/r/",
59
- "https://mirrors.aliyun.com/anaconda/pkgs/msys2/",
60
- "https://mirrors.aliyun.com/anaconda/pkgs/pro/",
61
- "https://mirrors.aliyun.com/anaconda/pkgs/dev/",
62
- "https://mirrors.aliyun.com/anaconda/cloud/conda-forge/",
63
- "https://mirrors.aliyun.com/anaconda/cloud/bioconda/",
64
- "https://mirrors.aliyun.com/anaconda/cloud/menpo/",
65
- "https://mirrors.aliyun.com/anaconda/cloud/pytorch/",
66
- ]
67
- ),
12
+
13
+ # Conda mirror URLs
14
+ _CONDA_MIRROR_URLS: Final[dict[str, frozenset[str]]] = {
15
+ "tsinghua": frozenset([
16
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/",
17
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/",
18
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r/",
19
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2/",
20
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/",
21
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/",
22
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/",
23
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/",
24
+ "https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/",
25
+ ]),
26
+ "ustc": frozenset([
27
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/main/",
28
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/free/",
29
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/r/",
30
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/msys2/",
31
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/pro/",
32
+ "https://mirrors.ustc.edu.cn/anaconda/pkgs/dev/",
33
+ "https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/",
34
+ "https://mirrors.ustc.edu.cn/anaconda/cloud/bioconda/",
35
+ "https://mirrors.ustc.edu.cn/anaconda/cloud/menpo/",
36
+ "https://mirrors.ustc.edu.cn/anaconda/cloud/pytorch/",
37
+ ]),
38
+ "bsfu": frozenset([
39
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/main/",
40
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/free/",
41
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/r/",
42
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/msys2/",
43
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/pro/",
44
+ "https://mirrors.bsfu.edu.cn/anaconda/pkgs/dev/",
45
+ "https://mirrors.bsfu.edu.cn/anaconda/cloud/conda-forge/",
46
+ "https://mirrors.bsfu.edu.cn/anaconda/cloud/bioconda/",
47
+ "https://mirrors.bsfu.edu.cn/anaconda/cloud/menpo/",
48
+ "https://mirrors.bsfu.edu.cn/anaconda/cloud/pytorch/",
49
+ ]),
50
+ "aliyun": frozenset([
51
+ "https://mirrors.aliyun.com/anaconda/pkgs/main/",
52
+ "https://mirrors.aliyun.com/anaconda/pkgs/free/",
53
+ "https://mirrors.aliyun.com/anaconda/pkgs/r/",
54
+ "https://mirrors.aliyun.com/anaconda/pkgs/msys2/",
55
+ "https://mirrors.aliyun.com/anaconda/pkgs/pro/",
56
+ "https://mirrors.aliyun.com/anaconda/pkgs/dev/",
57
+ "https://mirrors.aliyun.com/anaconda/cloud/conda-forge/",
58
+ "https://mirrors.aliyun.com/anaconda/cloud/bioconda/",
59
+ "https://mirrors.aliyun.com/anaconda/cloud/menpo/",
60
+ "https://mirrors.aliyun.com/anaconda/cloud/pytorch/",
61
+ ]),
68
62
  }
69
63
 
70
64
 
71
65
  def set_conda_mirror(mirror: str = "tsinghua") -> None:
72
- """Set the Conda mirror for the given channel."""
73
- if mirror in _CONDA_MIRROR_URLS:
74
- old_config = Path.home() / ".condarc"
75
- if old_config.exists():
76
- logger.info("Found existing .condarc file, backing it up")
77
- os.rename(old_config, Path.home() / ".condarc.bak")
78
- else:
79
- logger.debug("No existing .condarc file found")
80
-
81
- mirror_urls = _CONDA_MIRROR_URLS[mirror]
82
- for url in mirror_urls:
83
- logger.debug(f"Adding mirror: {url}")
84
- os.system(f"conda config --add channels {url}")
85
- os.system("conda config --set show_channel_urls yes")
86
- logger.info("Conda mirror set successfully")
87
- else:
66
+ """Set the Conda mirror for the given channel.
67
+
68
+ Args:
69
+ mirror: Mirror name (tsinghua, ustc, bsfu, or aliyun)
70
+ """
71
+ if mirror not in _CONDA_MIRROR_URLS:
88
72
  logger.error(f"Invalid mirror: {mirror}")
73
+ return
74
+
75
+ old_config = Path.home() / ".condarc"
76
+ if old_config.exists():
77
+ logger.info("Found existing .condarc file, backing it up")
78
+ os.rename(old_config, Path.home() / ".condarc.bak")
79
+ else:
80
+ logger.debug("No existing .condarc file found")
81
+
82
+ mirror_urls = _CONDA_MIRROR_URLS[mirror]
83
+ for url in mirror_urls:
84
+ logger.debug(f"Adding mirror: {url}")
85
+ try:
86
+ subprocess.run(["conda", "config", "--add", "channels", url], check=True)
87
+ except subprocess.CalledProcessError as e:
88
+ logger.error(f"Failed to add mirror {url}: {e}")
89
+ return
90
+
91
+ try:
92
+ subprocess.run(
93
+ ["conda", "config", "--set", "show_channel_urls", "yes"], check=True
94
+ )
95
+ logger.info("Conda mirror set successfully")
96
+ except subprocess.CalledProcessError as e:
97
+ logger.error(f"Failed to set show_channel_urls: {e}")
98
+
89
99
 
100
+ def parse_args() -> argparse.Namespace:
101
+ """Parse command line arguments.
90
102
 
91
- def parse_args():
103
+ Returns:
104
+ Parsed arguments
105
+ """
92
106
  parser = argparse.ArgumentParser(description="Setup Conda environment for SFI")
93
107
  parser.add_argument(
94
108
  "mirror",
@@ -103,7 +117,8 @@ def parse_args():
103
117
  return parser.parse_args()
104
118
 
105
119
 
106
- def main():
120
+ def main() -> None:
121
+ """Main entry point for condasetup CLI."""
107
122
  args = parse_args()
108
123
 
109
124
  if args.debug:
sfi/docdiff/__init__.py CHANGED
@@ -1 +1 @@
1
-
1
+
sfi/docdiff/docdiff.py CHANGED
@@ -10,9 +10,10 @@ import time
10
10
  from dataclasses import dataclass
11
11
  from functools import cached_property
12
12
  from pathlib import Path
13
- from typing import Any
13
+ from typing import Any, Final
14
14
 
15
- CONFIG_FILE = Path.home() / ".pysfi" / "docdiff.json"
15
+ # Configuration file path
16
+ CONFIG_FILE: Final[Path] = Path.home() / ".pysfi" / "docdiff.json"
16
17
 
17
18
  logging.basicConfig(level=logging.INFO, format="%(message)s")
18
19
  logger = logging.getLogger(__name__)
sfi/docscan/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- """Document scanner module for scanning and extracting content from various document formats."""
2
-
3
- __version__ = "0.1.13"
1
+ """Document scanner module for scanning and extracting content from various document formats."""
2
+
3
+ __version__ = "0.1.14"