Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Lib/_sitebuiltins.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,17 @@ def __repr__(self):
return "Type %s() to see the full %s text" % ((self.__name,)*2)

def __call__(self):
from _pyrepl.pager import get_pager
try:
from _pyrepl.pager import get_pager
except ModuleNotFoundError:
try:
from pydoc import get_pager
except ModuleNotFoundError:
def get_pager():
def _print(text, title=None):
print(text)
return _print

self.__setup()

pager = get_pager()
Expand Down
14 changes: 10 additions & 4 deletions Lib/asyncio/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
import types
import warnings

from _colorize import get_theme
from _pyrepl.console import InteractiveColoredConsole
try:
from _colorize import get_theme
from _pyrepl.console import InteractiveColoredConsole as InteractiveConsole
except ModuleNotFoundError:
from code import InteractiveConsole

from . import futures


class AsyncIOInteractiveConsole(InteractiveColoredConsole):
class AsyncIOInteractiveConsole(InteractiveConsole):

def __init__(self, locals, loop):
super().__init__(locals, filename="<stdin>")
Expand Down Expand Up @@ -185,7 +188,10 @@ def interrupt(self) -> None:
if os.getenv('PYTHON_BASIC_REPL'):
CAN_USE_PYREPL = False
else:
from _pyrepl.main import CAN_USE_PYREPL
try:
from _pyrepl.main import CAN_USE_PYREPL
except ModuleNotFoundError:
CAN_USE_PYREPL = False

return_code = 0
loop = asyncio.new_event_loop()
Expand Down
8 changes: 6 additions & 2 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,16 @@
import selectors
import threading
import _colorize
import _pyrepl.utils

from contextlib import ExitStack, closing, contextmanager
from types import CodeType
from warnings import deprecated

try:
import _pyrepl.utils
except ModuleNotFoundError:
_pyrepl = None


class Restart(Exception):
"""Causes a debugger to be restarted for the debugged python program."""
Expand Down Expand Up @@ -1097,7 +1101,7 @@ def handle_command_def(self, line):
return False

def _colorize_code(self, code):
if self.colorize:
if self.colorize and _pyrepl:
colors = list(_pyrepl.utils.gen_colors(code))
chars, _ = _pyrepl.utils.disp_str(code, colors=colors, force_color=True)
code = "".join(chars)
Expand Down
49 changes: 35 additions & 14 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,41 @@ class or function within a module or module in a package. If the
from reprlib import Repr
from traceback import format_exception_only

from _pyrepl.pager import (get_pager, pipe_pager,
plain_pager, tempfile_pager, tty_pager)

# Expose plain() as pydoc.plain()
from _pyrepl.pager import plain # noqa: F401


# --------------------------------------------------------- old names

getpager = get_pager
pipepager = pipe_pager
plainpager = plain_pager
tempfilepager = tempfile_pager
ttypager = tty_pager
try:
from _pyrepl.pager import (get_pager, pipe_pager,
plain_pager, tempfile_pager, tty_pager)

# Expose plain() as pydoc.plain()
from _pyrepl.pager import plain # noqa: F401

# --------------------------------------------------------- old names
getpager = get_pager
pipepager = pipe_pager
plainpager = plain_pager
tempfilepager = tempfile_pager
ttypager = tty_pager

except ModuleNotFoundError:
# Minimal alternatives for cases where _pyrepl is absent.

def plain(text: str) -> str:
"""Remove boldface formatting from text."""
return re.sub('.\b', '', text)

def plain_pager(text: str, title: str = '') -> None:
"""Simply print unformatted text. This is the ultimate fallback."""
encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
text = text.encode(encoding, 'backslashreplace').decode(encoding)
text = plain(text)
sys.stdout.write(text)

def get_pager():
"""Unconditionally return the plain pager, since _pyrepl is absent."""
return plain_pager

# --------------------------------------------------------- old names
getpager = get_pager
plainpager = plain_pager


