fix: make script help paths lightweight (#1937)
## Summary Consolidate the script help/startup fixes so standalone helper commands can show usage without requiring runtime-only dependencies or generating files. ## Details Several helper scripts imported optional packages, evaluated newer annotations, or started generation before users could inspect CLI usage. On a fresh checkout, this made common help paths fail on missing packages such as `cairosvg`, `Pillow`, `freetype-py`, `fontTools`, `pyphen`, `pyserial`, or on Python 3.9 annotation evaluation. A few generators also treated `--help` as a normal output argument and wrote files instead of printing usage. This change keeps runtime dependencies on the code paths that need them, handles lightweight help/list commands first, and adds explicit `--help` handling for generator scripts that previously started work. ## Validation - `python3 <script> --help` across tracked Python scripts, excluding `scripts/patch_jpegdec.py` because it is a PlatformIO pre-build hook rather than a standalone CLI - `python3 scripts/convert_icon.py` still exits with usage for missing required arguments - `python3 lib/EpdFont/scripts/fontconvert_sdcard.py --list-presets` - `python3 -m py_compile` for the changed scripts
This commit is contained in:
@@ -1,12 +1,10 @@
|
||||
#!python3
|
||||
import freetype
|
||||
import zlib
|
||||
import sys
|
||||
import re
|
||||
import math
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
# Force UTF-8 stdout so that `python fontconvert.py … > foo.h` on Windows
|
||||
# (default cp1252) doesn't emit UTF-16 LE / replacement chars in the generated
|
||||
@@ -27,6 +25,9 @@ parser.add_argument("--force-autohint", dest="force_autohint", action="store_tru
|
||||
parser.add_argument("--pnum", dest="pnum", action="store_true", help="Use proportional numerals (pnum OpenType feature) instead of default tabular figures. Reduces visual gaps between digits in running prose.")
|
||||
args = parser.parse_args()
|
||||
|
||||
import freetype
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
GlyphProps = namedtuple("GlyphProps", ["width", "height", "advance_x", "left", "top", "data_length", "data_offset", "code_point"])
|
||||
|
||||
font_stack = [freetype.Face(f) for f in args.fontstack]
|
||||
|
||||
@@ -22,7 +22,8 @@ Usage:
|
||||
|
||||
"""
|
||||
|
||||
import freetype
|
||||
from __future__ import annotations
|
||||
|
||||
import struct
|
||||
import sys
|
||||
import os
|
||||
@@ -31,8 +32,6 @@ import math
|
||||
import argparse
|
||||
from collections import namedtuple
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
from cpfont_version import CPFONT_VERSION
|
||||
|
||||
# --- Unicode interval presets ---
|
||||
@@ -252,6 +251,8 @@ def extract_kerning_fonttools(font_path, codepoints, ppem):
|
||||
codepoints. Values are scaled from font design units to integer
|
||||
pixels at ppem.
|
||||
"""
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
font = TTFont(font_path)
|
||||
units_per_em = font['head'].unitsPerEm
|
||||
cmap = font.getBestCmap() or {}
|
||||
@@ -402,6 +403,8 @@ def extract_ligatures_fonttools(font_path, codepoints):
|
||||
Returns list of (packed_pair, ligature_codepoint) for the given codepoints.
|
||||
Multi-character ligatures are decomposed into chained pairs.
|
||||
"""
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
font = TTFont(font_path)
|
||||
cmap = font.getBestCmap() or {}
|
||||
|
||||
@@ -514,6 +517,8 @@ def extract_ligatures_fonttools(font_path, codepoints):
|
||||
|
||||
def rasterize_font_style(fontfile, size, intervals, style_id=0, force_autohint=False):
|
||||
"""Rasterize all glyphs for one font style. Returns StyleRasterData."""
|
||||
import freetype
|
||||
|
||||
style_names = {0: "regular", 1: "bold", 2: "italic", 3: "bolditalic"}
|
||||
style_label = style_names.get(style_id, str(style_id))
|
||||
|
||||
|
||||
@@ -234,6 +234,9 @@ def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} <font_headers_directory>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if sys.argv[1] in ("-h", "--help"):
|
||||
print(f"Usage: {sys.argv[0]} <font_headers_directory>")
|
||||
sys.exit(0)
|
||||
|
||||
font_dir = sys.argv[1]
|
||||
if not os.path.isdir(font_dir):
|
||||
|
||||
+12
-6
@@ -1,18 +1,21 @@
|
||||
import sys
|
||||
import os
|
||||
from PIL import Image
|
||||
import cairosvg
|
||||
import io
|
||||
import sys
|
||||
|
||||
threshold = 128
|
||||
USAGE = 'Usage: python scripts/convert_icon.py input.png|input.svg output_name width height'
|
||||
|
||||
def svg_to_png_bytes(svg_path, width, height):
|
||||
import cairosvg
|
||||
|
||||
with open(svg_path, 'rb') as f:
|
||||
svg_data = f.read()
|
||||
png_bytes = cairosvg.svg2png(bytestring=svg_data, output_width=width, output_height=height)
|
||||
return png_bytes
|
||||
|
||||
def load_image(path, width, height):
|
||||
from PIL import Image
|
||||
|
||||
ext = os.path.splitext(path)[1].lower()
|
||||
if ext == '.svg':
|
||||
png_bytes = svg_to_png_bytes(path, width, height)
|
||||
@@ -58,8 +61,11 @@ def image_to_c_array(img, array_name):
|
||||
return c
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 5:
|
||||
print('Usage: python convert_image.py input.png output_name width height')
|
||||
if any(arg in ('-h', '--help') for arg in sys.argv[1:]):
|
||||
print(USAGE)
|
||||
sys.exit(0)
|
||||
if len(sys.argv) != 5:
|
||||
print(USAGE)
|
||||
sys.exit(1)
|
||||
input_path, output_name, width, height = sys.argv[1:5]
|
||||
array_name = output_name.capitalize() + 'Icon'
|
||||
@@ -77,4 +83,4 @@ def main():
|
||||
print(f'Wrote {output_path}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -35,6 +35,43 @@ import threading
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
|
||||
DEFAULT_BAUDRATE = 115200
|
||||
|
||||
|
||||
def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ESP32 Serial Monitor with Memory Graph - Real-time monitoring, graphing, and command interface"
|
||||
)
|
||||
parser.add_argument(
|
||||
"port",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Serial port (leave empty for autodetection)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--baud",
|
||||
type=int,
|
||||
default=DEFAULT_BAUDRATE,
|
||||
help=f"Baud rate (default: {DEFAULT_BAUDRATE})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--filter",
|
||||
type=str,
|
||||
default="",
|
||||
help="Only display lines containing this keyword (case-insensitive)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--suppress",
|
||||
type=str,
|
||||
default="",
|
||||
help="Suppress lines containing this keyword (case-insensitive)",
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
if any(arg in ("-h", "--help") for arg in sys.argv[1:]):
|
||||
build_arg_parser().parse_args()
|
||||
|
||||
# Try to import potentially missing packages
|
||||
PACKAGE_MAPPING: dict[str, str] = {
|
||||
"serial": "pyserial",
|
||||
@@ -401,34 +438,7 @@ def main() -> None:
|
||||
- Screenshot capture capability
|
||||
- Graceful shutdown on Ctrl-C or window close
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ESP32 Serial Monitor with Memory Graph - Real-time monitoring, graphing, and command interface"
|
||||
)
|
||||
default_baudrate = 115200
|
||||
parser.add_argument(
|
||||
"port",
|
||||
nargs="?",
|
||||
default=None,
|
||||
help="Serial port (leave empty for autodetection)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--baud",
|
||||
type=int,
|
||||
default=default_baudrate,
|
||||
help=f"Baud rate (default: {default_baudrate})",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--filter",
|
||||
type=str,
|
||||
default="",
|
||||
help="Only display lines containing this keyword (case-insensitive)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--suppress",
|
||||
type=str,
|
||||
default="",
|
||||
help="Suppress lines containing this keyword (case-insensitive)",
|
||||
)
|
||||
parser = build_arg_parser()
|
||||
args = parser.parse_args()
|
||||
port = args.port
|
||||
if port is None:
|
||||
|
||||
@@ -16,6 +16,8 @@ The input directory may be flat (all .cpfont files in one dir) or nested
|
||||
convention <FamilyName>_<size>.cpfont.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
@@ -137,10 +137,15 @@ Also includes:
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
if any(arg in ("-h", "--help") for arg in sys.argv[1:]):
|
||||
print(__doc__.strip())
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
|
||||
@@ -326,4 +326,7 @@ def main():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if any(arg in ("-h", "--help") for arg in sys.argv[1:]):
|
||||
print(__doc__.strip())
|
||||
sys.exit(0)
|
||||
main()
|
||||
|
||||
@@ -11,9 +11,14 @@ Creates EPUBs with annotated JPEG and PNG images to verify:
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
if any(arg in ("-h", "--help") for arg in sys.argv[1:]):
|
||||
print(__doc__.strip())
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
|
||||
@@ -16,7 +16,6 @@ Requirements:
|
||||
import argparse
|
||||
import re
|
||||
from collections import Counter
|
||||
import pyphen
|
||||
from pathlib import Path
|
||||
import zipfile
|
||||
|
||||
@@ -75,6 +74,8 @@ def generate_hyphenation_data(
|
||||
min_prefix: Minimum characters allowed before the first hyphen (default: 2)
|
||||
min_suffix: Minimum characters allowed after the last hyphen (default: 2)
|
||||
"""
|
||||
import pyphen
|
||||
|
||||
print(f"Reading from: {input_file}")
|
||||
|
||||
# Read the input file
|
||||
|
||||
Reference in New Issue
Block a user