pysfi 0.1.7__py3-none-any.whl → 0.1.10__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.10.dist-info}/METADATA +5 -3
- pysfi-0.1.10.dist-info/RECORD +39 -0
- {pysfi-0.1.7.dist-info → pysfi-0.1.10.dist-info}/entry_points.txt +4 -1
- sfi/__init__.py +1 -1
- sfi/bumpversion/__init__.py +1 -1
- 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/embedinstall/embedinstall.py +77 -17
- sfi/makepython/makepython.py +29 -28
- sfi/pdfsplit/pdfsplit.py +173 -173
- sfi/pylibpack/__init__.py +0 -0
- sfi/pylibpack/pylibpack.py +913 -0
- sfi/pyloadergen/pyloadergen.py +697 -111
- sfi/pypack/__init__.py +0 -0
- sfi/pypack/pypack.py +791 -0
- sfi/pysourcepack/pysourcepack.py +369 -0
- sfi/workflowengine/__init__.py +0 -0
- sfi/workflowengine/workflowengine.py +444 -0
- pysfi-0.1.7.dist-info/RECORD +0 -31
- sfi/pypacker/fspacker.py +0 -91
- {pysfi-0.1.7.dist-info → pysfi-0.1.10.dist-info}/WHEEL +0 -0
- /sfi/{pypacker → docscan/lang}/__init__.py +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"""Source code packaging tool for projects defined in projects.json."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
cwd = Path.cwd()
|
|
15
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# Default directories and files to exclude during packaging
|
|
19
|
+
DEFAULT_EXCLUDE = {
|
|
20
|
+
"__pycache__",
|
|
21
|
+
"*.pyc",
|
|
22
|
+
"*.pyo",
|
|
23
|
+
".pytest_cache",
|
|
24
|
+
".benchmarks",
|
|
25
|
+
"tests",
|
|
26
|
+
".git",
|
|
27
|
+
".gitignore",
|
|
28
|
+
"dist",
|
|
29
|
+
"build",
|
|
30
|
+
"*.egg-info",
|
|
31
|
+
"node_modules",
|
|
32
|
+
".idea",
|
|
33
|
+
"*.log",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Default files to include in packaging
|
|
37
|
+
DEFAULT_INCLUDE = {
|
|
38
|
+
"*.py",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"pyproject.toml",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_projects(projects_file: Path) -> dict:
|
|
46
|
+
"""Load projects from JSON file.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
projects_file: Path to projects.json file
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Dictionary containing project information
|
|
53
|
+
"""
|
|
54
|
+
if not projects_file.exists():
|
|
55
|
+
logger.error(f"Projects file {projects_file} does not exist")
|
|
56
|
+
return {}
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
with projects_file.open("r", encoding="utf-8") as f:
|
|
60
|
+
return json.load(f)
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.error(f"Error loading projects from {projects_file}: {e}")
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def list_projects(projects: dict) -> None:
|
|
67
|
+
"""List all available projects.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
projects: Dictionary containing project information
|
|
71
|
+
"""
|
|
72
|
+
if not projects:
|
|
73
|
+
logger.warning("No projects found")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
logger.info("Available projects:")
|
|
77
|
+
for name, info in sorted(projects.items()):
|
|
78
|
+
version = info.get("version", "N/A")
|
|
79
|
+
description = info.get("description", "No description")
|
|
80
|
+
logger.info(f" - {name} (v{version}): {description}")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def should_exclude(path: Path, exclude_patterns: set[str]) -> bool:
|
|
84
|
+
"""Check if a path should be excluded based on patterns.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
path: Path to check
|
|
88
|
+
exclude_patterns: Set of patterns to exclude
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True if path should be excluded, False otherwise
|
|
92
|
+
"""
|
|
93
|
+
for pattern in exclude_patterns:
|
|
94
|
+
if pattern.startswith("*"):
|
|
95
|
+
# Wildcard pattern (e.g., "*.pyc")
|
|
96
|
+
if path.match(pattern):
|
|
97
|
+
return True
|
|
98
|
+
else:
|
|
99
|
+
# Exact name pattern
|
|
100
|
+
if path.name == pattern:
|
|
101
|
+
return True
|
|
102
|
+
if path.parts:
|
|
103
|
+
# Check if any parent directory matches
|
|
104
|
+
for part in path.parts:
|
|
105
|
+
if part == pattern:
|
|
106
|
+
return True
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def should_include(path: Path, include_patterns: set[str], exclude_patterns: set[str]) -> bool:
|
|
111
|
+
"""Check if a path should be included based on include and exclude patterns.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
path: Path to check
|
|
115
|
+
include_patterns: Set of patterns to include
|
|
116
|
+
exclude_patterns: Set of patterns to exclude
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if path should be included, False otherwise
|
|
120
|
+
"""
|
|
121
|
+
# Check if should be excluded first
|
|
122
|
+
if should_exclude(path, exclude_patterns):
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
# If no include patterns, include everything not excluded
|
|
126
|
+
if not include_patterns:
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
# Check if matches any include pattern
|
|
130
|
+
for pattern in include_patterns:
|
|
131
|
+
if pattern.startswith("*"):
|
|
132
|
+
# Wildcard pattern (e.g., "*.py")
|
|
133
|
+
if path.match(pattern):
|
|
134
|
+
return True
|
|
135
|
+
else:
|
|
136
|
+
# Exact name pattern
|
|
137
|
+
if path.name == pattern:
|
|
138
|
+
return True
|
|
139
|
+
if path.is_dir() and path.name == pattern:
|
|
140
|
+
# Include directory and all its contents
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def pack_project(
|
|
147
|
+
project_name: str,
|
|
148
|
+
projects: dict,
|
|
149
|
+
base_dir: Path,
|
|
150
|
+
output_dir: Path | None = None,
|
|
151
|
+
include_patterns: set[str] | None = None,
|
|
152
|
+
exclude_patterns: set[str] | None = None,
|
|
153
|
+
) -> bool:
|
|
154
|
+
"""Pack project source code and resources to dist/src directory.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
project_name: Name of the project to pack
|
|
158
|
+
projects: Dictionary containing project information
|
|
159
|
+
base_dir: Base directory containing project folders
|
|
160
|
+
output_dir: Output directory (default: project's dist/src)
|
|
161
|
+
include_patterns: File patterns to include (default: DEFAULT_INCLUDE)
|
|
162
|
+
exclude_patterns: File patterns to exclude (default: DEFAULT_EXCLUDE)
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
True if packing succeeded, False otherwise
|
|
166
|
+
"""
|
|
167
|
+
if project_name not in projects:
|
|
168
|
+
logger.error(f"Project '{project_name}' not found in projects.json")
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
project_path = base_dir / project_name
|
|
172
|
+
|
|
173
|
+
# If project directory doesn't exist in base_dir, try to search for it
|
|
174
|
+
if not project_path.exists():
|
|
175
|
+
logger.debug(f"Project directory {project_path} does not exist in base_dir")
|
|
176
|
+
# Search in parent directories
|
|
177
|
+
search_dirs = [base_dir, base_dir.parent, base_dir.parent.parent]
|
|
178
|
+
for search_dir in search_dirs:
|
|
179
|
+
candidate = search_dir / project_name
|
|
180
|
+
if candidate.exists() and candidate.is_dir():
|
|
181
|
+
logger.info(f"Found project at {candidate}")
|
|
182
|
+
project_path = candidate
|
|
183
|
+
break
|
|
184
|
+
|
|
185
|
+
if not project_path.exists():
|
|
186
|
+
logger.error(f"Project directory for '{project_name}' not found")
|
|
187
|
+
logger.debug(f"Searched in: {base_dir}, {base_dir.parent}, {base_dir.parent.parent}")
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
if not project_path.is_dir():
|
|
191
|
+
logger.error(f"{project_path} is not a directory")
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
# Determine output directory
|
|
195
|
+
output_dir = project_path / "dist" / "src" if output_dir is None else Path(output_dir) / project_name
|
|
196
|
+
|
|
197
|
+
# Create output directory
|
|
198
|
+
try:
|
|
199
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.error(f"Error creating output directory {output_dir}: {e}")
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
# Use default patterns if not specified
|
|
205
|
+
if include_patterns is None:
|
|
206
|
+
include_patterns = DEFAULT_INCLUDE
|
|
207
|
+
if exclude_patterns is None:
|
|
208
|
+
exclude_patterns = DEFAULT_EXCLUDE
|
|
209
|
+
|
|
210
|
+
# Copy files
|
|
211
|
+
copied_files = 0
|
|
212
|
+
copied_dirs = 0
|
|
213
|
+
|
|
214
|
+
logger.info(f"Packing project '{project_name}' to {output_dir}")
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
for item in project_path.iterdir():
|
|
218
|
+
if item.is_file():
|
|
219
|
+
# For files, check if should be included
|
|
220
|
+
if should_include(item, include_patterns, exclude_patterns):
|
|
221
|
+
dest_file = output_dir / item.name
|
|
222
|
+
shutil.copy2(item, dest_file)
|
|
223
|
+
logger.debug(f"Copied file: {item.name}")
|
|
224
|
+
copied_files += 1
|
|
225
|
+
elif item.is_dir():
|
|
226
|
+
# For directories, exclude only if in exclude patterns
|
|
227
|
+
if not should_exclude(item, exclude_patterns):
|
|
228
|
+
# Copy directory recursively with exclude patterns
|
|
229
|
+
dest_dir = output_dir / item.name
|
|
230
|
+
if dest_dir.exists():
|
|
231
|
+
shutil.rmtree(dest_dir)
|
|
232
|
+
shutil.copytree(item, dest_dir, ignore=shutil.ignore_patterns(*DEFAULT_EXCLUDE))
|
|
233
|
+
logger.debug(f"Copied directory: {item.name}")
|
|
234
|
+
copied_dirs += 1
|
|
235
|
+
|
|
236
|
+
logger.info(f"Successfully packed {project_name}: {copied_files} files, {copied_dirs} directories")
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
except Exception as e:
|
|
240
|
+
logger.error(f"Error packing project {project_name}: {e}")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def main():
|
|
245
|
+
parser = argparse.ArgumentParser(
|
|
246
|
+
description="PySourcePack - Source code packaging tool",
|
|
247
|
+
epilog="Pack source code and resources for projects defined in projects.json",
|
|
248
|
+
)
|
|
249
|
+
parser.add_argument(
|
|
250
|
+
"-i",
|
|
251
|
+
"--input",
|
|
252
|
+
default="projects.json",
|
|
253
|
+
help="Input projects.json file path (default: projects.json)",
|
|
254
|
+
)
|
|
255
|
+
parser.add_argument(
|
|
256
|
+
"-d",
|
|
257
|
+
"--directory",
|
|
258
|
+
default=str(cwd),
|
|
259
|
+
help="Base directory containing project folders (default: current directory)",
|
|
260
|
+
)
|
|
261
|
+
parser.add_argument(
|
|
262
|
+
"-o",
|
|
263
|
+
"--output-dir",
|
|
264
|
+
default=None,
|
|
265
|
+
help="Output directory for packed files (default: <project>/dist/src)",
|
|
266
|
+
)
|
|
267
|
+
parser.add_argument(
|
|
268
|
+
"-p",
|
|
269
|
+
"--project",
|
|
270
|
+
default=None,
|
|
271
|
+
help="Project name to pack (default: pack all projects)",
|
|
272
|
+
)
|
|
273
|
+
parser.add_argument(
|
|
274
|
+
"-l",
|
|
275
|
+
"--list",
|
|
276
|
+
action="store_true",
|
|
277
|
+
help="List all available projects",
|
|
278
|
+
)
|
|
279
|
+
parser.add_argument(
|
|
280
|
+
"--include",
|
|
281
|
+
nargs="*",
|
|
282
|
+
default=None,
|
|
283
|
+
help="File patterns to include (default: *.py, README.md, pyproject.toml)",
|
|
284
|
+
)
|
|
285
|
+
parser.add_argument(
|
|
286
|
+
"--exclude",
|
|
287
|
+
nargs="*",
|
|
288
|
+
default=None,
|
|
289
|
+
help="File patterns to exclude (default: __pycache__, *.pyc, tests, .benchmarks, etc.)",
|
|
290
|
+
)
|
|
291
|
+
parser.add_argument(
|
|
292
|
+
"--debug",
|
|
293
|
+
action="store_true",
|
|
294
|
+
help="Enable debug mode",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
args = parser.parse_args()
|
|
298
|
+
|
|
299
|
+
if args.debug:
|
|
300
|
+
logger.setLevel(logging.DEBUG)
|
|
301
|
+
|
|
302
|
+
# Load projects file
|
|
303
|
+
projects_file = Path(args.input)
|
|
304
|
+
if not projects_file.is_absolute():
|
|
305
|
+
# Relative to base directory
|
|
306
|
+
base_dir = Path(args.directory)
|
|
307
|
+
projects_file = base_dir / args.input
|
|
308
|
+
else:
|
|
309
|
+
base_dir = Path(args.directory)
|
|
310
|
+
|
|
311
|
+
# If projects_file is relative and not found, try to find it in current directory
|
|
312
|
+
if not projects_file.exists():
|
|
313
|
+
logger.warning(f"Projects file {projects_file} not found, trying current directory...")
|
|
314
|
+
alt_file = Path.cwd() / args.input
|
|
315
|
+
if alt_file.exists():
|
|
316
|
+
projects_file = alt_file
|
|
317
|
+
base_dir = alt_file.parent
|
|
318
|
+
logger.info(f"Found projects file at {projects_file}")
|
|
319
|
+
|
|
320
|
+
# Check if user explicitly specified the -d parameter
|
|
321
|
+
explicit_dir = any(arg in ["-d", "--directory"] for arg in sys.argv)
|
|
322
|
+
if not explicit_dir and projects_file.exists():
|
|
323
|
+
# If -d was not explicitly specified, set base_dir to projects_file's parent
|
|
324
|
+
base_dir = projects_file.parent
|
|
325
|
+
logger.debug(f"Auto-set base_dir to {base_dir} (parent of projects.json)")
|
|
326
|
+
|
|
327
|
+
projects = load_projects(projects_file)
|
|
328
|
+
if not projects:
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
# List projects if requested
|
|
332
|
+
if args.list:
|
|
333
|
+
list_projects(projects)
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
# Process include and exclude patterns
|
|
337
|
+
include_patterns = set(args.include) if args.include else None
|
|
338
|
+
exclude_patterns = set(args.exclude) if args.exclude else None
|
|
339
|
+
|
|
340
|
+
# Pack specified project or all projects
|
|
341
|
+
if args.project:
|
|
342
|
+
success = pack_project(
|
|
343
|
+
args.project,
|
|
344
|
+
projects,
|
|
345
|
+
base_dir,
|
|
346
|
+
output_dir=Path(args.output_dir) if args.output_dir else None,
|
|
347
|
+
include_patterns=include_patterns,
|
|
348
|
+
exclude_patterns=exclude_patterns,
|
|
349
|
+
)
|
|
350
|
+
if success:
|
|
351
|
+
logger.info(f"Packed project '{args.project}' successfully")
|
|
352
|
+
else:
|
|
353
|
+
logger.error(f"Failed to pack project '{args.project}'")
|
|
354
|
+
else:
|
|
355
|
+
# Pack all projects
|
|
356
|
+
logger.info("Packing all projects...")
|
|
357
|
+
success_count = 0
|
|
358
|
+
for project_name in projects:
|
|
359
|
+
if pack_project(
|
|
360
|
+
project_name,
|
|
361
|
+
projects,
|
|
362
|
+
base_dir,
|
|
363
|
+
output_dir=Path(args.output_dir) if args.output_dir else None,
|
|
364
|
+
include_patterns=include_patterns,
|
|
365
|
+
exclude_patterns=exclude_patterns,
|
|
366
|
+
):
|
|
367
|
+
success_count += 1
|
|
368
|
+
|
|
369
|
+
logger.info(f"Packed {success_count}/{len(projects)} projects successfully")
|
|
File without changes
|