PyCodeTools 0.1.0__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.
- pycodetools/__init__.py +104 -0
- pycodetools/cli.py +145 -0
- pycodetools/gui.py +213 -0
- pycodetools-0.1.0.dist-info/METADATA +121 -0
- pycodetools-0.1.0.dist-info/RECORD +8 -0
- pycodetools-0.1.0.dist-info/WHEEL +5 -0
- pycodetools-0.1.0.dist-info/entry_points.txt +3 -0
- pycodetools-0.1.0.dist-info/top_level.txt +1 -0
pycodetools/__init__.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyCodeTools (PCT)
|
|
3
|
+
=================
|
|
4
|
+
|
|
5
|
+
A tiny helper toolkit + CLI for running Python scripts with a few
|
|
6
|
+
extra scripting conveniences, and a GUI for turning them into .exe
|
|
7
|
+
files with PyInstaller.
|
|
8
|
+
|
|
9
|
+
Enabling PCT in a script
|
|
10
|
+
-------------------------
|
|
11
|
+
Put this as the first line of your script::
|
|
12
|
+
|
|
13
|
+
pycodetools = {true}PCT.enable
|
|
14
|
+
|
|
15
|
+
When you run the file with the ``pycodetools`` (or ``pct``) command,
|
|
16
|
+
PCT will detect that line and print a confirmation, then run your
|
|
17
|
+
script with the extra helpers available via::
|
|
18
|
+
|
|
19
|
+
import pycodetools as pct
|
|
20
|
+
|
|
21
|
+
Extra helpers available once enabled
|
|
22
|
+
-------------------------------------
|
|
23
|
+
- pct.title("My App") -> sets the terminal window title
|
|
24
|
+
- pct.clear() -> clears the terminal screen
|
|
25
|
+
- pct.pause(msg="...") -> "Press Enter to continue..." style pause
|
|
26
|
+
- pct.banner("text") -> prints a nice banner
|
|
27
|
+
- pct.color("text", "red") -> returns text wrapped in ANSI color codes
|
|
28
|
+
- pct.timer(seconds) -> countdown in the terminal
|
|
29
|
+
- pct.confirm("Continue?") -> yes/no prompt returning bool
|
|
30
|
+
- pct.enabled -> True if PCT enable line was detected
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
import sys
|
|
37
|
+
import time
|
|
38
|
+
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
|
|
41
|
+
# Set to True by the pycodetools launcher when it detects the enable line.
|
|
42
|
+
# Scripts run directly with `python file.py` will have this as False,
|
|
43
|
+
# since the enable line by itself is just a harmless variable assignment.
|
|
44
|
+
enabled: bool = False
|
|
45
|
+
|
|
46
|
+
_COLORS = {
|
|
47
|
+
"red": "31",
|
|
48
|
+
"green": "32",
|
|
49
|
+
"yellow": "33",
|
|
50
|
+
"blue": "34",
|
|
51
|
+
"magenta": "35",
|
|
52
|
+
"cyan": "36",
|
|
53
|
+
"white": "37",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def title(text: str) -> None:
|
|
58
|
+
"""Set the terminal window title (Windows + most ANSI terminals)."""
|
|
59
|
+
if os.name == "nt":
|
|
60
|
+
os.system(f"title {text}")
|
|
61
|
+
else:
|
|
62
|
+
sys.stdout.write(f"\33]0;{text}\a")
|
|
63
|
+
sys.stdout.flush()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def clear() -> None:
|
|
67
|
+
"""Clear the terminal screen."""
|
|
68
|
+
os.system("cls" if os.name == "nt" else "clear")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def pause(msg: str = "Press Enter to continue...") -> None:
|
|
72
|
+
"""Pause execution until the user presses Enter."""
|
|
73
|
+
input(msg)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def banner(text: str, char: str = "=") -> None:
|
|
77
|
+
"""Print a simple banner around text."""
|
|
78
|
+
line = char * (len(text) + 4)
|
|
79
|
+
print(line)
|
|
80
|
+
print(f"{char} {text} {char}")
|
|
81
|
+
print(line)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def color(text: str, name: str = "white") -> str:
|
|
85
|
+
"""Wrap text in an ANSI color code (returns a string, doesn't print)."""
|
|
86
|
+
code = _COLORS.get(name.lower(), "37")
|
|
87
|
+
return f"\033[{code}m{text}\033[0m"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def timer(seconds: int, label: str = "Starting in") -> None:
|
|
91
|
+
"""Countdown timer printed to the terminal."""
|
|
92
|
+
for i in range(seconds, 0, -1):
|
|
93
|
+
print(f"\r{label} {i}...", end="", flush=True)
|
|
94
|
+
time.sleep(1)
|
|
95
|
+
print(f"\r{label} 0... ")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def confirm(prompt: str = "Continue?") -> bool:
|
|
99
|
+
"""Ask a yes/no question, returns True/False."""
|
|
100
|
+
ans = input(f"{prompt} [y/N]: ").strip().lower()
|
|
101
|
+
return ans in ("y", "yes")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
ENABLE_LINE = "pycodetools = {true}PCT.enable"
|
pycodetools/cli.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command line interface for PyCodeTools.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
pycodetools <file.py> Run a script (checks for the enable line)
|
|
6
|
+
pycodetools run <file.py> Same as above, explicit form
|
|
7
|
+
pycodetools config <file.py> Open the Tkinter build/config GUI
|
|
8
|
+
pct <file.py> Alias for pycodetools
|
|
9
|
+
pct config <file.py> Alias for pycodetools config
|
|
10
|
+
|
|
11
|
+
The enable line
|
|
12
|
+
----------------
|
|
13
|
+
If the first non-blank line of the target file is exactly:
|
|
14
|
+
|
|
15
|
+
pycodetools = {true}PCT.enable
|
|
16
|
+
|
|
17
|
+
PCT will print a confirmation and run the script with the
|
|
18
|
+
`pycodetools` helper module's `enabled` flag set to True, so the
|
|
19
|
+
script can do:
|
|
20
|
+
|
|
21
|
+
import pycodetools as pct
|
|
22
|
+
if pct.enabled:
|
|
23
|
+
pct.banner("PCT features on")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import argparse
|
|
29
|
+
import sys
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from . import ENABLE_LINE
|
|
33
|
+
from . import __version__
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _read_first_meaningful_line(path: Path) -> str:
|
|
37
|
+
with path.open("r", encoding="utf-8", errors="replace") as f:
|
|
38
|
+
for line in f:
|
|
39
|
+
stripped = line.strip()
|
|
40
|
+
if stripped:
|
|
41
|
+
return stripped
|
|
42
|
+
return ""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _run_file(file_path: str) -> int:
|
|
46
|
+
path = Path(file_path)
|
|
47
|
+
if not path.exists():
|
|
48
|
+
print(f"pct: error: no such file: {file_path}", file=sys.stderr)
|
|
49
|
+
return 1
|
|
50
|
+
if path.suffix != ".py":
|
|
51
|
+
print(f"pct: warning: '{file_path}' doesn't end in .py, running anyway")
|
|
52
|
+
|
|
53
|
+
first_line = _read_first_meaningful_line(path)
|
|
54
|
+
is_enabled = first_line == ENABLE_LINE
|
|
55
|
+
|
|
56
|
+
# Set the enabled flag on our own module before the target script
|
|
57
|
+
# imports it, so `import pycodetools as pct; pct.enabled` works.
|
|
58
|
+
import pycodetools as pct_module
|
|
59
|
+
pct_module.enabled = is_enabled
|
|
60
|
+
|
|
61
|
+
if is_enabled:
|
|
62
|
+
print("{true}PCT.enable")
|
|
63
|
+
print("PyCodeTools is now enabled for this run.")
|
|
64
|
+
else:
|
|
65
|
+
print("PyCodeTools: enable line not found, running as a plain script.")
|
|
66
|
+
|
|
67
|
+
print(f"Running: py {path.name}\n" + ("-" * 40))
|
|
68
|
+
|
|
69
|
+
source = path.read_text(encoding="utf-8", errors="replace")
|
|
70
|
+
if is_enabled:
|
|
71
|
+
# The enable line uses PCT's special "{true}PCT.enable" marker
|
|
72
|
+
# syntax, which is not valid Python. Replace just that first
|
|
73
|
+
# line with a real, equivalent assignment so the rest of the
|
|
74
|
+
# file still runs normally.
|
|
75
|
+
lines = source.splitlines(keepends=True)
|
|
76
|
+
for idx, line in enumerate(lines):
|
|
77
|
+
if line.strip() == ENABLE_LINE:
|
|
78
|
+
lines[idx] = "pycodetools = True\n"
|
|
79
|
+
break
|
|
80
|
+
source = "".join(lines)
|
|
81
|
+
|
|
82
|
+
code = compile(source, str(path), "exec")
|
|
83
|
+
|
|
84
|
+
old_argv = sys.argv
|
|
85
|
+
sys.argv = [str(path)]
|
|
86
|
+
try:
|
|
87
|
+
exec(code, {"__name__": "__main__", "__file__": str(path)})
|
|
88
|
+
except SystemExit as e:
|
|
89
|
+
return int(e.code) if isinstance(e.code, int) else 0
|
|
90
|
+
finally:
|
|
91
|
+
sys.argv = old_argv
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _open_config(file_path: str) -> int:
|
|
96
|
+
path = Path(file_path)
|
|
97
|
+
if not path.exists():
|
|
98
|
+
print(f"pct: error: no such file: {file_path}", file=sys.stderr)
|
|
99
|
+
return 1
|
|
100
|
+
|
|
101
|
+
from .gui import launch_config_gui
|
|
102
|
+
launch_config_gui(path)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def main(argv=None) -> int:
|
|
107
|
+
argv = argv if argv is not None else sys.argv[1:]
|
|
108
|
+
|
|
109
|
+
parser = argparse.ArgumentParser(
|
|
110
|
+
prog="pct",
|
|
111
|
+
description="PyCodeTools (PCT) - run and package Python scripts.",
|
|
112
|
+
)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"--version", action="version", version=f"PyCodeTools {__version__}"
|
|
115
|
+
)
|
|
116
|
+
if argv and argv[0] == "--version":
|
|
117
|
+
print(f"PyCodeTools {__version__}")
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
if not argv:
|
|
121
|
+
parser.print_help()
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
if argv[0] == "run":
|
|
125
|
+
if len(argv) < 2:
|
|
126
|
+
print("pct: error: 'run' requires a file argument", file=sys.stderr)
|
|
127
|
+
return 2
|
|
128
|
+
return _run_file(argv[1])
|
|
129
|
+
|
|
130
|
+
if argv[0] == "config":
|
|
131
|
+
if len(argv) < 2:
|
|
132
|
+
print("pct: error: 'config' requires a file argument", file=sys.stderr)
|
|
133
|
+
return 2
|
|
134
|
+
return _open_config(argv[1])
|
|
135
|
+
|
|
136
|
+
if argv[0] in ("-h", "--help"):
|
|
137
|
+
parser.print_help()
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
# Bare `pct file.py`
|
|
141
|
+
return _run_file(argv[0])
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
if __name__ == "__main__":
|
|
145
|
+
sys.exit(main())
|
pycodetools/gui.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tkinter GUI launched by `pct config <file.py>`.
|
|
3
|
+
|
|
4
|
+
Lets you:
|
|
5
|
+
- Pick dependencies from a preset list (or add custom ones) and
|
|
6
|
+
pip-install them.
|
|
7
|
+
- Compile the target script into a standalone .exe using PyInstaller.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import subprocess
|
|
13
|
+
import sys
|
|
14
|
+
import threading
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
import tkinter as tk
|
|
18
|
+
from tkinter import ttk, messagebox, scrolledtext
|
|
19
|
+
|
|
20
|
+
# A starter list of common packages people often need. Not exhaustive -
|
|
21
|
+
# custom packages can be typed in too.
|
|
22
|
+
COMMON_DEPENDENCIES = [
|
|
23
|
+
"requests",
|
|
24
|
+
"numpy",
|
|
25
|
+
"pandas",
|
|
26
|
+
"matplotlib",
|
|
27
|
+
"pillow",
|
|
28
|
+
"flask",
|
|
29
|
+
"django",
|
|
30
|
+
"beautifulsoup4",
|
|
31
|
+
"pygame",
|
|
32
|
+
"opencv-python",
|
|
33
|
+
"scikit-learn",
|
|
34
|
+
"tqdm",
|
|
35
|
+
"pyinstaller",
|
|
36
|
+
"python-dotenv",
|
|
37
|
+
"colorama",
|
|
38
|
+
"rich",
|
|
39
|
+
"sqlalchemy",
|
|
40
|
+
"pytest",
|
|
41
|
+
"pyyaml",
|
|
42
|
+
"click",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class PCTConfigApp:
|
|
47
|
+
def __init__(self, root: tk.Tk, target_file: Path):
|
|
48
|
+
self.root = root
|
|
49
|
+
self.target_file = target_file
|
|
50
|
+
self.root.title(f"PyCodeTools Config - {target_file.name}")
|
|
51
|
+
self.root.geometry("560x600")
|
|
52
|
+
self.root.minsize(480, 500)
|
|
53
|
+
|
|
54
|
+
self._build_ui()
|
|
55
|
+
|
|
56
|
+
# ---------- UI construction ----------
|
|
57
|
+
|
|
58
|
+
def _build_ui(self) -> None:
|
|
59
|
+
pad = {"padx": 10, "pady": 6}
|
|
60
|
+
|
|
61
|
+
header = ttk.Label(
|
|
62
|
+
self.root,
|
|
63
|
+
text=f"Configuring: {self.target_file}",
|
|
64
|
+
font=("Segoe UI", 10, "bold"),
|
|
65
|
+
wraplength=520,
|
|
66
|
+
)
|
|
67
|
+
header.pack(fill="x", **pad)
|
|
68
|
+
|
|
69
|
+
notebook = ttk.Notebook(self.root)
|
|
70
|
+
notebook.pack(fill="both", expand=True, padx=10, pady=6)
|
|
71
|
+
|
|
72
|
+
deps_tab = ttk.Frame(notebook)
|
|
73
|
+
build_tab = ttk.Frame(notebook)
|
|
74
|
+
notebook.add(deps_tab, text="Dependencies")
|
|
75
|
+
notebook.add(build_tab, text="Build .exe")
|
|
76
|
+
|
|
77
|
+
self._build_deps_tab(deps_tab)
|
|
78
|
+
self._build_build_tab(build_tab)
|
|
79
|
+
|
|
80
|
+
# Shared log/output box at the bottom
|
|
81
|
+
log_label = ttk.Label(self.root, text="Output:")
|
|
82
|
+
log_label.pack(anchor="w", padx=10)
|
|
83
|
+
self.log_box = scrolledtext.ScrolledText(self.root, height=10, state="disabled")
|
|
84
|
+
self.log_box.pack(fill="both", expand=False, padx=10, pady=(0, 10))
|
|
85
|
+
|
|
86
|
+
def _build_deps_tab(self, parent: ttk.Frame) -> None:
|
|
87
|
+
ttk.Label(parent, text="Pick dependencies to install:").pack(
|
|
88
|
+
anchor="w", padx=10, pady=(10, 2)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
list_frame = ttk.Frame(parent)
|
|
92
|
+
list_frame.pack(fill="both", expand=True, padx=10, pady=4)
|
|
93
|
+
|
|
94
|
+
self.deps_listbox = tk.Listbox(
|
|
95
|
+
list_frame, selectmode="multiple", height=10, exportselection=False
|
|
96
|
+
)
|
|
97
|
+
for dep in COMMON_DEPENDENCIES:
|
|
98
|
+
self.deps_listbox.insert("end", dep)
|
|
99
|
+
scrollbar = ttk.Scrollbar(
|
|
100
|
+
list_frame, orient="vertical", command=self.deps_listbox.yview
|
|
101
|
+
)
|
|
102
|
+
self.deps_listbox.configure(yscrollcommand=scrollbar.set)
|
|
103
|
+
self.deps_listbox.pack(side="left", fill="both", expand=True)
|
|
104
|
+
scrollbar.pack(side="right", fill="y")
|
|
105
|
+
|
|
106
|
+
custom_frame = ttk.Frame(parent)
|
|
107
|
+
custom_frame.pack(fill="x", padx=10, pady=6)
|
|
108
|
+
ttk.Label(custom_frame, text="Custom package:").pack(side="left")
|
|
109
|
+
self.custom_dep_entry = ttk.Entry(custom_frame)
|
|
110
|
+
self.custom_dep_entry.pack(side="left", fill="x", expand=True, padx=6)
|
|
111
|
+
|
|
112
|
+
install_btn = ttk.Button(
|
|
113
|
+
parent, text="Install Selected", command=self._install_dependencies
|
|
114
|
+
)
|
|
115
|
+
install_btn.pack(pady=6)
|
|
116
|
+
|
|
117
|
+
def _build_build_tab(self, parent: ttk.Frame) -> None:
|
|
118
|
+
ttk.Label(
|
|
119
|
+
parent,
|
|
120
|
+
text="Compile this script into a standalone .exe using PyInstaller.",
|
|
121
|
+
wraplength=500,
|
|
122
|
+
).pack(anchor="w", padx=10, pady=(10, 6))
|
|
123
|
+
|
|
124
|
+
opts_frame = ttk.LabelFrame(parent, text="Options")
|
|
125
|
+
opts_frame.pack(fill="x", padx=10, pady=6)
|
|
126
|
+
|
|
127
|
+
self.onefile_var = tk.BooleanVar(value=True)
|
|
128
|
+
ttk.Checkbutton(
|
|
129
|
+
opts_frame, text="Single file (--onefile)", variable=self.onefile_var
|
|
130
|
+
).pack(anchor="w", padx=8, pady=2)
|
|
131
|
+
|
|
132
|
+
self.console_var = tk.BooleanVar(value=True)
|
|
133
|
+
ttk.Checkbutton(
|
|
134
|
+
opts_frame,
|
|
135
|
+
text="Show console window (uncheck for --noconsole/GUI apps)",
|
|
136
|
+
variable=self.console_var,
|
|
137
|
+
).pack(anchor="w", padx=8, pady=2)
|
|
138
|
+
|
|
139
|
+
name_frame = ttk.Frame(opts_frame)
|
|
140
|
+
name_frame.pack(fill="x", padx=8, pady=4)
|
|
141
|
+
ttk.Label(name_frame, text="Output name:").pack(side="left")
|
|
142
|
+
self.exe_name_entry = ttk.Entry(name_frame)
|
|
143
|
+
self.exe_name_entry.insert(0, self.target_file.stem)
|
|
144
|
+
self.exe_name_entry.pack(side="left", fill="x", expand=True, padx=6)
|
|
145
|
+
|
|
146
|
+
build_btn = ttk.Button(
|
|
147
|
+
parent, text="Build .exe", command=self._build_exe
|
|
148
|
+
)
|
|
149
|
+
build_btn.pack(pady=10)
|
|
150
|
+
|
|
151
|
+
# ---------- Logging helper ----------
|
|
152
|
+
|
|
153
|
+
def _log(self, text: str) -> None:
|
|
154
|
+
self.log_box.configure(state="normal")
|
|
155
|
+
self.log_box.insert("end", text + "\n")
|
|
156
|
+
self.log_box.see("end")
|
|
157
|
+
self.log_box.configure(state="disabled")
|
|
158
|
+
|
|
159
|
+
def _run_in_thread(self, cmd: list[str]) -> None:
|
|
160
|
+
def worker():
|
|
161
|
+
self._log(f"$ {' '.join(cmd)}")
|
|
162
|
+
try:
|
|
163
|
+
proc = subprocess.Popen(
|
|
164
|
+
cmd,
|
|
165
|
+
stdout=subprocess.PIPE,
|
|
166
|
+
stderr=subprocess.STDOUT,
|
|
167
|
+
text=True,
|
|
168
|
+
)
|
|
169
|
+
for line in proc.stdout: # type: ignore[union-attr]
|
|
170
|
+
self._log(line.rstrip())
|
|
171
|
+
proc.wait()
|
|
172
|
+
if proc.returncode == 0:
|
|
173
|
+
self._log("Done.")
|
|
174
|
+
else:
|
|
175
|
+
self._log(f"Exited with code {proc.returncode}")
|
|
176
|
+
except FileNotFoundError as e:
|
|
177
|
+
self._log(f"Error: {e}")
|
|
178
|
+
except Exception as e: # noqa: BLE001
|
|
179
|
+
self._log(f"Error: {e}")
|
|
180
|
+
|
|
181
|
+
threading.Thread(target=worker, daemon=True).start()
|
|
182
|
+
|
|
183
|
+
# ---------- Actions ----------
|
|
184
|
+
|
|
185
|
+
def _install_dependencies(self) -> None:
|
|
186
|
+
selected_indices = self.deps_listbox.curselection()
|
|
187
|
+
packages = [self.deps_listbox.get(i) for i in selected_indices]
|
|
188
|
+
custom = self.custom_dep_entry.get().strip()
|
|
189
|
+
if custom:
|
|
190
|
+
packages.append(custom)
|
|
191
|
+
|
|
192
|
+
if not packages:
|
|
193
|
+
messagebox.showinfo("PyCodeTools", "No packages selected.")
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
cmd = [sys.executable, "-m", "pip", "install", *packages]
|
|
197
|
+
self._run_in_thread(cmd)
|
|
198
|
+
|
|
199
|
+
def _build_exe(self) -> None:
|
|
200
|
+
name = self.exe_name_entry.get().strip() or self.target_file.stem
|
|
201
|
+
cmd = [sys.executable, "-m", "PyInstaller"]
|
|
202
|
+
if self.onefile_var.get():
|
|
203
|
+
cmd.append("--onefile")
|
|
204
|
+
if not self.console_var.get():
|
|
205
|
+
cmd.append("--noconsole")
|
|
206
|
+
cmd.extend(["--name", name, str(self.target_file)])
|
|
207
|
+
self._run_in_thread(cmd)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def launch_config_gui(target_file: Path) -> None:
|
|
211
|
+
root = tk.Tk()
|
|
212
|
+
PCTConfigApp(root, target_file)
|
|
213
|
+
root.mainloop()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: PyCodeTools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Run Python scripts with extra scripting helpers, and package them into .exe files from a simple GUI.
|
|
5
|
+
Author: Your Name
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pct.ct.ws
|
|
8
|
+
Keywords: cli,scripting,pyinstaller,tkinter,build tools
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
14
|
+
Requires-Python: >=3.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Provides-Extra: build
|
|
17
|
+
Requires-Dist: pyinstaller>=6.0; extra == "build"
|
|
18
|
+
|
|
19
|
+
# PyCodeTools (PCT)
|
|
20
|
+
|
|
21
|
+
Run Python scripts with a few extra scripting helpers, and package them
|
|
22
|
+
into standalone `.exe` files from a simple Tkinter GUI — all from the
|
|
23
|
+
command line.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install PyCodeTools
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This gives you two equivalent commands: `pycodetools` and `pct`.
|
|
32
|
+
|
|
33
|
+
## Enabling PCT in a script
|
|
34
|
+
|
|
35
|
+
Add this as the **first line** of your `.py` file:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
pycodetools = {true}PCT.enable
|
|
39
|
+
print("pycodetools")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
That first line is what PCT looks for. It's still valid, harmless Python
|
|
43
|
+
(just a variable assignment) if someone runs the file normally with
|
|
44
|
+
`python file.py` — but when you run it through PCT, PCT detects it,
|
|
45
|
+
prints a confirmation, and unlocks extra helpers for your script.
|
|
46
|
+
|
|
47
|
+
## Running a script
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pct myscript.py
|
|
51
|
+
# or explicitly:
|
|
52
|
+
pct run myscript.py
|
|
53
|
+
# or the long form:
|
|
54
|
+
pycodetools myscript.py
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If the enable line is found, you'll see:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
{true}PCT.enable
|
|
61
|
+
PyCodeTools is now enabled for this run.
|
|
62
|
+
Running: py myscript.py
|
|
63
|
+
----------------------------------------
|
|
64
|
+
pycodetools
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Extra helpers in your script
|
|
68
|
+
|
|
69
|
+
Once enabled, your script can use the extra helper functions by
|
|
70
|
+
importing the package:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
pycodetools = {true}PCT.enable
|
|
74
|
+
import pycodetools as pct
|
|
75
|
+
|
|
76
|
+
if pct.enabled:
|
|
77
|
+
pct.title("My App")
|
|
78
|
+
pct.banner("Welcome!")
|
|
79
|
+
print(pct.color("This is red text", "red"))
|
|
80
|
+
pct.timer(3)
|
|
81
|
+
if pct.confirm("Keep going?"):
|
|
82
|
+
print("Continuing...")
|
|
83
|
+
pct.pause()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Available helpers:
|
|
87
|
+
|
|
88
|
+
| Function | What it does |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `pct.title(text)` | Sets the terminal window title |
|
|
91
|
+
| `pct.clear()` | Clears the terminal |
|
|
92
|
+
| `pct.pause(msg=...)` | "Press Enter to continue..." prompt |
|
|
93
|
+
| `pct.banner(text)` | Prints a boxed banner |
|
|
94
|
+
| `pct.color(text, name)` | Returns ANSI-colored text |
|
|
95
|
+
| `pct.timer(seconds)` | Countdown timer |
|
|
96
|
+
| `pct.confirm(prompt)` | Yes/No prompt, returns bool |
|
|
97
|
+
| `pct.enabled` | `True` if the enable line was detected |
|
|
98
|
+
|
|
99
|
+
## Config / Build GUI
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pct config myscript.py
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Opens a Tkinter window with two tabs:
|
|
106
|
+
|
|
107
|
+
- **Dependencies** — pick from a preset list of common packages (or type
|
|
108
|
+
a custom one) and install them with pip.
|
|
109
|
+
- **Build .exe** — compile the script into a standalone executable using
|
|
110
|
+
[PyInstaller](https://pyinstaller.org/), with options for one-file
|
|
111
|
+
builds, hiding the console window, and naming the output.
|
|
112
|
+
|
|
113
|
+
Building `.exe` files requires PyInstaller:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install PyCodeTools[build]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pycodetools/__init__.py,sha256=WGlIBlhLwwVK61sASLbZqcgRj6kEkwAyLYng7hFIfyE,3000
|
|
2
|
+
pycodetools/cli.py,sha256=H_uIU3SzdHtBMnM-VX04yx8KLwiifuMsScrtUPhJTQM,4218
|
|
3
|
+
pycodetools/gui.py,sha256=Cilbb53O4bP5jMqMwgoFN5AL2kUhtNExyjlsMxvJtR0,7025
|
|
4
|
+
pycodetools-0.1.0.dist-info/METADATA,sha256=zTTq14WJSumn3sAACyoMrbQ3qvS_SNUkWbzoQmh5g9M,3238
|
|
5
|
+
pycodetools-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
6
|
+
pycodetools-0.1.0.dist-info/entry_points.txt,sha256=hg2Hj2oWS1EiboZOt4CHuAPieMUgx7cJVtSC0dHZGWI,80
|
|
7
|
+
pycodetools-0.1.0.dist-info/top_level.txt,sha256=OLCaX5mX_h6phv-gPkiHCLCY0N8ZC3iOQJcHHUv0gYY,12
|
|
8
|
+
pycodetools-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pycodetools
|