pysfi 0.1.13__tar.gz → 0.1.14__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.
Files changed (145) hide show
  1. {pysfi-0.1.13 → pysfi-0.1.14}/PKG-INFO +1 -1
  2. {pysfi-0.1.13 → pysfi-0.1.14}/pyproject.toml +6 -2
  3. pysfi-0.1.14/sfi/__init__.py +20 -0
  4. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/alarmclock/__init__.py +3 -3
  5. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/bumpversion/__init__.py +5 -5
  6. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/bumpversion/bumpversion.py +64 -15
  7. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/cleanbuild/__init__.py +3 -3
  8. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/cleanbuild/cleanbuild.py +5 -1
  9. pysfi-0.1.14/sfi/cli.py +32 -0
  10. {pysfi-0.1.13/sfi/filedate/tests → pysfi-0.1.14/sfi/condasetup}/__init__.py +1 -1
  11. pysfi-0.1.14/sfi/condasetup/condasetup.py +131 -0
  12. {pysfi-0.1.13/sfi/llmserver → pysfi-0.1.14/sfi/docdiff}/__init__.py +1 -1
  13. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/docdiff.py +3 -2
  14. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/__init__.py +3 -3
  15. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/docscan.py +78 -23
  16. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/docscan_gui.py +5 -5
  17. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/filedate/filedate.py +12 -5
  18. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/img2pdf/img2pdf.py +5 -5
  19. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmquantize/llmquantize.py +44 -33
  20. pysfi-0.1.14/sfi/llmserver/__init__.py +1 -0
  21. pysfi-0.1.14/sfi/makepython/makepython.py +1271 -0
  22. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/makepython/tests/test_makepython.py +10 -10
  23. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pdfsplit/pdfsplit.py +45 -12
  24. pysfi-0.1.14/sfi/pyarchive/__init__.py +1 -0
  25. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyarchive/tests/test_pyarchive.py +18 -7
  26. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/pylibpack.py +5 -13
  27. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/pypack.py +127 -105
  28. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/pyprojectparse.py +11 -14
  29. pysfi-0.1.14/sfi/pysourcepack/__init__.py +1 -0
  30. pysfi-0.1.13/sfi/__init__.py +0 -5
  31. pysfi-0.1.13/sfi/cli.py +0 -21
  32. pysfi-0.1.13/sfi/condasetup/condasetup.py +0 -116
  33. pysfi-0.1.13/sfi/makepython/makepython.py +0 -710
  34. pysfi-0.1.13/sfi/pyarchive/__init__.py +0 -1
  35. pysfi-0.1.13/sfi/pyembedinstall/tests/__init__.py +0 -1
  36. pysfi-0.1.13/sfi/pysourcepack/__init__.py +0 -1
  37. pysfi-0.1.13/sfi/workflowengine/__init__.py +0 -0
  38. pysfi-0.1.13/sfi/workflowengine/tests/__init__.py +0 -1
  39. pysfi-0.1.13/sfi/workflowengine/tests/test_benchmark.py +0 -725
  40. pysfi-0.1.13/sfi/workflowengine/tests/test_workflowengine.py +0 -740
  41. pysfi-0.1.13/sfi/workflowengine/workflowengine.py +0 -547
  42. {pysfi-0.1.13 → pysfi-0.1.14}/.gitignore +0 -0
  43. {pysfi-0.1.13 → pysfi-0.1.14}/README.md +0 -0
  44. {pysfi-0.1.13 → pysfi-0.1.14}/examples/pack_demo/README.md +0 -0
  45. {pysfi-0.1.13/sfi/cleanbuild → pysfi-0.1.14/examples/workflowengine}/README.md +0 -0
  46. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/alarmclock/README.md +0 -0
  47. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/alarmclock/alarmclock.py +0 -0
  48. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/bumpversion/README.md +0 -0
  49. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/bumpversion/tests/__init__.py +0 -0
  50. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/bumpversion/tests/test_bumpversion.py +0 -0
  51. {pysfi-0.1.13/sfi/condasetup → pysfi-0.1.14/sfi/cleanbuild}/README.md +0 -0
  52. {pysfi-0.1.13/sfi/gittool → pysfi-0.1.14/sfi/condasetup}/README.md +0 -0
  53. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/README.md +0 -0
  54. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/tests/__init__.py +0 -0
  55. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/tests/test_benchmark.py +0 -0
  56. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/tests/test_docdiff.py +0 -0
  57. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/tests/test_enhanced.py +0 -0
  58. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docdiff/tests/test_functional.py +0 -0
  59. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/README.md +0 -0
  60. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/lang/__init__.py +0 -0
  61. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/lang/eng.py +0 -0
  62. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/lang/zhcn.py +0 -0
  63. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/tests/__init__.py +0 -0
  64. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/tests/test_benchmark.py +0 -0
  65. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/tests/test_docscan.py +0 -0
  66. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/docscan/tests/test_enhanced.py +0 -0
  67. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/filedate/README.md +0 -0
  68. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/filedate/__init__.py +0 -0
  69. {pysfi-0.1.13/sfi/condasetup → pysfi-0.1.14/sfi/filedate/tests}/__init__.py +0 -0
  70. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/filedate/tests/test_filedate.py +0 -0
  71. {pysfi-0.1.13/sfi/img2pdf → pysfi-0.1.14/sfi/gittool}/README.md +0 -0
  72. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/gittool/__init__.py +0 -0
  73. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/gittool/gittool.py +0 -0
  74. {pysfi-0.1.13/sfi/llmclient → pysfi-0.1.14/sfi/img2pdf}/README.md +0 -0
  75. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/img2pdf/__init__.py +0 -0
  76. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/img2pdf/tests/__init__.py +0 -0
  77. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/img2pdf/tests/test_benchmark.py +0 -0
  78. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/img2pdf/tests/test_img2pdf.py +0 -0
  79. {pysfi-0.1.13/sfi/llmquantize → pysfi-0.1.14/sfi/llmclient}/README.md +0 -0
  80. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmclient/__init__.py +0 -0
  81. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmclient/llmclient.py +0 -0
  82. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmclient/tests/__init__.py +0 -0
  83. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmclient/tests/test_benchmark.py +0 -0
  84. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmclient/tests/test_llmclient.py +0 -0
  85. {pysfi-0.1.13/sfi/llmserver → pysfi-0.1.14/sfi/llmquantize}/README.md +0 -0
  86. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmquantize/__init__.py +0 -0
  87. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmquantize/tests/__init__.py +0 -0
  88. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmquantize/tests/test_llmquantize.py +0 -0
  89. {pysfi-0.1.13/sfi/workflowengine → pysfi-0.1.14/sfi/llmserver}/README.md +0 -0
  90. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/llmserver/llmserver.py +0 -0
  91. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/makepython/README.md +0 -0
  92. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/makepython/__init__.py +0 -0
  93. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/makepython/tests/test_benchmark.py +0 -0
  94. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pdfsplit/__init__.py +0 -0
  95. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pdfsplit/tests/__init__.py +0 -0
  96. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pdfsplit/tests/test_pdfsplit.py +0 -0
  97. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyarchive/README.md +0 -0
  98. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyarchive/pyarchive.py +0 -0
  99. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyarchive/tests/__init__.py +0 -0
  100. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyarchive/tests/test_benchmark.py +0 -0
  101. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyembedinstall/__init__.py +0 -0
  102. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyembedinstall/pyembedinstall.py +0 -0
  103. {pysfi-0.1.13/sfi/docdiff → pysfi-0.1.14/sfi/pyembedinstall/tests}/__init__.py +0 -0
  104. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyembedinstall/tests/test_pyembedinstall.py +0 -0
  105. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/README.md +0 -0
  106. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/__init__.py +0 -0
  107. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/tests/__init__.py +0 -0
  108. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/tests/test_benchmark.py +0 -0
  109. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pylibpack/tests/test_pylibpack.py +0 -0
  110. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyloadergen/__init__.py +0 -0
  111. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyloadergen/pyloadergen.py +0 -0
  112. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyloadergen/tests/__init__.py +0 -0
  113. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyloadergen/tests/test_benchmark.py +0 -0
  114. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyloadergen/tests/test_pyloadergen.py +0 -0
  115. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/README.md +0 -0
  116. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/__init__.py +0 -0
  117. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/tests/__init__.py +0 -0
  118. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/tests/test_benchmark.py +0 -0
  119. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pypack/tests/test_pack.py +0 -0
  120. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/README.md +0 -0
  121. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/__init__.py +0 -0
  122. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/tests/__init__.py +0 -0
  123. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/tests/test_benchmark.py +0 -0
  124. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pyprojectparse/tests/test_projectparse.py +0 -0
  125. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pysourcepack/README.md +0 -0
  126. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/pysourcepack/pysourcepack.py +0 -0
  127. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/README.md +0 -0
  128. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/__init__.py +0 -0
  129. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/quizbase.py +0 -0
  130. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/quizbase_gui.py +0 -0
  131. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/tests/__init__.py +0 -0
  132. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/tests/test_quizbase.py +0 -0
  133. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/quizbase/tests/test_quizbase_gui.py +0 -0
  134. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/regexvalidate/README.md +0 -0
  135. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/regexvalidate/__init__.py +0 -0
  136. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/regexvalidate/regexvalidate.py +0 -0
  137. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/README.md +0 -0
  138. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/__init__.py +0 -0
  139. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/taskkill.py +0 -0
  140. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/tests/__init__.py +0 -0
  141. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/tests/test_benchmark.py +0 -0
  142. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/tests/test_smoke.py +0 -0
  143. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/taskkill/tests/test_taskkill.py +0 -0
  144. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/which/__init__.py +0 -0
  145. {pysfi-0.1.13 → pysfi-0.1.14}/sfi/which/which.py +0 -0
