From e5f7a41d988de298069eda636e823f374b973bd4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 18:33:15 +0200 Subject: Adding upstream version 3.0.26. Signed-off-by: Daniel Baumann --- ptpython/entry_points/__init__.py | 0 ptpython/entry_points/run_ptipython.py | 82 ++++++++++++ ptpython/entry_points/run_ptpython.py | 234 +++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 ptpython/entry_points/__init__.py create mode 100644 ptpython/entry_points/run_ptipython.py create mode 100644 ptpython/entry_points/run_ptpython.py (limited to 'ptpython/entry_points') diff --git a/ptpython/entry_points/__init__.py b/ptpython/entry_points/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ptpython/entry_points/run_ptipython.py b/ptpython/entry_points/run_ptipython.py new file mode 100644 index 0000000..b660a0a --- /dev/null +++ b/ptpython/entry_points/run_ptipython.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +from __future__ import annotations + +import os +import sys + +from .run_ptpython import create_parser, get_config_and_history_file + + +def run(user_ns=None): + a = create_parser().parse_args() + + config_file, history_file = get_config_and_history_file(a) + + # If IPython is not available, show message and exit here with error status + # code. + try: + import IPython + except ImportError: + print("IPython not found. Please install IPython (pip install ipython).") + sys.exit(1) + else: + from ptpython.ipython import embed + from ptpython.repl import enable_deprecation_warnings, run_config + + # 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") + exec(code, {"__name__": "__main__", "__file__": path}) + else: + enable_deprecation_warnings() + + # Create an empty namespace for this interactive shell. (If we don't do + # that, all the variables from this function will become available in + # the IPython shell.) + if user_ns is None: + user_ns = {} + + # Startup path + startup_paths = [] + if "PYTHONSTARTUP" in os.environ: + startup_paths.append(os.environ["PYTHONSTARTUP"]) + + # --interactive + if a.interactive: + startup_paths.append(a.args[0]) + sys.argv = a.args + + # exec scripts from startup paths + for path in startup_paths: + if os.path.exists(path): + with open(path, "rb") as f: + code = compile(f.read(), path, "exec") + exec(code, user_ns, user_ns) + else: + print(f"File not found: {path}\n\n") + sys.exit(1) + + # Apply config file + def configure(repl): + if os.path.exists(config_file): + run_config(repl, config_file) + + # Run interactive shell. + embed( + vi_mode=a.vi, + history_filename=history_file, + configure=configure, + user_ns=user_ns, + title="IPython REPL (ptipython)", + ) + + +if __name__ == "__main__": + run() diff --git a/ptpython/entry_points/run_ptpython.py b/ptpython/entry_points/run_ptpython.py new file mode 100644 index 0000000..1d4a532 --- /dev/null +++ b/ptpython/entry_points/run_ptpython.py @@ -0,0 +1,234 @@ +#!/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( + " ~/.ptpython/config.py is deprecated, move your configuration to %s\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( + " ~/.ptpython/history is deprecated, move your history to %s\n" + ) + % history_file + ) + history_file = legacy_history_file + + # Print warnings. + if warnings: + print_formatted_text(HTML("Warning:")) + 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() -- cgit v1.2.3