"""Demo script showing how to use `pyinstallerguiwrapper.spec_editor` to inspect and modify a .spec AST. Usage: run from repository root: python tools/spec_editor_demo.py It uses a small embedded sample spec (the same used previously) to show the API and writes a modified .spec to a temp file named `modified_demo.spec`. """ import pathlib import tempfile from pyinstallerguiwrapper import spec_editor SAMPLE_SPEC = '''# sample block_cipher = None a = Analysis( pathex=['pyucc', '.', 'external\\python-tkinter-logger', 'external\\python-resource-monitor'], binaries=[], datas=[('PyUcc.ico', '.'), ('external\\python-tkinter-logger\\tkinter_logger.py', '.'), ('external\\python-resource-monitor\\resource_monitor.py', '.')], hiddenimports=['tkinter_logger','resource_monitor','pyucc.core.differ'], hookspath=[], runtime_hooks=[], excludes=[], cipher=block_cipher, noarchive=False, scripts=['pyucc\\__main__.py'] ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='PyUcc', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, icon='PyUcc.ico', exclude_binaries=True) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True, upx_exclude=[], name='PyUcc') ''' def run_demo(): print('Parsing embedded sample spec...') tree = spec_editor.parse_spec_from_string(SAMPLE_SPEC) # Note: helper below expects ast.Module; parse_spec_to_ast_from_string returns that print('Summary before change:') print(spec_editor.inspect_spec_summary(tree)) # Add a hiddenimport entry (demonstrate modification) print('\nAdding hiddenimport "my.added.module" to Analysis...') # Retrieve existing hiddenimports node analysis_calls = spec_editor.find_calls(tree, 'Analysis') if analysis_calls: call = analysis_calls[0] # Build new hiddenimports list by inspecting existing or creating fresh existing_node = spec_editor.get_keyword_value(call, 'hiddenimports') if existing_node is None: new_list = ['my.added.module'] else: py_existing = spec_editor.ast_node_to_python(existing_node) if isinstance(py_existing, list): new_list = py_existing + ['my.added.module'] else: new_list = ['my.added.module'] spec_editor.set_call_keyword(tree, 'Analysis', 'hiddenimports', new_list) print('Modification applied.') print('\nSummary after change:') print(spec_editor.inspect_spec_summary(tree)) # Write out modified spec out_path = pathlib.Path('modified_demo.spec') src = spec_editor.ast_to_source(tree) spec_editor.write_source_to_file(src, str(out_path)) print(f'Wrote modified spec to: {out_path.resolve()}') def ast_unparse(node): import ast if hasattr(ast, 'unparse'): return ast.unparse(node) else: try: import astor return astor.to_source(node) except Exception: return '' if __name__ == '__main__': run_demo()