Files
Jean-Pierre MartineauandClaude Opus 5.5 ffe31dac36 Docstrings and comments of meshterm/core are in ASD-STE100
The prose of each docstring and comment in the core package now follows
ASD-STE100 Simplified Technical English, with Canadian spelling: short
sentences, simple verb tenses, no dashes or semicolons, and one word for
one concept (docs/development/writing-style.md has the rules and the
glossary). The meaning and each reason stay. The code does not change:
the AST of each file, without its docstrings, is the same as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 03:29:03 -04:00

109 lines
5.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""What each relaunch of MeshTerm needs, for each terminal into which it relaunches.
MeshTerm opens itself again when the terminal from which it started cannot draw it. There
are two such terminals, for two unrelated reasons. The classic Windows console has no font
fallback, so an icon that its font does not have is a box, and no font choice corrects it
(:mod:`meshterm.core.winterminal`). macOS Terminal has the opposite problem: its fallback
is too generous. No Mac monospace face has the Braille Patterns block. Thus the charts come
from a proportional face, and their width is wrong (:mod:`meshterm.core.macterminal`). One
host shows nothing, and the other shows something crooked.
The destinations are very different, but the action is the same. Find the command that
starts this session again, give it an environment in which a new start can survive, and
let the caller exit. Thus this code is here, one time, and each platform module supplies
only the part that is really its own: which terminal, and how to ask it.
"""
from __future__ import annotations
import os
import sys
#: Set in the reopened session, so that a relaunch can never start another relaunch. The
#: gate of each platform usually refuses a second pass anyway. (Windows Terminal is not a
#: classic console. A window that opened from the profile of MeshTerm is already in that
#: profile.) But this variable is a second safeguard for those gates, and it costs one
#: environment variable.
REOPENED_ENV = "MESHTERM_REOPENED"
def own_command() -> list[str]:
"""The command that starts this same session again.
A frozen build is its own executable, and takes the arguments as they were given. A
development install or a ``pip`` install starts through its interpreter, because
MeshTerm cannot be sure that the receiving terminal resolves the path of the console
script.
Returns:
The program and its arguments, ready to give to a terminal.
"""
if getattr(sys, "frozen", False):
return [sys.executable, *sys.argv[1:]]
return [sys.executable, "-m", "meshterm", *sys.argv[1:]]
def child_environment() -> dict[str, str]:
"""The environment for a new start of MeshTerm, without the bookkeeping of this bundle.
A PyInstaller one-file build runs in two stages. The executable unpacks itself into a
temporary directory, and then runs itself again as a child. The two halves coordinate
through ``_PYI*`` environment variables. These variables must not get to a new start of
the executable. Else the bootloader finds them, and concludes that it is the second
stage of a start that it never made. Then it checks that its parent is the same
program, finds the terminal instead, and aborts::
[PYI-33368:ERROR] Security validation failure: parent process has different
executable!
Then the full session is lost. A new window shows a message that nobody can act on,
and the window that offered the move is already closed. When the function removes these
variables, the new process is a first stage, which is its correct stage.
Returns:
The environment to start the new process with.
"""
environment = dict(os.environ)
environment[REOPENED_ENV] = "1"
if not getattr(sys, "frozen", False):
return environment
for key in [key for key in environment if key.startswith("_PYI")]:
del environment[key]
environment.pop("_MEIPASS2", None) # the name that older bootloaders used
# The bootloader sets the loader path to the unpacked bundle, and keeps the original
# value of the caller next to it. The new process unpacks its own copy, so it must
# have the original value.
for name in ("LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"):
original = environment.pop(f"{name}_ORIG", None)
if original is not None:
environment[name] = original
else:
environment.pop(name, None)
return environment
def wanted_size() -> tuple[int, int]:
"""The window size to ask for, as ``(cols, rows)``.
MeshTerm asks for a size, because a window that MeshTerm opens must be a window in
which MeshTerm fits. A session can open at the size with which the shell of the user
starts, and that size can be too small for the app. The failure is quiet and cosmetic.
The startup wordmark is 71 cells wide, so at 70 cells MeshTerm silently uses the narrow
wordmark that was drawn for the PicoCalc. Also, a short window truncates each
description onto the pager.
The numbers are the minimum that the platform itself states, plus a margin. Thus they
follow that minimum, and do not repeat it. A terminal can limit the size to what it can
show, so this is a request and not a demand. On a small screen, the app degrades
exactly as it does without the request.
Returns:
The width in cells and the height in rows to ask the terminal for.
"""
from ..platforms import get_platform
platform = get_platform()
return platform.readable_cols + 8, platform.readable_rows + 6