Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,28 @@
from .application import Application
from .current import (
AppSession,
create_app_session,
get_app,
get_app_or_none,
get_app_session,
set_app,
)
from .dummy import DummyApplication
from .run_in_terminal import in_terminal, run_in_terminal
__all__ = [
# Application.
"Application",
# Current.
"AppSession",
"get_app_session",
"create_app_session",
"get_app",
"get_app_or_none",
"set_app",
# Dummy.
"DummyApplication",
# Run_in_terminal
"in_terminal",
"run_in_terminal",
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
import sys
from contextlib import contextmanager
from typing import TYPE_CHECKING, Any, Generator, Optional
try:
from contextvars import ContextVar
except ImportError:
from prompt_toolkit.eventloop.dummy_contextvars import ContextVar # type: ignore
if TYPE_CHECKING:
from prompt_toolkit.input.defaults import Input
from prompt_toolkit.output.defaults import Output
from .application import Application
__all__ = [
"AppSession",
"get_app_session",
"get_app",
"get_app_or_none",
"set_app",
"create_app_session",
]
class AppSession:
"""
An AppSession is an interactive session, usually connected to one terminal.
Within one such session, interaction with many applications can happen, one
after the other.
The input/output device is not supposed to change during one session.
Warning: Always use the `create_app_session` function to create an
instance, so that it gets activated correctly.
:param input: Use this as a default input for all applications
running in this session, unless an input is passed to the `Application`
explicitely.
:param output: Use this as a default output.
"""
def __init__(
self, input: Optional["Input"] = None, output: Optional["Output"] = None
) -> None:
self._input = input
self._output = output
# The application will be set dynamically by the `set_app` context
# manager. This is called in the application itself.
self.app: Optional["Application[Any]"] = None
def __repr__(self) -> str:
return "AppSession(app=%r)" % (self.app,)
@property
def input(self) -> "Input":
if self._input is None:
from prompt_toolkit.input.defaults import create_input
self._input = create_input()
return self._input
@property
def output(self) -> "Output":
if self._output is None:
from prompt_toolkit.output.defaults import create_output
self._output = create_output()
return self._output
_current_app_session: ContextVar["AppSession"] = ContextVar(
"_current_app_session", default=AppSession()
)
def get_app_session() -> AppSession:
return _current_app_session.get()
def get_app() -> "Application[Any]":
"""
Get the current active (running) Application.
An :class:`.Application` is active during the
:meth:`.Application.run_async` call.
We assume that there can only be one :class:`.Application` active at the
same time. There is only one terminal window, with only one stdin and
stdout. This makes the code significantly easier than passing around the
:class:`.Application` everywhere.
If no :class:`.Application` is running, then return by default a
:class:`.DummyApplication`. For practical reasons, we prefer to not raise
an exception. This way, we don't have to check all over the place whether
an actual `Application` was returned.
(For applications like pymux where we can have more than one `Application`,
we'll use a work-around to handle that.)
"""
session = _current_app_session.get()
if session.app is not None:
return session.app
from .dummy import DummyApplication
return DummyApplication()
def get_app_or_none() -> Optional["Application[Any]"]:
"""
Get the current active (running) Application, or return `None` if no
application is running.
"""
session = _current_app_session.get()
return session.app
@contextmanager
def set_app(app: "Application[Any]") -> Generator[None, None, None]:
"""
Context manager that sets the given :class:`.Application` active in an
`AppSession`.
This should only be called by the `Application` itself.
The application will automatically be active while its running. If you want
the application to be active in other threads/coroutines, where that's not
the case, use `contextvars.copy_context()`, or use `Application.context` to
run it in the appropriate context.
"""
session = _current_app_session.get()
previous_app = session.app
session.app = app
try:
yield
finally:
session.app = previous_app
@contextmanager
def create_app_session(
input: Optional["Input"] = None, output: Optional["Output"] = None
) -> Generator[AppSession, None, None]:
"""
Create a separate AppSession.
This is useful if there can be multiple individual `AppSession`s going on.
Like in the case of an Telnet/SSH server. This functionality uses
contextvars and requires at least Python 3.7.
"""
if sys.version_info <= (3, 6):
raise RuntimeError("Application sessions require Python 3.7.")
# If no input/output is specified, fall back to the current input/output,
# whatever that is.
if input is None:
input = get_app_session().input
if output is None:
output = get_app_session().output
# Create new `AppSession` and activate.
session = AppSession(input=input, output=output)
token = _current_app_session.set(session)
try:
yield session
finally:
_current_app_session.reset(token)

View file

@ -0,0 +1,47 @@
from typing import Callable, Optional
from prompt_toolkit.formatted_text import AnyFormattedText
from prompt_toolkit.input import DummyInput
from prompt_toolkit.output import DummyOutput
from .application import Application
__all__ = [
"DummyApplication",
]
class DummyApplication(Application[None]):
"""
When no :class:`.Application` is running,
:func:`.get_app` will run an instance of this :class:`.DummyApplication` instead.
"""
def __init__(self) -> None:
super().__init__(output=DummyOutput(), input=DummyInput())
def run(
self,
pre_run: Optional[Callable[[], None]] = None,
set_exception_handler: bool = True,
) -> None:
raise NotImplementedError("A DummyApplication is not supposed to run.")
async def run_async(
self,
pre_run: Optional[Callable[[], None]] = None,
set_exception_handler: bool = True,
) -> None:
raise NotImplementedError("A DummyApplication is not supposed to run.")
async def run_system_command(
self,
command: str,
wait_for_enter: bool = True,
display_before_text: AnyFormattedText = "",
wait_text: str = "",
) -> None:
raise NotImplementedError
def suspend_to_background(self, suspend_group: bool = True) -> None:
raise NotImplementedError

View file

@ -0,0 +1,116 @@
"""
Tools for running functions on the terminal above the current application or prompt.
"""
from asyncio import Future, ensure_future
from typing import AsyncGenerator, Awaitable, Callable, TypeVar
from prompt_toolkit.eventloop import run_in_executor_with_context
from .current import get_app_or_none
try:
from contextlib import asynccontextmanager # type: ignore
except ImportError:
from prompt_toolkit.eventloop.async_context_manager import asynccontextmanager
__all__ = [
"run_in_terminal",
"in_terminal",
]
_T = TypeVar("_T")
def run_in_terminal(
func: Callable[[], _T], render_cli_done: bool = False, in_executor: bool = False
) -> Awaitable[_T]:
"""
Run function on the terminal above the current application or prompt.
What this does is first hiding the prompt, then running this callable
(which can safely output to the terminal), and then again rendering the
prompt which causes the output of this function to scroll above the
prompt.
``func`` is supposed to be a synchronous function. If you need an
asynchronous version of this function, use the ``in_terminal`` context
manager directly.
:param func: The callable to execute.
:param render_cli_done: When True, render the interface in the
'Done' state first, then execute the function. If False,
erase the interface first.
:param in_executor: When True, run in executor. (Use this for long
blocking functions, when you don't want to block the event loop.)
:returns: A `Future`.
"""
async def run() -> _T:
async with in_terminal(render_cli_done=render_cli_done):
if in_executor:
return await run_in_executor_with_context(func)
else:
return func()
return ensure_future(run())
@asynccontextmanager
async def in_terminal(render_cli_done: bool = False) -> AsyncGenerator[None, None]:
"""
Asynchronous context manager that suspends the current application and runs
the body in the terminal.
.. code::
async def f():
async with in_terminal():
call_some_function()
await call_some_async_function()
"""
app = get_app_or_none()
if app is None or not app._is_running:
yield
return
# When a previous `run_in_terminal` call was in progress. Wait for that
# to finish, before starting this one. Chain to previous call.
previous_run_in_terminal_f = app._running_in_terminal_f
new_run_in_terminal_f: Future[None] = Future()
app._running_in_terminal_f = new_run_in_terminal_f
# Wait for the previous `run_in_terminal` to finish.
if previous_run_in_terminal_f is not None:
await previous_run_in_terminal_f
# Wait for all CPRs to arrive. We don't want to detach the input until
# all cursor position responses have been arrived. Otherwise, the tty
# will echo its input and can show stuff like ^[[39;1R.
if app.output.responds_to_cpr:
await app.renderer.wait_for_cpr_responses()
# Draw interface in 'done' state, or erase.
if render_cli_done:
app._redraw(render_as_done=True)
else:
app.renderer.erase()
# Disable rendering.
app._running_in_terminal = True
# Detach input.
try:
with app.input.detach():
with app.input.cooked_mode():
yield
finally:
# Redraw interface again.
try:
app._running_in_terminal = False
app.renderer.reset()
app._request_absolute_cursor_position()
app._redraw()
finally:
new_run_in_terminal_f.set_result(None)