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.
Files changed (55) hide show
  1. {pysfi-0.1.7.dist-info → pysfi-0.1.11.dist-info}/METADATA +11 -9
  2. pysfi-0.1.11.dist-info/RECORD +60 -0
  3. pysfi-0.1.11.dist-info/entry_points.txt +28 -0
  4. sfi/__init__.py +1 -1
  5. sfi/alarmclock/alarmclock.py +40 -40
  6. sfi/bumpversion/__init__.py +1 -1
  7. sfi/cleanbuild/cleanbuild.py +155 -0
  8. sfi/condasetup/condasetup.py +116 -0
  9. sfi/docscan/__init__.py +1 -1
  10. sfi/docscan/docscan.py +407 -103
  11. sfi/docscan/docscan_gui.py +1282 -596
  12. sfi/docscan/lang/eng.py +152 -0
  13. sfi/docscan/lang/zhcn.py +170 -0
  14. sfi/filedate/filedate.py +185 -112
  15. sfi/gittool/__init__.py +2 -0
  16. sfi/gittool/gittool.py +401 -0
  17. sfi/llmclient/llmclient.py +592 -0
  18. sfi/llmquantize/llmquantize.py +480 -0
  19. sfi/llmserver/llmserver.py +335 -0
  20. sfi/makepython/makepython.py +31 -30
  21. sfi/pdfsplit/pdfsplit.py +173 -173
  22. sfi/pyarchive/pyarchive.py +418 -0
  23. sfi/pyembedinstall/pyembedinstall.py +629 -0
  24. sfi/pylibpack/__init__.py +0 -0
  25. sfi/pylibpack/pylibpack.py +1457 -0
  26. sfi/pylibpack/rules/numpy.json +22 -0
  27. sfi/pylibpack/rules/pymupdf.json +10 -0
  28. sfi/pylibpack/rules/pyqt5.json +19 -0
  29. sfi/pylibpack/rules/pyside2.json +23 -0
  30. sfi/pylibpack/rules/scipy.json +23 -0
  31. sfi/pylibpack/rules/shiboken2.json +24 -0
  32. sfi/pyloadergen/pyloadergen.py +512 -227
  33. sfi/pypack/__init__.py +0 -0
  34. sfi/pypack/pypack.py +1142 -0
  35. sfi/pyprojectparse/__init__.py +0 -0
  36. sfi/pyprojectparse/pyprojectparse.py +500 -0
  37. sfi/pysourcepack/pysourcepack.py +308 -0
  38. sfi/quizbase/__init__.py +0 -0
  39. sfi/quizbase/quizbase.py +828 -0
  40. sfi/quizbase/quizbase_gui.py +987 -0
  41. sfi/regexvalidate/__init__.py +0 -0
  42. sfi/regexvalidate/regex_help.html +284 -0
  43. sfi/regexvalidate/regexvalidate.py +468 -0
  44. sfi/taskkill/taskkill.py +0 -2
  45. sfi/workflowengine/__init__.py +0 -0
  46. sfi/workflowengine/workflowengine.py +444 -0
  47. pysfi-0.1.7.dist-info/RECORD +0 -31
  48. pysfi-0.1.7.dist-info/entry_points.txt +0 -15
  49. sfi/embedinstall/embedinstall.py +0 -418
  50. sfi/projectparse/projectparse.py +0 -152
  51. sfi/pypacker/fspacker.py +0 -91
  52. {pysfi-0.1.7.dist-info → pysfi-0.1.11.dist-info}/WHEEL +0 -0
  53. /sfi/{embedinstall → docscan/lang}/__init__.py +0 -0
  54. /sfi/{projectparse → llmquantize}/__init__.py +0 -0
  55. /sfi/{pypacker → pyembedinstall}/__init__.py +0 -0
