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
27 changes: 17 additions & 10 deletions src/prompt_toolkit/shortcuts/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ class CompleteStyle(str, Enum):
_T = TypeVar("_T")


class _Unset:
pass


_UNSET = _Unset()


class PromptSession(Generic[_T]):
"""
PromptSession for a prompt application, which can be used as a GNU Readline
Expand Down Expand Up @@ -910,7 +917,7 @@ def prompt(
validate_while_typing: FilterOrBool | None = None,
complete_style: CompleteStyle | None = None,
auto_suggest: AutoSuggest | None = None,
validator: Validator | None = None,
validator: Validator | None | _Unset = _UNSET,
clipboard: Clipboard | None = None,
mouse_support: FilterOrBool | None = None,
input_processors: list[Processor] | None = None,
Expand Down Expand Up @@ -940,10 +947,10 @@ class itself. For these, passing in ``None`` will keep the current
attribute for the session, which means that it applies to the current,
but also to the next prompts.

Note that in order to erase a ``Completer``, ``Validator`` or
``AutoSuggest``, you can't use ``None``. Instead pass in a
``DummyCompleter``, ``DummyValidator`` or ``DummyAutoSuggest`` instance
respectively. For a ``Lexer`` you can pass in an empty ``SimpleLexer``.
Note that in order to erase a ``Completer`` or ``AutoSuggest``, you
can't use ``None``. Instead pass in a ``DummyCompleter`` or
``DummyAutoSuggest`` instance respectively. For a ``Lexer`` you can
pass in an empty ``SimpleLexer``.

Additional arguments, specific for this prompt:

Expand Down Expand Up @@ -1024,8 +1031,8 @@ class itself. For these, passing in ``None`` will keep the current
self.complete_style = complete_style
if auto_suggest is not None:
self.auto_suggest = auto_suggest
if validator is not None:
self.validator = validator
if validator is not _UNSET:
self.validator = cast(Validator | None, validator)
if clipboard is not None:
self.clipboard = clipboard
if mouse_support is not None:
Expand Down Expand Up @@ -1150,7 +1157,7 @@ async def prompt_async(
validate_while_typing: FilterOrBool | None = None,
complete_style: CompleteStyle | None = None,
auto_suggest: AutoSuggest | None = None,
validator: Validator | None = None,
validator: Validator | None | _Unset = _UNSET,
clipboard: Clipboard | None = None,
mouse_support: FilterOrBool | None = None,
input_processors: list[Processor] | None = None,
Expand Down Expand Up @@ -1221,8 +1228,8 @@ async def prompt_async(
self.complete_style = complete_style
if auto_suggest is not None:
self.auto_suggest = auto_suggest
if validator is not None:
self.validator = validator
if validator is not _UNSET:
self.validator = cast(Validator | None, validator)
if clipboard is not None:
self.clipboard = clipboard
if mouse_support is not None:
Expand Down
54 changes: 54 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import annotations

import asyncio
from functools import partial

import pytest
Expand All @@ -19,6 +20,7 @@
from prompt_toolkit.key_binding.key_bindings import KeyBindings
from prompt_toolkit.output import DummyOutput
from prompt_toolkit.shortcuts import PromptSession
from prompt_toolkit.validation import Validator


def _history():
Expand Down Expand Up @@ -71,6 +73,58 @@ def test_simple_text_input():
assert cli.current_buffer.text == "hello"


def test_prompt_validator_none_clears_session_validator():
validator = Validator.from_callable(lambda text: True)

with create_pipe_input() as inp:
inp.send_text("hello\r")
session = PromptSession(
input=inp,
output=DummyOutput(),
validator=validator,
)

session.prompt(validator=None)

assert session.validator is None


def test_prompt_without_validator_keeps_session_validator():
validator = Validator.from_callable(lambda text: True)

with create_pipe_input() as inp:
inp.send_text("hello\r")
session = PromptSession(
input=inp,
output=DummyOutput(),
validator=validator,
)

session.prompt()

assert session.validator is validator


def test_prompt_async_validator_none_clears_session_validator():
validator = Validator.from_callable(lambda text: True)

async def run_prompt() -> PromptSession[str]:
with create_pipe_input() as inp:
inp.send_text("hello\r")
session = PromptSession(
input=inp,
output=DummyOutput(),
validator=validator,
)

await session.prompt_async(validator=None)

return session

session = asyncio.run(run_prompt())
assert session.validator is None


def test_emacs_cursor_movements():
"""
Test cursor movements with Emacs key bindings.
Expand Down
Loading