4444
4545
4646DEFAULT_SETTINGS = {
47+ # Inputs can be explicit paths OR discovered via globbing when glob_mode=True
4748 "inputs" : ["main.py" ],
49+ "inputs_glob" : ["**/*.py" ], # evaluated within the workspace when glob_mode=True
50+ "glob_mode" : False ,
51+
52+ # Output directory (relative to workspace by default)
4853 "out_dir" : "obfuscated" ,
54+
55+ # Recursion and symlink handling
4956 "recursive" : True ,
50- "exclude_patterns" : ["venv/**" , "build/**" , "dist/**" , "tests/**" , "__pycache__/**" ],
57+ "follow_symlinks" : False ,
58+
59+ # Exclusions (broad by default; keep overkill to avoid polluting the output)
60+ "exclude_patterns" : [
61+ "venv/**" ,
62+ "build/**" ,
63+ "dist/**" ,
64+ "tests/**" ,
65+ "__pycache__/**" ,
66+ ".git/**" ,
67+ ".hg/**" ,
68+ ".svn/**" ,
69+ ".idea/**" ,
70+ ".vscode/**" ,
71+ "node_modules/**" ,
72+ "*.egg-info/**" ,
73+ ],
74+
75+ # Manifest file path; when manifest_auto=True a manifest may be generated from inputs/excludes
5176 "manifest" : "" ,
52- "advanced" : {},
77+ "manifest_auto" : False ,
78+
79+ # Advanced options forwarded to `pyarmor gen` (None values are ignored)
80+ # Keep keys here for convenience; they are not applied unless set by the user.
81+ "advanced" : {
82+ "platform" : None , # e.g., "linux.x86_64", "windows.x86_64" or comma-separated
83+ "obf-code" : None , # 0|1|2
84+ "obf-module" : None , # 0|1
85+ "restrict-mode" : None , # 0|1|2
86+ "mix-str" : None , # True to enable string mixing
87+ "keep-runtime" : None , # True to keep runtime package alongside outputs
88+ "no-cross-protection" : None , # True to disable cross protection
89+ "bootstrap-code" : None , # Python statements injected at startup
90+ "enable-suffix" : None , # True to append obfuscated suffix
91+ "enable-assert-hook" : None , # True to protect assert
92+ "plugin" : None , # Optional PyArmor plugin name
93+ },
94+
95+ # Dry run will only log the command and not execute
5396 "dry_run" : False ,
97+
98+ # Execution controls
99+ "timeout_s" : 1800 , # Max seconds for obfuscation command
100+
101+ # Workspace controls
102+ "workspace" : "" , # Optional: override selected workspace (absolute or relative)
103+ "post_switch_workspace" : True , # Switch to out_dir on success
54104}
55105
56106# -----------------------------
@@ -215,6 +265,12 @@ def on_pre_compile(self, ctx: PreCompileContext) -> None:
215265 manifest : str = subcfg .get ("manifest" , DEFAULT_SETTINGS ["manifest" ]) or ""
216266 advanced : dict [str , Any ] = subcfg .get ("advanced" , DEFAULT_SETTINGS ["advanced" ]) or {}
217267 dry_run : bool = bool (subcfg .get ("dry_run" , DEFAULT_SETTINGS ["dry_run" ]))
268+ # Overkill/versatile options
269+ glob_mode : bool = bool (subcfg .get ("glob_mode" , DEFAULT_SETTINGS .get ("glob_mode" , False )))
270+ inputs_glob : list [str ] = subcfg .get ("inputs_glob" , DEFAULT_SETTINGS .get ("inputs_glob" , ["**/*.py" ])) or DEFAULT_SETTINGS .get ("inputs_glob" , ["**/*.py" ])
271+ follow_symlinks : bool = bool (subcfg .get ("follow_symlinks" , DEFAULT_SETTINGS .get ("follow_symlinks" , False )))
272+ timeout_s : int = int (subcfg .get ("timeout_s" , DEFAULT_SETTINGS .get ("timeout_s" , 1800 )))
273+ post_switch_workspace : bool = bool (subcfg .get ("post_switch_workspace" , DEFAULT_SETTINGS .get ("post_switch_workspace" , True )))
218274
219275 # Sanitize and resolve paths relative to workspace
220276 ws = sctx .workspace_root
@@ -228,6 +284,43 @@ def on_pre_compile(self, ctx: PreCompileContext) -> None:
228284 sctx .log_warn (f"[pyarmor] Input does not exist: { it } " )
229285 except Exception :
230286 sctx .log_warn (f"[pyarmor] Invalid input: { it } " )
287+ # Optional: discover inputs by globbing inside the workspace
288+ if glob_mode :
289+ discovered : list [str ] = []
290+ try :
291+ # Prefer SDK iter_files when available
292+ try :
293+ it = sctx .iter_files (patterns = list (inputs_glob or ["**/*.py" ]), exclude = list (exclude_patterns or []), enforce_workspace = True )
294+ for p in it :
295+ try :
296+ if p and getattr (p , "is_file" , lambda : False )():
297+ discovered .append (str (p ))
298+ except Exception :
299+ pass
300+ except Exception :
301+ # Fallback to pathlib globbing
302+ for pat in list (inputs_glob or ["**/*.py" ]):
303+ try :
304+ for p in ws .glob (pat ):
305+ try :
306+ if p .is_file ():
307+ rp = str (p )
308+ # Exclude patterns match on full path
309+ if not any (p .match (ex ) for ex in (exclude_patterns or [])):
310+ discovered .append (rp )
311+ except Exception :
312+ pass
313+ except Exception :
314+ pass
315+ except Exception :
316+ pass
317+ if discovered :
318+ # Deduplicate while keeping order
319+ seen = set ()
320+ for x in discovered :
321+ if x not in seen :
322+ seen .add (x )
323+ abs_inputs .append (x )
231324 if not abs_inputs :
232325 sctx .log_warn ("[pyarmor] No valid inputs to obfuscate; skipping" )
233326 return
@@ -308,7 +401,7 @@ def on_pre_compile(self, ctx: PreCompileContext) -> None:
308401 manifest = (str (sctx .safe_path (manifest )) if manifest else None ) if manifest else None ,
309402 advanced = advanced ,
310403 cwd = str (ws ),
311- timeout_s = 1800 ,
404+ timeout_s = timeout_s ,
312405 )
313406 try :
314407 ph .update (1 , "Done" )
@@ -328,8 +421,8 @@ def on_pre_compile(self, ctx: PreCompileContext) -> None:
328421 sctx .log_info (f"[pyarmor] Report saved: { rpt } " )
329422 except Exception :
330423 pass
331- # On success, switch the selected workspace to the obfuscated output directory
332- if ok :
424+ # On success, optionally switch the selected workspace to the obfuscated output directory
425+ if ok and post_switch_workspace :
333426 try :
334427 switched = set_selected_workspace (str (out_path ))
335428 if switched :
@@ -338,6 +431,8 @@ def on_pre_compile(self, ctx: PreCompileContext) -> None:
338431 sctx .log_warn (f"[pyarmor] Workspace switch refused: { out_path } " )
339432 except Exception as e :
340433 sctx .log_warn (f"[pyarmor] Failed to switch workspace: { e } " )
434+ elif ok :
435+ sctx .log_info ("[pyarmor] post_switch_workspace=False; keeping current workspace" )
341436 if not ok :
342437 try :
343438 out = (report .get ("stdout" ) or "" ) + "\n " + (report .get ("stderr" ) or "" )
0 commit comments