@@ -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'
@@ -8,7 +8,7 @@ description = "Single File commands for Interactive python."
8
8
  name = "pysfi"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
11
- version = "0.1.13"
11
+ version = "0.1.14"
12
12
 
13
13
  [project.scripts]
14
14
  alarmclk = "sfi.alarmclock.alarmclock:main"
@@ -25,6 +25,7 @@ llmcli = "sfi.llmclient.llmclient:main"
25
25
  llmqnt = "sfi.llmquantize.llmquantize:main"
26
26
  llmsvr = "sfi.llmserver.llmserver:main"
27
27
  mkp = "sfi.makepython.makepython:main"
28
+ makepython = "sfi.makepython.makepython:main"
28
29
  pdfsplit = "sfi.pdfsplit.pdfsplit:main"
29
30
  pyarchive = "sfi.pyarchive.pyarchive:main"
30
31
  pyembedinstall = "sfi.pyembedinstall.pyembedinstall:main"
@@ -127,6 +128,9 @@ known-first-party = ["sfi"]
127
128
  [tool.ruff.lint.per-file-ignores]
128
129
  "__init__.py" = ["F401"] # Allow unused imports
129
130
 
131
+ [tool.uv.sources]
132
+ pysfi = { workspace = true }
133
+
130
134
  [tool.uv.workspace]
