1
0
Fork 0
ptpython/ptpython/entry_points/run_ptpython.py
Daniel Baumann f1b95081af
Adding upstream version 3.0.25.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-09 18:31:25 +01:00

234 lines
7.4 KiB
Python

#!/usr/bin/env python
"""
ptpython: Interactive Python shell.
positional arguments:
args Script and arguments
optional arguments:
-h, --help show this help message and exit
--vi Enable Vi key bindings
-i, --interactive Start interactive shell after executing this file.
--asyncio Run an asyncio event loop to support top-level "await".
--light-bg Run on a light background (use dark colors for text).
--dark-bg Run on a dark background (use light colors for text).
--config-file CONFIG_FILE
Location of configuration file.
--history-file HISTORY_FILE
Location of history file.
-V, --version show program's version number and exit
environment variables:
PTPYTHON_CONFIG_HOME: a configuration directory to use
PYTHONSTARTUP: file executed on interactive startup (no default)
"""
from __future__ import annotations
import argparse
import asyncio
import os
import pathlib
import sys
from textwrap import dedent
from typing import IO
import appdirs
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.shortcuts import print_formatted_text
from ptpython.repl import PythonRepl, embed, enable_deprecation_warnings, run_config
try:
from importlib import metadata # type: ignore
except ImportError:
import importlib_metadata as metadata # type: ignore
__all__ = ["create_parser", "get_config_and_history_file", "run"]
class _Parser(argparse.ArgumentParser):
def print_help(self, file: IO[str] | None = None) -> None:
super().print_help()
print(
dedent(
"""
environment variables:
PTPYTHON_CONFIG_HOME: a configuration directory to use
PYTHONSTARTUP: file executed on interactive startup (no default)
""",
).rstrip(),
)
def create_parser() -> _Parser:
parser = _Parser(description="ptpython: Interactive Python shell.")
parser.add_argument("--vi", action="store_true", help="Enable Vi key bindings")
parser.add_argument(
"-i",
"--interactive",
action="store_true",
help="Start interactive shell after executing this file.",
)
parser.add_argument(
"--asyncio",
action="store_true",
help='Run an asyncio event loop to support top-level "await".',
)
parser.add_argument(
"--light-bg",
action="store_true",
help="Run on a light background (use dark colors for text).",
)
parser.add_argument(
"--dark-bg",
action="store_true",
help="Run on a dark background (use light colors for text).",
)
parser.add_argument(
"--config-file", type=str, help="Location of configuration file."
)
parser.add_argument("--history-file", type=str, help="Location of history file.")
parser.add_argument(
"-V",
"--version",
action="version",
version=metadata.version("ptpython"),
)
parser.add_argument("args", nargs="*", help="Script and arguments")
return parser
def get_config_and_history_file(namespace: argparse.Namespace) -> tuple[str, str]:
"""
Check which config/history files to use, ensure that the directories for
these files exist, and return the config and history path.
"""
config_dir = os.environ.get(
"PTPYTHON_CONFIG_HOME",
appdirs.user_config_dir("ptpython", "prompt_toolkit"),
)
data_dir = appdirs.user_data_dir("ptpython", "prompt_toolkit")
# Create directories.
for d in (config_dir, data_dir):
pathlib.Path(d).mkdir(parents=True, exist_ok=True)
# Determine config file to be used.
config_file = os.path.join(config_dir, "config.py")
legacy_config_file = os.path.join(os.path.expanduser("~/.ptpython"), "config.py")
warnings = []
# Config file
if namespace.config_file:
# Override config_file.
config_file = os.path.expanduser(namespace.config_file)
elif os.path.isfile(legacy_config_file):
# Warn about the legacy configuration file.
warnings.append(
HTML(
" <i>~/.ptpython/config.py</i> is deprecated, move your configuration to <i>%s</i>\n"
)
% config_file
)
config_file = legacy_config_file
# Determine history file to be used.
history_file = os.path.join(data_dir, "history")
legacy_history_file = os.path.join(os.path.expanduser("~/.ptpython"), "history")
if namespace.history_file:
# Override history_file.
history_file = os.path.expanduser(namespace.history_file)
elif os.path.isfile(legacy_history_file):
# Warn about the legacy history file.
warnings.append(
HTML(
" <i>~/.ptpython/history</i> is deprecated, move your history to <i>%s</i>\n"
)
% history_file
)
history_file = legacy_history_file
# Print warnings.
if warnings:
print_formatted_text(HTML("<u>Warning:</u>"))
for w in warnings:
print_formatted_text(w)
return config_file, history_file
def run() -> None:
a = create_parser().parse_args()
config_file, history_file = get_config_and_history_file(a)
# Startup path
startup_paths = []
if "PYTHONSTARTUP" in os.environ:
startup_paths.append(os.environ["PYTHONSTARTUP"])
# --interactive
if a.interactive and a.args:
# Note that we shouldn't run PYTHONSTARTUP when -i is given.
startup_paths = [a.args[0]]
sys.argv = a.args
# Add the current directory to `sys.path`.
if sys.path[0] != "":
sys.path.insert(0, "")
# When a file has been given, run that, otherwise start the shell.
if a.args and not a.interactive:
sys.argv = a.args
path = a.args[0]
with open(path, "rb") as f:
code = compile(f.read(), path, "exec")
# NOTE: We have to pass a dict as namespace. Omitting this argument
# causes imports to not be found. See issue #326.
# However, an empty dict sets __name__ to 'builtins', which
# breaks `if __name__ == '__main__'` checks. See issue #444.
exec(code, {"__name__": "__main__", "__file__": path})
# Run interactive shell.
else:
enable_deprecation_warnings()
# Apply config file
def configure(repl: PythonRepl) -> None:
if os.path.exists(config_file):
run_config(repl, config_file)
# Adjust colors if dark/light background flag has been given.
if a.light_bg:
repl.min_brightness = 0.0
repl.max_brightness = 0.60
elif a.dark_bg:
repl.min_brightness = 0.60
repl.max_brightness = 1.0
import __main__
embed_result = embed( # type: ignore
vi_mode=a.vi,
history_filename=history_file,
configure=configure,
locals=__main__.__dict__,
globals=__main__.__dict__,
startup_paths=startup_paths,
title="Python REPL (ptpython)",
return_asyncio_coroutine=a.asyncio,
)
if a.asyncio:
print("Starting ptpython asyncio REPL")
print('Use "await" directly instead of "asyncio.run()".')
asyncio.run(embed_result)
if __name__ == "__main__":
run()