# --------------------------------------------------------- common routines
Expand Down
2 changes: 2 additions & 0 deletions Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ def register_readline():
import _pyrepl.unix_console
console_errors = _pyrepl.unix_console._error
from _pyrepl.main import CAN_USE_PYREPL
except ModuleNotFoundError:
CAN_USE_PYREPL = False
finally:
sys.path = original_path
except ImportError:
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3022,6 +3022,13 @@ def force_color(color: bool):
import _colorize
from .os_helper import EnvironmentVarGuard

if color:
try:
import _pyrepl # noqa: F401
except ModuleNotFoundError:
# Can't force enable color without _pyrepl, so just skip.
raise unittest.SkipTest("_pyrepl is missing")

with (
swap_attr(_colorize, "can_colorize", lambda *, file=None: color),
EnvironmentVarGuard() as env,
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_pyclbr.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ def test_others(self):
ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget', 'curframe_locals',
'_InteractState', 'rlcompleter'),
)
cm('pydoc', ignore=('input', 'output',)) # properties
cm('pydoc', ignore=('input', 'output', # properties
'getpager', 'plainpager', )) # aliases

# Tests for modules inside packages
cm('email.parser')
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_pyrepl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from test.support import import_helper, load_package_tests


import_helper.import_module("_pyrepl")


if sys.platform != "win32":
import_helper.import_module("termios")

Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ def test_toplevel_contextvars_async(self):
p = spawn_asyncio_repl()
p.stdin.write(user_input)
user_input2 = "async def set_var(): var.set('ok')\n"
try:
import _pyrepl # noqa: F401
except ModuleNotFoundError:
# If we're going to be forced into the regular REPL, then we need an
# extra newline here. Omit it by default to catch any breakage to
# the new REPL's behavior.
user_input2 += "\n"
p.stdin.write(user_input2)
user_input3 = "await set_var()\n"
p.stdin.write(user_input3)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Allows omitting the internal library ``_pyrepl`` with limited loss of
functionality. This allows complete removal of the modern REPL, which is an
unsupported configuration, but still desirable for some distributions.
48 changes: 35 additions & 13 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,13 +561,25 @@ pymain_run_stdin(PyConfig *config)
return pymain_exit_err_print();
}

if (!isatty(fileno(stdin))
|| _Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
PyCompilerFlags cf = _PyCompilerFlags_INIT;
int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
return (run != 0);
int run;
if (isatty(fileno(stdin))
&& !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
PyObject *pyrepl = PyImport_ImportModule("_pyrepl");
if (pyrepl != NULL) {
run = pymain_start_pyrepl(0);
Py_DECREF(pyrepl);
return run;
}
if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) {
fprintf(stderr, "Could not import _pyrepl.main\n");
return pymain_exit_err_print();
}
PyErr_Clear();
}
return pymain_start_pyrepl(0);

PyCompilerFlags cf = _PyCompilerFlags_INIT;
run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
return (run != 0);
}


Expand All @@ -593,14 +605,24 @@ pymain_repl(PyConfig *config, int *exitcode)
return;
}

if (!isatty(fileno(stdin))
|| _Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
PyCompilerFlags cf = _PyCompilerFlags_INIT;
int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
*exitcode = (run != 0);
return;
if (isatty(fileno(stdin))
&& !_Py_GetEnv(config->use_environment, "PYTHON_BASIC_REPL")) {
PyObject *pyrepl = PyImport_ImportModule("_pyrepl");
if (pyrepl != NULL) {
int run = pymain_start_pyrepl(1);
*exitcode = (run != 0);
Py_DECREF(pyrepl);
return;
}
if (!PyErr_ExceptionMatches(PyExc_ModuleNotFoundError)) {
PyErr_Clear();
fprintf(stderr, "Could not import _pyrepl.main\n");
return;
}
PyErr_Clear();
}
int run = pymain_start_pyrepl(1);
PyCompilerFlags cf = _PyCompilerFlags_INIT;
int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, &cf);
*exitcode = (run != 0);
return;
}
Expand Down
Loading