131
135
  members = [
132
136
  "sfi/alarmclock",
@@ -154,7 +158,6 @@ members = [
154
158
  "sfi/regexvalidate",
155
159
  "sfi/taskkill",
156
160
  "sfi/which",
157
- "sfi/workflowengine",
158
161
  ]
159
162
 
160
163
  [dependency-groups]
@@ -168,6 +171,7 @@ dev = [
168
171
  "pytest-cov>=5.0.0",
169
172
  "pytest-mock>=3.14.0",
170
173
  "pytest-qt>=4.4.0",
174
+ "pytest-xdist>=3.5.0",
171
175
  "pytest>=8.3.5",
172
176
  "qdarkstyle>=3.2.3",
173
177
  "ruff>=0.14.11",
@@ -0,0 +1,20 @@
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:
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ from sfi import __version__ as VERSION
6
+
7
+
8
+ def main() -> None:
9
+ """Main entry point for the pysfi CLI tool.
10
+
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.
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
+ )
25
+ parser.add_argument(
26
+ "-v",
27
+ "--version",
28
+ action="version",
29
+ version=f"%(prog)s v{VERSION}",
30
+ help="Print version and exit",
31
+ )
32
+ parser.parse_args()
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import logging
5
+ import os
6
+ import subprocess
7
+ from pathlib import Path
8
+ from typing import Final
9
+
10
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
11
+ logger = logging.getLogger(__name__)
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
+ ]),
62
+ }
63
+
64
+
65
+ def set_conda_mirror(mirror: str = "tsinghua") -> None:
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:
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
+
99
+
100
+ def parse_args() -> argparse.Namespace:
101
+ """Parse command line arguments.
102
+
103
+ Returns:
104
+ Parsed arguments
105
+ """
106
+ parser = argparse.ArgumentParser(description="Setup Conda environment for SFI")
107
+ parser.add_argument(
108
+ "mirror",
109
+ type=str,
110
+ choices=["tsinghua", "ustc", "bsfu", "aliyun"],
111
+ nargs="?",
112
+ default="tsinghua",
113
+ help="Conda mirror to use",
114
+ )
115
+ parser.add_argument("-d", "--debug", help="Debug mode", action="store_true")
116
+
117
+ return parser.parse_args()
118
+
119
+
120
+ def main() -> None:
121
+ """Main entry point for condasetup CLI."""
122
+ args = parse_args()
123
+
124
+ if args.debug:
125
+ logger.setLevel(logging.DEBUG)
126
+
127
+ set_conda_mirror(args.mirror)
128
+
129
+
130
+ if __name__ == "__main__":
131
+ main()
@@ -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__)
@@ -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"