@@ -0,0 +1,308 @@
1
+ """Source code packaging tool for projects defined in projects.json."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import logging
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ __version__ = "0.1.0"
11
+ __all__ = [
12
+ "pack_project",
13
+ ]
14
+
15
+ cwd = Path.cwd()
16
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Default directories and files to exclude during packaging
20
+ DEFAULT_EXCLUDE = {
21
+ "__pycache__",
22
+ "*.pyc",
23
+ "*.pyo",
24
+ ".pytest_cache",
25
+ ".benchmarks",
26
+ "tests",
27
+ ".git",
28
+ ".gitignore",
29
+ "dist",
30
+ "build",
31
+ "*.egg-info",
32
+ "node_modules",
33
+ ".idea",
34
+ "*.log",
35
+ }
36
+
37
+ # Default files to include in packaging
38
+ DEFAULT_INCLUDE = {
39
+ "*.py",
40
+ "README.md",
41
+ "LICENSE",
42
+ "pyproject.toml",
43
+ }
44
+
45
+
46
+ def should_exclude(path: Path, exclude_patterns: set[str]) -> bool:
47
+ """Check if a path should be excluded based on patterns.
48
+
49
+ Args:
50
+ path: Path to check
51
+ exclude_patterns: Set of patterns to exclude
52
+
53
+ Returns:
54
+ True if path should be excluded, False otherwise
55
+ """
56
+ for pattern in exclude_patterns:
57
+ if pattern.startswith("*"):
58
+ # Wildcard pattern (e.g., "*.pyc")
59
+ if path.match(pattern):
60
+ return True
61
+ else:
62
+ # Exact name pattern
63
+ if path.name == pattern:
64
+ return True
65
+ if path.parts:
66
+ # Check if any parent directory matches
67
+ for part in path.parts:
68
+ if part == pattern:
69
+ return True
70
+ return False
71
+
72
+
73
+ def should_include(
74
+ path: Path, include_patterns: set[str], exclude_patterns: set[str]
75
+ ) -> bool:
76
+ """Check if a path should be included based on include and exclude patterns.
77
+
78
+ Args:
79
+ path: Path to check
80
+ include_patterns: Set of patterns to include
81
+ exclude_patterns: Set of patterns to exclude
82
+
83
+ Returns:
84
+ True if path should be included, False otherwise
85
+ """
86
+ # Check if should be excluded first
87
+ if should_exclude(path, exclude_patterns):
88
+ return False
89
+
90
+ # If no include patterns, include everything not excluded
91
+ if not include_patterns:
92
+ return True
93
+
94
+ # Check if matches any include pattern
95
+ for pattern in include_patterns:
96
+ if pattern.startswith("*"):
97
+ # Wildcard pattern (e.g., "*.py")
98
+ if path.match(pattern):
99
+ return True
100
+ else:
101
+ # Exact name pattern
102
+ if path.name == pattern:
103
+ return True
104
+ if path.is_dir() and path.name == pattern:
105
+ # Include directory and all its contents
106
+ return True
107
+
108
+ return False
109
+
110
+
111
+ def _check_project_path(project_path: Path) -> bool:
112
+ if not project_path.exists():
113
+ logger.error(f"Project directory {project_path} does not exist")
114
+ return False
115
+
116
+ if not project_path.is_dir():
117
+ logger.error(f"{project_path} is not a directory")
118
+ return False
119
+
120
+ return True
121
+
122
+
123
+ def pack_project(
124
+ base_dir: Path,
125
+ projects: dict,
126
+ project_name: str,
127
+ include_patterns: set[str] | None = None,
128
+ exclude_patterns: set[str] | None = None,
129
+ ) -> bool:
130
+ """Pack project source code and resources to dist/src directory.
131
+
132
+ Args:
133
+ base_dir: Base directory containing project folders
134
+ project_name: Name of the project to pack
135
+ include_patterns: File patterns to include (default: DEFAULT_INCLUDE)
136
+ exclude_patterns: File patterns to exclude (default: DEFAULT_EXCLUDE)
137
+
138
+ Returns:
139
+ True if packing succeeded, False otherwise
140
+ """
141
+ logger.debug(f"Start packing project: {project_name}")
142
+
143
+ if project_name not in projects:
144
+ logger.error(f"Project '{project_name}' not found in projects.json")
145
+ return False
146
+
147
+ if not project_name:
148
+ logger.error("Project name cannot be empty")
149
+ return False
150
+
151
+ project_path = base_dir if len(projects) == 1 else base_dir / project_name
152
+ output_dir = base_dir / "dist" / "src" / project_name
153
+
154
+ logger.debug(f"Project path: {project_path}, project_name: {project_name}")
155
+ if not _check_project_path(project_path):
156
+ return False
157
+
158
+ output_dir.mkdir(parents=True, exist_ok=True)
159
+
160
+ # Use default patterns if not specified
161
+ if include_patterns is None:
162
+ include_patterns = DEFAULT_INCLUDE
163
+ if exclude_patterns is None:
164
+ exclude_patterns = DEFAULT_EXCLUDE
165
+
166
+ # Copy files
167
+ copied_files = 0
168
+ copied_dirs = 0
169
+
170
+ logger.info(f"Packing project '{project_name}' to {output_dir}")
171
+
172
+ try:
173
+ for item in project_path.iterdir():
174
+ if item.is_file():
175
+ # For files, check if should be included
176
+ if should_include(item, include_patterns, exclude_patterns):
177
+ dest_file = output_dir / item.name
178
+ shutil.copy2(item, dest_file)
179
+ logger.debug(f"Copied file: {item.name}")
180
+ copied_files += 1
181
+ elif item.is_dir() and not should_exclude(item, exclude_patterns):
182
+ # Copy directory recursively with exclude patterns
183
+ dest_dir = output_dir / item.name
184
+ if dest_dir.exists():
185
+ shutil.rmtree(dest_dir)
186
+ shutil.copytree(
187
+ item, dest_dir, ignore=shutil.ignore_patterns(*DEFAULT_EXCLUDE)
188
+ )
189
+ logger.debug(f"Copied directory: {item.name}")
190
+ copied_dirs += 1
191
+
192
+ logger.info(
193
+ f"Successfully packed {project_name}: {copied_files} files, {copied_dirs} directories"
194
+ )
195
+ return True
196
+
197
+ except Exception as e:
198
+ logger.error(f"Error packing project {project_name}: {e}")
199
+ return False
200
+
201
+
202
+ def create_parser() -> argparse.ArgumentParser:
203
+ """Create and return an argument parser for the PySourcePack tool."""
204
+ parser = argparse.ArgumentParser(
205
+ description="PySourcePack - Source code packaging tool",
206
+ epilog="Pack source code and resources for projects defined in projects.json",
207
+ )
208
+ parser.add_argument(
209
+ "directory",
210
+ default=str(cwd),
211
+ nargs="?",
212
+ help="Base directory containing project folders (default: current directory)",
213
+ )
214
+ parser.add_argument(
215
+ "-p",
216
+ "--project",
217
+ default=None,
218
+ help="Project name to pack (default: pack all projects)",
219
+ )
220
+ parser.add_argument(
221
+ "-l",
222
+ "--list",
223
+ action="store_true",
224
+ help="List all available projects",
225
+ )
226
+ parser.add_argument(
227
+ "--include",
228
+ nargs="*",
229
+ default=None,
230
+ help="File patterns to include (default: *.py, README.md, pyproject.toml)",
231
+ )
232
+ parser.add_argument(
233
+ "--exclude",
234
+ nargs="*",
235
+ default=None,
236
+ help="File patterns to exclude (default: __pycache__, *.pyc, tests, .benchmarks, etc.)",
237
+ )
238
+ parser.add_argument(
239
+ "--debug",
240
+ "-d",
241
+ action="store_true",
242
+ help="Enable debug mode",
243
+ )
244
+ return parser
245
+
246
+
247
+ def main() -> None:
248
+ from sfi.pyprojectparse.pyprojectparse import Solution
249
+
250
+ parser = create_parser()
251
+ args = parser.parse_args()
252
+
253
+ if args.debug:
254
+ logger.setLevel(logging.DEBUG)
255
+
256
+ # Load projects file
257
+ base_dir = Path(args.directory)
258
+ project_json = base_dir / "projects.json"
259
+ projects = Solution.from_directory(base_dir, recursive=True, update=True).projects
260
+
261
+ if not project_json.exists():
262
+ logger.error("Failed to create projects.json")
263
+ return
264
+
265
+ if not projects:
266
+ logger.error("No projects found in projects.json")
267
+ return
268
+
269
+ if args.list:
270
+ logger.info("Available projects:")
271
+ for info in projects.values():
272
+ logger.info(info)
273
+ return
274
+
275
+ # Process include and exclude patterns
276
+ include_patterns = set(args.include) if args.include else None
277
+ exclude_patterns = set(args.exclude) if args.exclude else None
278
+
279
+ logger.debug(
280
+ f"Start packing, config: base_dir={base_dir}, project_name={args.project}, include_patterns={include_patterns}, exclude_patterns={exclude_patterns}"
281
+ )
282
+ # Pack specified project or all projects
283
+ if args.project:
284
+ if pack_project(
285
+ base_dir=base_dir,
286
+ projects=projects,
287
+ project_name=args.project,
288
+ include_patterns=include_patterns,
289
+ exclude_patterns=exclude_patterns,
290
+ ):
291
+ logger.info(f"Packed project '{args.project}' successfully")
292
+ else:
293
+ logger.error(f"Failed to pack project '{args.project}'")
294
+ return
295
+
296
+ # Pack all projects
297
+ logger.info("Packing all projects...")
298
+ success_count = 0
299
+ for project_name in projects:
300
+ if pack_project(
301
+ base_dir=base_dir,
302
+ projects=projects,
303
+ project_name=project_name,
304
+ include_patterns=include_patterns,
305
+ exclude_patterns=exclude_patterns,
306
+ ):
307
+ success_count += 1
308
+ logger.info(f"Packed {success_count}/{len(projects)} projects successfully")
File without changes