Merging upstream version 3.0.21.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
8fd03f3098
commit
3623c97041
29 changed files with 547 additions and 772 deletions
40
.github/workflows/test.yaml
vendored
Normal file
40
.github/workflows/test.yaml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push: # any branch
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.6, 3.7, 3.8, 3.9, "3.10"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt remove python3-pip
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install . black isort mypy pytest readme_renderer
|
||||||
|
python -m pip install . types-dataclasses # Needed for Python 3.6
|
||||||
|
pip list
|
||||||
|
- name: Type Checker
|
||||||
|
run: |
|
||||||
|
mypy ptpython
|
||||||
|
isort -c --profile black ptpython examples setup.py
|
||||||
|
black --check ptpython examples setup.py
|
||||||
|
if: matrix.python-version != '3.6'
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
./tests/run_tests.py
|
||||||
|
- name: Validate README.md
|
||||||
|
# Ensure that the README renders correctly (required for uploading to PyPI).
|
||||||
|
run: |
|
||||||
|
python -m readme_renderer README.rst > /dev/null
|
16
CHANGELOG
16
CHANGELOG
|
@ -1,6 +1,22 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
3.0.21: 2022-11-25
|
||||||
|
------------------
|
||||||
|
|
||||||
|
New features:
|
||||||
|
- Make ptipython respect more config changes.
|
||||||
|
(See: https://github.com/prompt-toolkit/ptpython/pull/110 )
|
||||||
|
- Improved performance of `DictionaryCompleter` for slow mappings.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Call `super()` in `PythonInputFilter`. This will prevent potentially breakage
|
||||||
|
with an upcoming prompt_toolkit change.
|
||||||
|
(See: https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1690 )
|
||||||
|
- Improved type annotations.
|
||||||
|
- Added `py.typed` to the `package_data`.
|
||||||
|
|
||||||
|
|
||||||
3.0.20: 2021-09-14
|
3.0.20: 2021-09-14
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
263
PKG-INFO
263
PKG-INFO
|
@ -1,263 +0,0 @@
|
||||||
Metadata-Version: 2.1
|
|
||||||
Name: ptpython
|
|
||||||
Version: 3.0.20
|
|
||||||
Summary: Python REPL build on top of prompt_toolkit
|
|
||||||
Home-page: https://github.com/prompt-toolkit/ptpython
|
|
||||||
Author: Jonathan Slenders
|
|
||||||
License: UNKNOWN
|
|
||||||
Description: ptpython
|
|
||||||
========
|
|
||||||
|
|
||||||
|Build Status| |PyPI| |License|
|
|
||||||
|
|
||||||
*A better Python REPL*
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
pip install ptpython
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/example1.png
|
|
||||||
|
|
||||||
Ptpython is an advanced Python REPL. It should work on all
|
|
||||||
Python versions from 2.6 up to 3.9 and work cross platform (Linux,
|
|
||||||
BSD, OS X and Windows).
|
|
||||||
|
|
||||||
Note: this version of ptpython requires at least Python 3.6. Install ptpython
|
|
||||||
2.0.5 for older Python versions.
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
************
|
|
||||||
|
|
||||||
Install it using pip:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
pip install ptpython
|
|
||||||
|
|
||||||
Start it by typing ``ptpython``.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
********
|
|
||||||
|
|
||||||
- Syntax highlighting.
|
|
||||||
- Multiline editing (the up arrow works).
|
|
||||||
- Autocompletion.
|
|
||||||
- Mouse support. [1]
|
|
||||||
- Support for color schemes.
|
|
||||||
- Support for `bracketed paste <https://cirw.in/blog/bracketed-paste>`_ [2].
|
|
||||||
- Both Vi and Emacs key bindings.
|
|
||||||
- Support for double width (Chinese) characters.
|
|
||||||
- ... and many other things.
|
|
||||||
|
|
||||||
|
|
||||||
[1] Disabled by default. (Enable in the menu.)
|
|
||||||
|
|
||||||
[2] If the terminal supports it (most terminals do), this allows pasting
|
|
||||||
without going into paste mode. It will keep the indentation.
|
|
||||||
|
|
||||||
__pt_repr__: A nicer repr with colors
|
|
||||||
*************************************
|
|
||||||
|
|
||||||
When classes implement a ``__pt_repr__`` method, this will be used instead of
|
|
||||||
``__repr__`` for printing. Any `prompt_toolkit "formatted text"
|
|
||||||
<https://python-prompt-toolkit.readthedocs.io/en/master/pages/printing_text.html>`_
|
|
||||||
can be returned from here. In order to avoid writing a ``__repr__`` as well,
|
|
||||||
the ``ptpython.utils.ptrepr_to_repr`` decorator can be applied. For instance:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.utils import ptrepr_to_repr
|
|
||||||
from prompt_toolkit.formatted_text import HTML
|
|
||||||
|
|
||||||
@ptrepr_to_repr
|
|
||||||
class MyClass:
|
|
||||||
def __pt_repr__(self):
|
|
||||||
return HTML('<yellow>Hello world!</yellow>')
|
|
||||||
|
|
||||||
More screenshots
|
|
||||||
****************
|
|
||||||
|
|
||||||
The configuration menu:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-menu.png
|
|
||||||
|
|
||||||
The history page and its help:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-history-help.png
|
|
||||||
|
|
||||||
Autocompletion:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/file-completion.png
|
|
||||||
|
|
||||||
|
|
||||||
Embedding the REPL
|
|
||||||
******************
|
|
||||||
|
|
||||||
Embedding the REPL in any Python application is easy:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.repl import embed
|
|
||||||
embed(globals(), locals())
|
|
||||||
|
|
||||||
You can make ptpython your default Python REPL by creating a `PYTHONSTARTUP file
|
|
||||||
<https://docs.python.org/3/tutorial/appendix.html#the-interactive-startup-file>`_ containing code
|
|
||||||
like this:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
try:
|
|
||||||
from ptpython.repl import embed
|
|
||||||
except ImportError:
|
|
||||||
print("ptpython is not available: falling back to standard prompt")
|
|
||||||
else:
|
|
||||||
sys.exit(embed(globals(), locals()))
|
|
||||||
|
|
||||||
|
|
||||||
Multiline editing
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Multi-line editing mode will automatically turn on when you press enter after a
|
|
||||||
colon.
|
|
||||||
|
|
||||||
To execute the input in multi-line mode, you can either press ``Alt+Enter``, or
|
|
||||||
``Esc`` followed by ``Enter``. (If you want the first to work in the OS X
|
|
||||||
terminal, you have to check the "Use option as meta key" checkbox in your
|
|
||||||
terminal settings. For iTerm2, you have to check "Left option acts as +Esc" in
|
|
||||||
the options.)
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/multiline.png
|
|
||||||
|
|
||||||
|
|
||||||
Syntax validation
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Before execution, ``ptpython`` will see whether the input is syntactically
|
|
||||||
correct Python code. If not, it will show a warning, and move the cursor to the
|
|
||||||
error.
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/validation.png
|
|
||||||
|
|
||||||
|
|
||||||
Additional features
|
|
||||||
*******************
|
|
||||||
|
|
||||||
Running system commands: Press ``Meta-!`` in Emacs mode or just ``!`` in Vi
|
|
||||||
navigation mode to see the "Shell command" prompt. There you can enter system
|
|
||||||
commands without leaving the REPL.
|
|
||||||
|
|
||||||
Selecting text: Press ``Control+Space`` in Emacs mode or ``V`` (major V) in Vi
|
|
||||||
navigation mode.
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
*************
|
|
||||||
|
|
||||||
It is possible to create a ``config.py`` file to customize configuration.
|
|
||||||
ptpython will look in an appropriate platform-specific directory via `appdirs
|
|
||||||
<https://pypi.org/project/appdirs/>`. See the ``appdirs`` documentation for the
|
|
||||||
precise location for your platform. A ``PTPYTHON_CONFIG_HOME`` environment
|
|
||||||
variable, if set, can also be used to explicitly override where configuration
|
|
||||||
is looked for.
|
|
||||||
|
|
||||||
Have a look at this example to see what is possible:
|
|
||||||
`config.py <https://github.com/jonathanslenders/ptpython/blob/master/examples/ptpython_config/config.py>`_
|
|
||||||
|
|
||||||
|
|
||||||
IPython support
|
|
||||||
***************
|
|
||||||
|
|
||||||
Run ``ptipython`` (prompt_toolkit - IPython), to get a nice interactive shell
|
|
||||||
with all the power that IPython has to offer, like magic functions and shell
|
|
||||||
integration. Make sure that IPython has been installed. (``pip install
|
|
||||||
ipython``)
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ipython.png
|
|
||||||
|
|
||||||
This is also available for embedding:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.ipython.repl import embed
|
|
||||||
embed(globals(), locals())
|
|
||||||
|
|
||||||
|
|
||||||
Django support
|
|
||||||
**************
|
|
||||||
|
|
||||||
`django-extensions <https://github.com/django-extensions/django-extensions>`_
|
|
||||||
has a ``shell_plus`` management command. When ``ptpython`` has been installed,
|
|
||||||
it will by default use ``ptpython`` or ``ptipython``.
|
|
||||||
|
|
||||||
|
|
||||||
PDB
|
|
||||||
***
|
|
||||||
|
|
||||||
There is an experimental PDB replacement: `ptpdb
|
|
||||||
<https://github.com/jonathanslenders/ptpdb>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Windows support
|
|
||||||
***************
|
|
||||||
|
|
||||||
``prompt_toolkit`` and ``ptpython`` works better on Linux and OS X than on
|
|
||||||
Windows. Some things might not work, but it is usable:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/windows.png
|
|
||||||
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
***
|
|
||||||
|
|
||||||
**Q**: The ``Ctrl-S`` forward search doesn't work and freezes my terminal.
|
|
||||||
|
|
||||||
**A**: Try to run ``stty -ixon`` in your terminal to disable flow control.
|
|
||||||
|
|
||||||
**Q**: The ``Meta``-key doesn't work.
|
|
||||||
|
|
||||||
**A**: For some terminals you have to enable the Alt-key to act as meta key, but you
|
|
||||||
can also type ``Escape`` before any key instead.
|
|
||||||
|
|
||||||
|
|
||||||
Alternatives
|
|
||||||
************
|
|
||||||
|
|
||||||
- `BPython <http://bpython-interpreter.org/downloads.html>`_
|
|
||||||
- `IPython <https://ipython.org/>`_
|
|
||||||
|
|
||||||
If you find another alternative, you can create an issue and we'll list it
|
|
||||||
here. If you find a nice feature somewhere that is missing in ``ptpython``,
|
|
||||||
also create a GitHub issue and maybe we'll implement it.
|
|
||||||
|
|
||||||
|
|
||||||
Special thanks to
|
|
||||||
*****************
|
|
||||||
|
|
||||||
- `Pygments <http://pygments.org/>`_: Syntax highlighter.
|
|
||||||
- `Jedi <http://jedi.jedidjah.ch/en/latest/>`_: Autocompletion library.
|
|
||||||
- `wcwidth <https://github.com/jquast/wcwidth>`_: Determine columns needed for a wide characters.
|
|
||||||
- `prompt_toolkit <http://github.com/jonathanslenders/python-prompt-toolkit>`_ for the interface.
|
|
||||||
|
|
||||||
.. |Build Status| image:: https://api.travis-ci.org/prompt-toolkit/ptpython.svg?branch=master
|
|
||||||
:target: https://travis-ci.org/prompt-toolkit/ptpython#
|
|
||||||
|
|
||||||
.. |License| image:: https://img.shields.io/github/license/prompt-toolkit/ptpython.svg
|
|
||||||
:target: https://github.com/prompt-toolkit/ptpython/blob/master/LICENSE
|
|
||||||
|
|
||||||
.. |PyPI| image:: https://pypip.in/version/ptpython/badge.svg
|
|
||||||
:target: https://pypi.python.org/pypi/ptpython/
|
|
||||||
:alt: Latest Version
|
|
||||||
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
|
||||||
Classifier: Programming Language :: Python :: 3.7
|
|
||||||
Classifier: Programming Language :: Python :: 3.8
|
|
||||||
Classifier: Programming Language :: Python :: 3 :: Only
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Requires-Python: >=3.6
|
|
||||||
Provides-Extra: ptipython
|
|
||||||
Provides-Extra: all
|
|
41
README.rst
41
README.rst
|
@ -50,6 +50,40 @@ Features
|
||||||
[2] If the terminal supports it (most terminals do), this allows pasting
|
[2] If the terminal supports it (most terminals do), this allows pasting
|
||||||
without going into paste mode. It will keep the indentation.
|
without going into paste mode. It will keep the indentation.
|
||||||
|
|
||||||
|
Command Line Options
|
||||||
|
********************
|
||||||
|
|
||||||
|
The help menu shows basic command-line options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ ptpython --help
|
||||||
|
usage: ptpython [-h] [--vi] [-i] [--light-bg] [--dark-bg] [--config-file CONFIG_FILE]
|
||||||
|
[--history-file HISTORY_FILE] [-V]
|
||||||
|
[args ...]
|
||||||
|
|
||||||
|
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.
|
||||||
|
--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)
|
||||||
|
|
||||||
|
|
||||||
__pt_repr__: A nicer repr with colors
|
__pt_repr__: A nicer repr with colors
|
||||||
*************************************
|
*************************************
|
||||||
|
|
||||||
|
@ -109,6 +143,8 @@ like this:
|
||||||
else:
|
else:
|
||||||
sys.exit(embed(globals(), locals()))
|
sys.exit(embed(globals(), locals()))
|
||||||
|
|
||||||
|
Note config file support currently only works when invoking `ptpython` directly.
|
||||||
|
That it, the config file will be ignored when embedding ptpython in an application.
|
||||||
|
|
||||||
Multiline editing
|
Multiline editing
|
||||||
*****************
|
*****************
|
||||||
|
@ -159,6 +195,9 @@ is looked for.
|
||||||
Have a look at this example to see what is possible:
|
Have a look at this example to see what is possible:
|
||||||
`config.py <https://github.com/jonathanslenders/ptpython/blob/master/examples/ptpython_config/config.py>`_
|
`config.py <https://github.com/jonathanslenders/ptpython/blob/master/examples/ptpython_config/config.py>`_
|
||||||
|
|
||||||
|
Note config file support currently only works when invoking `ptpython` directly.
|
||||||
|
That it, the config file will be ignored when embedding ptpython in an application.
|
||||||
|
|
||||||
|
|
||||||
IPython support
|
IPython support
|
||||||
***************
|
***************
|
||||||
|
@ -211,7 +250,7 @@ FAQ
|
||||||
|
|
||||||
**Q**: The ``Meta``-key doesn't work.
|
**Q**: The ``Meta``-key doesn't work.
|
||||||
|
|
||||||
**A**: For some terminals you have to enable the Alt-key to act as meta key, but you
|
**A**: For some terminals you have to enable the Alt-key to act as meta key, but you
|
||||||
can also type ``Escape`` before any key instead.
|
can also type ``Escape`` before any key instead.
|
||||||
|
|
||||||
|
|
||||||
|
|
91
docs/concurrency-challenges.rst
Normal file
91
docs/concurrency-challenges.rst
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
|
||||||
|
Concurrency-related challenges regarding embedding of ptpython in asyncio code
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
Things we want to be possible
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Embed blocking ptpython in non-asyncio code (the normal use case).
|
||||||
|
- Embed blocking ptpython in asyncio code (the event loop will block).
|
||||||
|
- Embed awaitable ptpython in asyncio code (the loop will continue).
|
||||||
|
- React to resize events (SIGWINCH).
|
||||||
|
- Support top-level await.
|
||||||
|
- Be able to patch_stdout, so that logging messages from another thread will be
|
||||||
|
printed above the prompt.
|
||||||
|
- It should be possible to handle `KeyboardInterrupt` during evaluation of an
|
||||||
|
expression.
|
||||||
|
- The "eval" should happen in the same thread from where embed() was called.
|
||||||
|
|
||||||
|
|
||||||
|
Limitations of asyncio/python
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- We can only listen to SIGWINCH signal (resize) events in the main thread.
|
||||||
|
|
||||||
|
- Usage of Control-C for triggering a `KeyboardInterrupt` only works for code
|
||||||
|
running in the main thread. (And only if the terminal was not set in raw
|
||||||
|
input mode).
|
||||||
|
|
||||||
|
- Spawning a new event loop from within a coroutine, that's being executed in
|
||||||
|
an existing event loop is not allowed in asyncio. We can however spawn any
|
||||||
|
event loop in a separate thread, and wait for that thread to finish.
|
||||||
|
|
||||||
|
- For patch_stdout to work correctly, we have to know what prompt_toolkit
|
||||||
|
application is running on the terminal, then tell that application to print
|
||||||
|
the output and redraw itself.
|
||||||
|
|
||||||
|
|
||||||
|
Additional challenges for IPython
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
IPython supports integration of 3rd party event loops (for various GUI
|
||||||
|
toolkits). These event loops are supposed to continue running while we are
|
||||||
|
prompting for input. In an asyncio environment, it means that there are
|
||||||
|
situations where we have to juggle three event loops:
|
||||||
|
|
||||||
|
- The asyncio loop in which the code was embedded.
|
||||||
|
- The asyncio loop from the prompt.
|
||||||
|
- The 3rd party GUI loop.
|
||||||
|
|
||||||
|
Approach taken in ptpython 3.0.11
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
For ptpython, the most reliable solution is to to run the prompt_toolkit input
|
||||||
|
prompt in a separate background thread. This way it can use its own asyncio
|
||||||
|
event loop without ever having to interfere with whatever runs in the main
|
||||||
|
thread.
|
||||||
|
|
||||||
|
Then, depending on how we embed, we do the following:
|
||||||
|
When a normal blocking embed is used:
|
||||||
|
* We start the UI thread for the input, and do a blocking wait on
|
||||||
|
`thread.join()` here.
|
||||||
|
* The "eval" happens in the main thread.
|
||||||
|
* The "print" happens also in the main thread. Unless a pager is shown,
|
||||||
|
which is also a prompt_toolkit application, then the pager itself is runs
|
||||||
|
also in another thread, similar to the way we do the input.
|
||||||
|
|
||||||
|
When an awaitable embed is used, for embedding in a coroutine, but having the
|
||||||
|
event loop continue:
|
||||||
|
* We run the input method from the blocking embed in an asyncio executor
|
||||||
|
and do an `await loop.run_in_excecutor(...)`.
|
||||||
|
* The "eval" happens again in the main thread.
|
||||||
|
* "print" is also similar, except that the pager code (if used) runs in an
|
||||||
|
executor too.
|
||||||
|
|
||||||
|
This means that the prompt_toolkit application code will always run in a
|
||||||
|
different thread. It means it won't be able to respond to SIGWINCH (window
|
||||||
|
resize events), but prompt_toolkit's 3.0.11 has now terminal size polling which
|
||||||
|
solves this.
|
||||||
|
|
||||||
|
Control-C key presses won't interrupt the main thread while we wait for input,
|
||||||
|
because the prompt_toolkit application turns the terminal in raw mode, while
|
||||||
|
it's reading, which means that it will receive control-c key presses as raw
|
||||||
|
data in its own thread.
|
||||||
|
|
||||||
|
Top-level await works in most situations as expected.
|
||||||
|
- If a blocking embed is used. We execute ``loop.run_until_complete(code)``.
|
||||||
|
This assumes that the blocking embed is not used in a coroutine of a running
|
||||||
|
event loop, otherwise, this will attempt to start a nested event loop, which
|
||||||
|
asyncio does not support. In that case we will get an exception.
|
||||||
|
- If an awaitable embed is used. We literally execute ``await code``. This will
|
||||||
|
integrate nicely in the current event loop.
|
|
@ -106,8 +106,13 @@ def configure(repl):
|
||||||
repl.enable_input_validation = True
|
repl.enable_input_validation = True
|
||||||
|
|
||||||
# Use this colorscheme for the code.
|
# Use this colorscheme for the code.
|
||||||
|
# Ptpython uses Pygments for code styling, so you can choose from Pygments'
|
||||||
|
# color schemes. See:
|
||||||
|
# https://pygments.org/docs/styles/
|
||||||
|
# https://pygments.org/demo/
|
||||||
repl.use_code_colorscheme("default")
|
repl.use_code_colorscheme("default")
|
||||||
# repl.use_code_colorscheme("pastie")
|
# A colorscheme that looks good on dark backgrounds is 'native':
|
||||||
|
# repl.use_code_colorscheme("native")
|
||||||
|
|
||||||
# Set color depth (keep in mind that not all terminals support true color).
|
# Set color depth (keep in mind that not all terminals support true color).
|
||||||
|
|
||||||
|
|
6
mypy.ini
Normal file
6
mypy.ini
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[mypy]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
no_implicit_optional = True
|
||||||
|
platform = win32
|
||||||
|
strict_equality = True
|
||||||
|
strict_optional = True
|
|
@ -1,263 +0,0 @@
|
||||||
Metadata-Version: 2.1
|
|
||||||
Name: ptpython
|
|
||||||
Version: 3.0.20
|
|
||||||
Summary: Python REPL build on top of prompt_toolkit
|
|
||||||
Home-page: https://github.com/prompt-toolkit/ptpython
|
|
||||||
Author: Jonathan Slenders
|
|
||||||
License: UNKNOWN
|
|
||||||
Description: ptpython
|
|
||||||
========
|
|
||||||
|
|
||||||
|Build Status| |PyPI| |License|
|
|
||||||
|
|
||||||
*A better Python REPL*
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
pip install ptpython
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/example1.png
|
|
||||||
|
|
||||||
Ptpython is an advanced Python REPL. It should work on all
|
|
||||||
Python versions from 2.6 up to 3.9 and work cross platform (Linux,
|
|
||||||
BSD, OS X and Windows).
|
|
||||||
|
|
||||||
Note: this version of ptpython requires at least Python 3.6. Install ptpython
|
|
||||||
2.0.5 for older Python versions.
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
************
|
|
||||||
|
|
||||||
Install it using pip:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
pip install ptpython
|
|
||||||
|
|
||||||
Start it by typing ``ptpython``.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
|
||||||
********
|
|
||||||
|
|
||||||
- Syntax highlighting.
|
|
||||||
- Multiline editing (the up arrow works).
|
|
||||||
- Autocompletion.
|
|
||||||
- Mouse support. [1]
|
|
||||||
- Support for color schemes.
|
|
||||||
- Support for `bracketed paste <https://cirw.in/blog/bracketed-paste>`_ [2].
|
|
||||||
- Both Vi and Emacs key bindings.
|
|
||||||
- Support for double width (Chinese) characters.
|
|
||||||
- ... and many other things.
|
|
||||||
|
|
||||||
|
|
||||||
[1] Disabled by default. (Enable in the menu.)
|
|
||||||
|
|
||||||
[2] If the terminal supports it (most terminals do), this allows pasting
|
|
||||||
without going into paste mode. It will keep the indentation.
|
|
||||||
|
|
||||||
__pt_repr__: A nicer repr with colors
|
|
||||||
*************************************
|
|
||||||
|
|
||||||
When classes implement a ``__pt_repr__`` method, this will be used instead of
|
|
||||||
``__repr__`` for printing. Any `prompt_toolkit "formatted text"
|
|
||||||
<https://python-prompt-toolkit.readthedocs.io/en/master/pages/printing_text.html>`_
|
|
||||||
can be returned from here. In order to avoid writing a ``__repr__`` as well,
|
|
||||||
the ``ptpython.utils.ptrepr_to_repr`` decorator can be applied. For instance:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.utils import ptrepr_to_repr
|
|
||||||
from prompt_toolkit.formatted_text import HTML
|
|
||||||
|
|
||||||
@ptrepr_to_repr
|
|
||||||
class MyClass:
|
|
||||||
def __pt_repr__(self):
|
|
||||||
return HTML('<yellow>Hello world!</yellow>')
|
|
||||||
|
|
||||||
More screenshots
|
|
||||||
****************
|
|
||||||
|
|
||||||
The configuration menu:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-menu.png
|
|
||||||
|
|
||||||
The history page and its help:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ptpython-history-help.png
|
|
||||||
|
|
||||||
Autocompletion:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/file-completion.png
|
|
||||||
|
|
||||||
|
|
||||||
Embedding the REPL
|
|
||||||
******************
|
|
||||||
|
|
||||||
Embedding the REPL in any Python application is easy:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.repl import embed
|
|
||||||
embed(globals(), locals())
|
|
||||||
|
|
||||||
You can make ptpython your default Python REPL by creating a `PYTHONSTARTUP file
|
|
||||||
<https://docs.python.org/3/tutorial/appendix.html#the-interactive-startup-file>`_ containing code
|
|
||||||
like this:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
try:
|
|
||||||
from ptpython.repl import embed
|
|
||||||
except ImportError:
|
|
||||||
print("ptpython is not available: falling back to standard prompt")
|
|
||||||
else:
|
|
||||||
sys.exit(embed(globals(), locals()))
|
|
||||||
|
|
||||||
|
|
||||||
Multiline editing
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Multi-line editing mode will automatically turn on when you press enter after a
|
|
||||||
colon.
|
|
||||||
|
|
||||||
To execute the input in multi-line mode, you can either press ``Alt+Enter``, or
|
|
||||||
``Esc`` followed by ``Enter``. (If you want the first to work in the OS X
|
|
||||||
terminal, you have to check the "Use option as meta key" checkbox in your
|
|
||||||
terminal settings. For iTerm2, you have to check "Left option acts as +Esc" in
|
|
||||||
the options.)
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/multiline.png
|
|
||||||
|
|
||||||
|
|
||||||
Syntax validation
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Before execution, ``ptpython`` will see whether the input is syntactically
|
|
||||||
correct Python code. If not, it will show a warning, and move the cursor to the
|
|
||||||
error.
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/validation.png
|
|
||||||
|
|
||||||
|
|
||||||
Additional features
|
|
||||||
*******************
|
|
||||||
|
|
||||||
Running system commands: Press ``Meta-!`` in Emacs mode or just ``!`` in Vi
|
|
||||||
navigation mode to see the "Shell command" prompt. There you can enter system
|
|
||||||
commands without leaving the REPL.
|
|
||||||
|
|
||||||
Selecting text: Press ``Control+Space`` in Emacs mode or ``V`` (major V) in Vi
|
|
||||||
navigation mode.
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
*************
|
|
||||||
|
|
||||||
It is possible to create a ``config.py`` file to customize configuration.
|
|
||||||
ptpython will look in an appropriate platform-specific directory via `appdirs
|
|
||||||
<https://pypi.org/project/appdirs/>`. See the ``appdirs`` documentation for the
|
|
||||||
precise location for your platform. A ``PTPYTHON_CONFIG_HOME`` environment
|
|
||||||
variable, if set, can also be used to explicitly override where configuration
|
|
||||||
is looked for.
|
|
||||||
|
|
||||||
Have a look at this example to see what is possible:
|
|
||||||
`config.py <https://github.com/jonathanslenders/ptpython/blob/master/examples/ptpython_config/config.py>`_
|
|
||||||
|
|
||||||
|
|
||||||
IPython support
|
|
||||||
***************
|
|
||||||
|
|
||||||
Run ``ptipython`` (prompt_toolkit - IPython), to get a nice interactive shell
|
|
||||||
with all the power that IPython has to offer, like magic functions and shell
|
|
||||||
integration. Make sure that IPython has been installed. (``pip install
|
|
||||||
ipython``)
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/ipython.png
|
|
||||||
|
|
||||||
This is also available for embedding:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
from ptpython.ipython.repl import embed
|
|
||||||
embed(globals(), locals())
|
|
||||||
|
|
||||||
|
|
||||||
Django support
|
|
||||||
**************
|
|
||||||
|
|
||||||
`django-extensions <https://github.com/django-extensions/django-extensions>`_
|
|
||||||
has a ``shell_plus`` management command. When ``ptpython`` has been installed,
|
|
||||||
it will by default use ``ptpython`` or ``ptipython``.
|
|
||||||
|
|
||||||
|
|
||||||
PDB
|
|
||||||
***
|
|
||||||
|
|
||||||
There is an experimental PDB replacement: `ptpdb
|
|
||||||
<https://github.com/jonathanslenders/ptpdb>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Windows support
|
|
||||||
***************
|
|
||||||
|
|
||||||
``prompt_toolkit`` and ``ptpython`` works better on Linux and OS X than on
|
|
||||||
Windows. Some things might not work, but it is usable:
|
|
||||||
|
|
||||||
.. image :: https://github.com/jonathanslenders/ptpython/raw/master/docs/images/windows.png
|
|
||||||
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
***
|
|
||||||
|
|
||||||
**Q**: The ``Ctrl-S`` forward search doesn't work and freezes my terminal.
|
|
||||||
|
|
||||||
**A**: Try to run ``stty -ixon`` in your terminal to disable flow control.
|
|
||||||
|
|
||||||
**Q**: The ``Meta``-key doesn't work.
|
|
||||||
|
|
||||||
**A**: For some terminals you have to enable the Alt-key to act as meta key, but you
|
|
||||||
can also type ``Escape`` before any key instead.
|
|
||||||
|
|
||||||
|
|
||||||
Alternatives
|
|
||||||
************
|
|
||||||
|
|
||||||
- `BPython <http://bpython-interpreter.org/downloads.html>`_
|
|
||||||
- `IPython <https://ipython.org/>`_
|
|
||||||
|
|
||||||
If you find another alternative, you can create an issue and we'll list it
|
|
||||||
here. If you find a nice feature somewhere that is missing in ``ptpython``,
|
|
||||||
also create a GitHub issue and maybe we'll implement it.
|
|
||||||
|
|
||||||
|
|
||||||
Special thanks to
|
|
||||||
*****************
|
|
||||||
|
|
||||||
- `Pygments <http://pygments.org/>`_: Syntax highlighter.
|
|
||||||
- `Jedi <http://jedi.jedidjah.ch/en/latest/>`_: Autocompletion library.
|
|
||||||
- `wcwidth <https://github.com/jquast/wcwidth>`_: Determine columns needed for a wide characters.
|
|
||||||
- `prompt_toolkit <http://github.com/jonathanslenders/python-prompt-toolkit>`_ for the interface.
|
|
||||||
|
|
||||||
.. |Build Status| image:: https://api.travis-ci.org/prompt-toolkit/ptpython.svg?branch=master
|
|
||||||
:target: https://travis-ci.org/prompt-toolkit/ptpython#
|
|
||||||
|
|
||||||
.. |License| image:: https://img.shields.io/github/license/prompt-toolkit/ptpython.svg
|
|
||||||
:target: https://github.com/prompt-toolkit/ptpython/blob/master/LICENSE
|
|
||||||
|
|
||||||
.. |PyPI| image:: https://pypip.in/version/ptpython/badge.svg
|
|
||||||
:target: https://pypi.python.org/pypi/ptpython/
|
|
||||||
:alt: Latest Version
|
|
||||||
|
|
||||||
Platform: UNKNOWN
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
|
||||||
Classifier: Programming Language :: Python :: 3.7
|
|
||||||
Classifier: Programming Language :: Python :: 3.8
|
|
||||||
Classifier: Programming Language :: Python :: 3 :: Only
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Requires-Python: >=3.6
|
|
||||||
Provides-Extra: ptipython
|
|
||||||
Provides-Extra: all
|
|
|
@ -1,54 +0,0 @@
|
||||||
.gitignore
|
|
||||||
CHANGELOG
|
|
||||||
LICENSE
|
|
||||||
MANIFEST.in
|
|
||||||
README.rst
|
|
||||||
pyproject.toml
|
|
||||||
setup.cfg
|
|
||||||
setup.py
|
|
||||||
docs/images/example1.png
|
|
||||||
docs/images/file-completion.png
|
|
||||||
docs/images/ipython.png
|
|
||||||
docs/images/multiline.png
|
|
||||||
docs/images/ptpython-history-help.png
|
|
||||||
docs/images/ptpython-menu.png
|
|
||||||
docs/images/ptpython.png
|
|
||||||
docs/images/validation.png
|
|
||||||
docs/images/windows.png
|
|
||||||
examples/asyncio-python-embed.py
|
|
||||||
examples/asyncio-ssh-python-embed.py
|
|
||||||
examples/python-embed-with-custom-prompt.py
|
|
||||||
examples/python-embed.py
|
|
||||||
examples/python-input.py
|
|
||||||
examples/ssh-and-telnet-embed.py
|
|
||||||
examples/ptpython_config/config.py
|
|
||||||
examples/test-cases/ptpython-in-other-thread.py
|
|
||||||
ptpython/__init__.py
|
|
||||||
ptpython/__main__.py
|
|
||||||
ptpython/completer.py
|
|
||||||
ptpython/eventloop.py
|
|
||||||
ptpython/filters.py
|
|
||||||
ptpython/history_browser.py
|
|
||||||
ptpython/ipython.py
|
|
||||||
ptpython/key_bindings.py
|
|
||||||
ptpython/layout.py
|
|
||||||
ptpython/lexer.py
|
|
||||||
ptpython/prompt_style.py
|
|
||||||
ptpython/python_input.py
|
|
||||||
ptpython/repl.py
|
|
||||||
ptpython/signatures.py
|
|
||||||
ptpython/style.py
|
|
||||||
ptpython/utils.py
|
|
||||||
ptpython/validator.py
|
|
||||||
ptpython.egg-info/PKG-INFO
|
|
||||||
ptpython.egg-info/SOURCES.txt
|
|
||||||
ptpython.egg-info/dependency_links.txt
|
|
||||||
ptpython.egg-info/entry_points.txt
|
|
||||||
ptpython.egg-info/requires.txt
|
|
||||||
ptpython.egg-info/top_level.txt
|
|
||||||
ptpython/contrib/__init__.py
|
|
||||||
ptpython/contrib/asyncssh_repl.py
|
|
||||||
ptpython/entry_points/__init__.py
|
|
||||||
ptpython/entry_points/run_ptipython.py
|
|
||||||
ptpython/entry_points/run_ptpython.py
|
|
||||||
tests/run_tests.py
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
[console_scripts]
|
|
||||||
ptipython = ptpython.entry_points.run_ptipython:run
|
|
||||||
ptipython3 = ptpython.entry_points.run_ptipython:run
|
|
||||||
ptipython3.9 = ptpython.entry_points.run_ptipython:run
|
|
||||||
ptpython = ptpython.entry_points.run_ptpython:run
|
|
||||||
ptpython3 = ptpython.entry_points.run_ptpython:run
|
|
||||||
ptpython3.9 = ptpython.entry_points.run_ptpython:run
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
appdirs
|
|
||||||
jedi>=0.16.0
|
|
||||||
prompt_toolkit<3.1.0,>=3.0.18
|
|
||||||
pygments
|
|
||||||
|
|
||||||
[:python_version < "3.8"]
|
|
||||||
importlib_metadata
|
|
||||||
|
|
||||||
[all]
|
|
||||||
black
|
|
||||||
|
|
||||||
[ptipython]
|
|
||||||
ipython
|
|
|
@ -1 +0,0 @@
|
||||||
ptpython
|
|
|
@ -4,7 +4,7 @@ import inspect
|
||||||
import keyword
|
import keyword
|
||||||
import re
|
import re
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple
|
||||||
|
|
||||||
from prompt_toolkit.completion import (
|
from prompt_toolkit.completion import (
|
||||||
CompleteEvent,
|
CompleteEvent,
|
||||||
|
@ -21,6 +21,7 @@ from prompt_toolkit.formatted_text import fragment_list_to_text, to_formatted_te
|
||||||
from ptpython.utils import get_jedi_script_from_document
|
from ptpython.utils import get_jedi_script_from_document
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
import jedi.api.classes
|
||||||
from prompt_toolkit.contrib.regular_languages.compiler import _CompiledGrammar
|
from prompt_toolkit.contrib.regular_languages.compiler import _CompiledGrammar
|
||||||
|
|
||||||
__all__ = ["PythonCompleter", "CompletePrivateAttributes", "HidePrivateCompleter"]
|
__all__ = ["PythonCompleter", "CompletePrivateAttributes", "HidePrivateCompleter"]
|
||||||
|
@ -43,8 +44,8 @@ class PythonCompleter(Completer):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
get_globals: Callable[[], dict],
|
get_globals: Callable[[], Dict[str, Any]],
|
||||||
get_locals: Callable[[], dict],
|
get_locals: Callable[[], Dict[str, Any]],
|
||||||
enable_dictionary_completion: Callable[[], bool],
|
enable_dictionary_completion: Callable[[], bool],
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -200,7 +201,11 @@ class JediCompleter(Completer):
|
||||||
Autocompleter that uses the Jedi library.
|
Autocompleter that uses the Jedi library.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, get_globals, get_locals) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
get_globals: Callable[[], Dict[str, Any]],
|
||||||
|
get_locals: Callable[[], Dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.get_globals = get_globals
|
self.get_globals = get_globals
|
||||||
|
@ -296,7 +301,11 @@ class DictionaryCompleter(Completer):
|
||||||
function calls, so it only triggers attribute access.
|
function calls, so it only triggers attribute access.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, get_globals, get_locals):
|
def __init__(
|
||||||
|
self,
|
||||||
|
get_globals: Callable[[], Dict[str, Any]],
|
||||||
|
get_locals: Callable[[], Dict[str, Any]],
|
||||||
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.get_globals = get_globals
|
self.get_globals = get_globals
|
||||||
|
@ -495,7 +504,7 @@ class DictionaryCompleter(Completer):
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
for k in result:
|
for k, v in result.items():
|
||||||
if str(k).startswith(str(key_obj)):
|
if str(k).startswith(str(key_obj)):
|
||||||
try:
|
try:
|
||||||
k_repr = self._do_repr(k)
|
k_repr = self._do_repr(k)
|
||||||
|
@ -503,7 +512,7 @@ class DictionaryCompleter(Completer):
|
||||||
k_repr + "]",
|
k_repr + "]",
|
||||||
-len(key),
|
-len(key),
|
||||||
display=f"[{k_repr}]",
|
display=f"[{k_repr}]",
|
||||||
display_meta=abbr_meta(self._do_repr(result[k])),
|
display_meta=abbr_meta(self._do_repr(v)),
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# `result[k]` lookup failed. Trying to complete
|
# `result[k]` lookup failed. Trying to complete
|
||||||
|
@ -574,7 +583,7 @@ class DictionaryCompleter(Completer):
|
||||||
underscore names to the end.
|
underscore names to the end.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def sort_key(name: str):
|
def sort_key(name: str) -> Tuple[int, str]:
|
||||||
if name.startswith("__"):
|
if name.startswith("__"):
|
||||||
return (2, name) # Double underscore comes latest.
|
return (2, name) # Double underscore comes latest.
|
||||||
if name.startswith("_"):
|
if name.startswith("_"):
|
||||||
|
@ -639,7 +648,9 @@ except ImportError: # Python 2.
|
||||||
_builtin_names = []
|
_builtin_names = []
|
||||||
|
|
||||||
|
|
||||||
def _get_style_for_jedi_completion(jedi_completion) -> str:
|
def _get_style_for_jedi_completion(
|
||||||
|
jedi_completion: "jedi.api.classes.Completion",
|
||||||
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Return completion style to use for this name.
|
Return completion style to use for this name.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -26,16 +26,16 @@ import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import Tuple
|
from typing import IO, Optional, Tuple
|
||||||
|
|
||||||
import appdirs
|
import appdirs
|
||||||
from prompt_toolkit.formatted_text import HTML
|
from prompt_toolkit.formatted_text import HTML
|
||||||
from prompt_toolkit.shortcuts import print_formatted_text
|
from prompt_toolkit.shortcuts import print_formatted_text
|
||||||
|
|
||||||
from ptpython.repl import embed, enable_deprecation_warnings, run_config
|
from ptpython.repl import PythonRepl, embed, enable_deprecation_warnings, run_config
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from importlib import metadata
|
from importlib import metadata # type: ignore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import importlib_metadata as metadata # type: ignore
|
import importlib_metadata as metadata # type: ignore
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ __all__ = ["create_parser", "get_config_and_history_file", "run"]
|
||||||
|
|
||||||
|
|
||||||
class _Parser(argparse.ArgumentParser):
|
class _Parser(argparse.ArgumentParser):
|
||||||
def print_help(self):
|
def print_help(self, file: Optional[IO[str]] = None) -> None:
|
||||||
super().print_help()
|
super().print_help()
|
||||||
print(
|
print(
|
||||||
dedent(
|
dedent(
|
||||||
|
@ -84,7 +84,7 @@ def create_parser() -> _Parser:
|
||||||
"-V",
|
"-V",
|
||||||
"--version",
|
"--version",
|
||||||
action="version",
|
action="version",
|
||||||
version=metadata.version("ptpython"), # type: ignore
|
version=metadata.version("ptpython"),
|
||||||
)
|
)
|
||||||
parser.add_argument("args", nargs="*", help="Script and arguments")
|
parser.add_argument("args", nargs="*", help="Script and arguments")
|
||||||
return parser
|
return parser
|
||||||
|
@ -190,7 +190,7 @@ def run() -> None:
|
||||||
enable_deprecation_warnings()
|
enable_deprecation_warnings()
|
||||||
|
|
||||||
# Apply config file
|
# Apply config file
|
||||||
def configure(repl) -> None:
|
def configure(repl: PythonRepl) -> None:
|
||||||
if os.path.exists(config_file):
|
if os.path.exists(config_file):
|
||||||
run_config(repl, config_file)
|
run_config(repl, config_file)
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,12 @@ will fix it for Tk.)
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from prompt_toolkit.eventloop import InputHookContext
|
||||||
|
|
||||||
__all__ = ["inputhook"]
|
__all__ = ["inputhook"]
|
||||||
|
|
||||||
|
|
||||||
def _inputhook_tk(inputhook_context):
|
def _inputhook_tk(inputhook_context: InputHookContext) -> None:
|
||||||
"""
|
"""
|
||||||
Inputhook for Tk.
|
Inputhook for Tk.
|
||||||
Run the Tk eventloop until prompt-toolkit needs to process the next input.
|
Run the Tk eventloop until prompt-toolkit needs to process the next input.
|
||||||
|
@ -23,9 +25,9 @@ def _inputhook_tk(inputhook_context):
|
||||||
|
|
||||||
import _tkinter # Keep this imports inline!
|
import _tkinter # Keep this imports inline!
|
||||||
|
|
||||||
root = tkinter._default_root
|
root = tkinter._default_root # type: ignore
|
||||||
|
|
||||||
def wait_using_filehandler():
|
def wait_using_filehandler() -> None:
|
||||||
"""
|
"""
|
||||||
Run the TK eventloop until the file handler that we got from the
|
Run the TK eventloop until the file handler that we got from the
|
||||||
inputhook becomes readable.
|
inputhook becomes readable.
|
||||||
|
@ -34,7 +36,7 @@ def _inputhook_tk(inputhook_context):
|
||||||
# to process.
|
# to process.
|
||||||
stop = [False]
|
stop = [False]
|
||||||
|
|
||||||
def done(*a):
|
def done(*a: object) -> None:
|
||||||
stop[0] = True
|
stop[0] = True
|
||||||
|
|
||||||
root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
|
root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
|
||||||
|
@ -46,7 +48,7 @@ def _inputhook_tk(inputhook_context):
|
||||||
|
|
||||||
root.deletefilehandler(inputhook_context.fileno())
|
root.deletefilehandler(inputhook_context.fileno())
|
||||||
|
|
||||||
def wait_using_polling():
|
def wait_using_polling() -> None:
|
||||||
"""
|
"""
|
||||||
Windows TK doesn't support 'createfilehandler'.
|
Windows TK doesn't support 'createfilehandler'.
|
||||||
So, run the TK eventloop and poll until input is ready.
|
So, run the TK eventloop and poll until input is ready.
|
||||||
|
@ -65,7 +67,7 @@ def _inputhook_tk(inputhook_context):
|
||||||
wait_using_polling()
|
wait_using_polling()
|
||||||
|
|
||||||
|
|
||||||
def inputhook(inputhook_context):
|
def inputhook(inputhook_context: InputHookContext) -> None:
|
||||||
# Only call the real input hook when the 'Tkinter' library was loaded.
|
# Only call the real input hook when the 'Tkinter' library was loaded.
|
||||||
if "Tkinter" in sys.modules or "tkinter" in sys.modules:
|
if "Tkinter" in sys.modules or "tkinter" in sys.modules:
|
||||||
_inputhook_tk(inputhook_context)
|
_inputhook_tk(inputhook_context)
|
||||||
|
|
|
@ -10,6 +10,7 @@ __all__ = ["HasSignature", "ShowSidebar", "ShowSignature", "ShowDocstring"]
|
||||||
|
|
||||||
class PythonInputFilter(Filter):
|
class PythonInputFilter(Filter):
|
||||||
def __init__(self, python_input: "PythonInput") -> None:
|
def __init__(self, python_input: "PythonInput") -> None:
|
||||||
|
super().__init__()
|
||||||
self.python_input = python_input
|
self.python_input = python_input
|
||||||
|
|
||||||
def __call__(self) -> bool:
|
def __call__(self) -> bool:
|
||||||
|
|
|
@ -5,6 +5,7 @@ Utility to easily select lines from the history and execute them again.
|
||||||
run as a sub application of the Repl/PythonInput.
|
run as a sub application of the Repl/PythonInput.
|
||||||
"""
|
"""
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from typing import TYPE_CHECKING, Callable, List, Optional, Set
|
||||||
|
|
||||||
from prompt_toolkit.application import Application
|
from prompt_toolkit.application import Application
|
||||||
from prompt_toolkit.application.current import get_app
|
from prompt_toolkit.application.current import get_app
|
||||||
|
@ -12,8 +13,11 @@ from prompt_toolkit.buffer import Buffer
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||||
from prompt_toolkit.filters import Condition, has_focus
|
from prompt_toolkit.filters import Condition, has_focus
|
||||||
|
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
||||||
from prompt_toolkit.formatted_text.utils import fragment_list_to_text
|
from prompt_toolkit.formatted_text.utils import fragment_list_to_text
|
||||||
|
from prompt_toolkit.history import History
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
||||||
from prompt_toolkit.layout.containers import (
|
from prompt_toolkit.layout.containers import (
|
||||||
ConditionalContainer,
|
ConditionalContainer,
|
||||||
Container,
|
Container,
|
||||||
|
@ -24,13 +28,23 @@ from prompt_toolkit.layout.containers import (
|
||||||
VSplit,
|
VSplit,
|
||||||
Window,
|
Window,
|
||||||
WindowAlign,
|
WindowAlign,
|
||||||
|
WindowRenderInfo,
|
||||||
|
)
|
||||||
|
from prompt_toolkit.layout.controls import (
|
||||||
|
BufferControl,
|
||||||
|
FormattedTextControl,
|
||||||
|
UIContent,
|
||||||
)
|
)
|
||||||
from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
|
|
||||||
from prompt_toolkit.layout.dimension import Dimension as D
|
from prompt_toolkit.layout.dimension import Dimension as D
|
||||||
from prompt_toolkit.layout.layout import Layout
|
from prompt_toolkit.layout.layout import Layout
|
||||||
from prompt_toolkit.layout.margins import Margin, ScrollbarMargin
|
from prompt_toolkit.layout.margins import Margin, ScrollbarMargin
|
||||||
from prompt_toolkit.layout.processors import Processor, Transformation
|
from prompt_toolkit.layout.processors import (
|
||||||
|
Processor,
|
||||||
|
Transformation,
|
||||||
|
TransformationInput,
|
||||||
|
)
|
||||||
from prompt_toolkit.lexers import PygmentsLexer
|
from prompt_toolkit.lexers import PygmentsLexer
|
||||||
|
from prompt_toolkit.mouse_events import MouseEvent
|
||||||
from prompt_toolkit.widgets import Frame
|
from prompt_toolkit.widgets import Frame
|
||||||
from prompt_toolkit.widgets.toolbars import ArgToolbar, SearchToolbar
|
from prompt_toolkit.widgets.toolbars import ArgToolbar, SearchToolbar
|
||||||
from pygments.lexers import Python3Lexer as PythonLexer
|
from pygments.lexers import Python3Lexer as PythonLexer
|
||||||
|
@ -40,10 +54,15 @@ from ptpython.layout import get_inputmode_fragments
|
||||||
|
|
||||||
from .utils import if_mousedown
|
from .utils import if_mousedown
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .python_input import PythonInput
|
||||||
|
|
||||||
HISTORY_COUNT = 2000
|
HISTORY_COUNT = 2000
|
||||||
|
|
||||||
__all__ = ["HistoryLayout", "PythonHistory"]
|
__all__ = ["HistoryLayout", "PythonHistory"]
|
||||||
|
|
||||||
|
E = KeyPressEvent
|
||||||
|
|
||||||
HELP_TEXT = """
|
HELP_TEXT = """
|
||||||
This interface is meant to select multiple lines from the
|
This interface is meant to select multiple lines from the
|
||||||
history and execute them together.
|
history and execute them together.
|
||||||
|
@ -109,7 +128,7 @@ class HistoryLayout:
|
||||||
application.
|
application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, history):
|
def __init__(self, history: "PythonHistory") -> None:
|
||||||
search_toolbar = SearchToolbar()
|
search_toolbar = SearchToolbar()
|
||||||
|
|
||||||
self.help_buffer_control = BufferControl(
|
self.help_buffer_control = BufferControl(
|
||||||
|
@ -201,19 +220,19 @@ class HistoryLayout:
|
||||||
self.layout = Layout(self.root_container, history_window)
|
self.layout = Layout(self.root_container, history_window)
|
||||||
|
|
||||||
|
|
||||||
def _get_top_toolbar_fragments():
|
def _get_top_toolbar_fragments() -> StyleAndTextTuples:
|
||||||
return [("class:status-bar.title", "History browser - Insert from history")]
|
return [("class:status-bar.title", "History browser - Insert from history")]
|
||||||
|
|
||||||
|
|
||||||
def _get_bottom_toolbar_fragments(history):
|
def _get_bottom_toolbar_fragments(history: "PythonHistory") -> StyleAndTextTuples:
|
||||||
python_input = history.python_input
|
python_input = history.python_input
|
||||||
|
|
||||||
@if_mousedown
|
@if_mousedown
|
||||||
def f1(mouse_event):
|
def f1(mouse_event: MouseEvent) -> None:
|
||||||
_toggle_help(history)
|
_toggle_help(history)
|
||||||
|
|
||||||
@if_mousedown
|
@if_mousedown
|
||||||
def tab(mouse_event):
|
def tab(mouse_event: MouseEvent) -> None:
|
||||||
_select_other_window(history)
|
_select_other_window(history)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -239,14 +258,16 @@ class HistoryMargin(Margin):
|
||||||
This displays a green bar for the selected entries.
|
This displays a green bar for the selected entries.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, history):
|
def __init__(self, history: "PythonHistory") -> None:
|
||||||
self.history_buffer = history.history_buffer
|
self.history_buffer = history.history_buffer
|
||||||
self.history_mapping = history.history_mapping
|
self.history_mapping = history.history_mapping
|
||||||
|
|
||||||
def get_width(self, ui_content):
|
def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
def create_margin(self, window_render_info, width, height):
|
def create_margin(
|
||||||
|
self, window_render_info: WindowRenderInfo, width: int, height: int
|
||||||
|
) -> StyleAndTextTuples:
|
||||||
document = self.history_buffer.document
|
document = self.history_buffer.document
|
||||||
|
|
||||||
lines_starting_new_entries = self.history_mapping.lines_starting_new_entries
|
lines_starting_new_entries = self.history_mapping.lines_starting_new_entries
|
||||||
|
@ -255,7 +276,7 @@ class HistoryMargin(Margin):
|
||||||
current_lineno = document.cursor_position_row
|
current_lineno = document.cursor_position_row
|
||||||
|
|
||||||
visible_line_to_input_line = window_render_info.visible_line_to_input_line
|
visible_line_to_input_line = window_render_info.visible_line_to_input_line
|
||||||
result = []
|
result: StyleAndTextTuples = []
|
||||||
|
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
line_number = visible_line_to_input_line.get(y)
|
line_number = visible_line_to_input_line.get(y)
|
||||||
|
@ -286,14 +307,16 @@ class ResultMargin(Margin):
|
||||||
The margin to be shown in the result pane.
|
The margin to be shown in the result pane.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, history):
|
def __init__(self, history: "PythonHistory") -> None:
|
||||||
self.history_mapping = history.history_mapping
|
self.history_mapping = history.history_mapping
|
||||||
self.history_buffer = history.history_buffer
|
self.history_buffer = history.history_buffer
|
||||||
|
|
||||||
def get_width(self, ui_content):
|
def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
def create_margin(self, window_render_info, width, height):
|
def create_margin(
|
||||||
|
self, window_render_info: WindowRenderInfo, width: int, height: int
|
||||||
|
) -> StyleAndTextTuples:
|
||||||
document = self.history_buffer.document
|
document = self.history_buffer.document
|
||||||
|
|
||||||
current_lineno = document.cursor_position_row
|
current_lineno = document.cursor_position_row
|
||||||
|
@ -303,7 +326,7 @@ class ResultMargin(Margin):
|
||||||
|
|
||||||
visible_line_to_input_line = window_render_info.visible_line_to_input_line
|
visible_line_to_input_line = window_render_info.visible_line_to_input_line
|
||||||
|
|
||||||
result = []
|
result: StyleAndTextTuples = []
|
||||||
|
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
line_number = visible_line_to_input_line.get(y)
|
line_number = visible_line_to_input_line.get(y)
|
||||||
|
@ -324,7 +347,7 @@ class ResultMargin(Margin):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def invalidation_hash(self, document):
|
def invalidation_hash(self, document: Document) -> int:
|
||||||
return document.cursor_position_row
|
return document.cursor_position_row
|
||||||
|
|
||||||
|
|
||||||
|
@ -333,13 +356,15 @@ class GrayExistingText(Processor):
|
||||||
Turn the existing input, before and after the inserted code gray.
|
Turn the existing input, before and after the inserted code gray.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, history_mapping):
|
def __init__(self, history_mapping: "HistoryMapping") -> None:
|
||||||
self.history_mapping = history_mapping
|
self.history_mapping = history_mapping
|
||||||
self._lines_before = len(
|
self._lines_before = len(
|
||||||
history_mapping.original_document.text_before_cursor.splitlines()
|
history_mapping.original_document.text_before_cursor.splitlines()
|
||||||
)
|
)
|
||||||
|
|
||||||
def apply_transformation(self, transformation_input):
|
def apply_transformation(
|
||||||
|
self, transformation_input: TransformationInput
|
||||||
|
) -> Transformation:
|
||||||
lineno = transformation_input.lineno
|
lineno = transformation_input.lineno
|
||||||
fragments = transformation_input.fragments
|
fragments = transformation_input.fragments
|
||||||
|
|
||||||
|
@ -357,17 +382,22 @@ class HistoryMapping:
|
||||||
Keep a list of all the lines from the history and the selected lines.
|
Keep a list of all the lines from the history and the selected lines.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, history, python_history, original_document):
|
def __init__(
|
||||||
|
self,
|
||||||
|
history: "PythonHistory",
|
||||||
|
python_history: History,
|
||||||
|
original_document: Document,
|
||||||
|
) -> None:
|
||||||
self.history = history
|
self.history = history
|
||||||
self.python_history = python_history
|
self.python_history = python_history
|
||||||
self.original_document = original_document
|
self.original_document = original_document
|
||||||
|
|
||||||
self.lines_starting_new_entries = set()
|
self.lines_starting_new_entries = set()
|
||||||
self.selected_lines = set()
|
self.selected_lines: Set[int] = set()
|
||||||
|
|
||||||
# Process history.
|
# Process history.
|
||||||
history_strings = python_history.get_strings()
|
history_strings = python_history.get_strings()
|
||||||
history_lines = []
|
history_lines: List[str] = []
|
||||||
|
|
||||||
for entry_nr, entry in list(enumerate(history_strings))[-HISTORY_COUNT:]:
|
for entry_nr, entry in list(enumerate(history_strings))[-HISTORY_COUNT:]:
|
||||||
self.lines_starting_new_entries.add(len(history_lines))
|
self.lines_starting_new_entries.add(len(history_lines))
|
||||||
|
@ -389,7 +419,7 @@ class HistoryMapping:
|
||||||
else:
|
else:
|
||||||
self.result_line_offset = 0
|
self.result_line_offset = 0
|
||||||
|
|
||||||
def get_new_document(self, cursor_pos=None):
|
def get_new_document(self, cursor_pos: Optional[int] = None) -> Document:
|
||||||
"""
|
"""
|
||||||
Create a `Document` instance that contains the resulting text.
|
Create a `Document` instance that contains the resulting text.
|
||||||
"""
|
"""
|
||||||
|
@ -413,13 +443,13 @@ class HistoryMapping:
|
||||||
cursor_pos = len(text)
|
cursor_pos = len(text)
|
||||||
return Document(text, cursor_pos)
|
return Document(text, cursor_pos)
|
||||||
|
|
||||||
def update_default_buffer(self):
|
def update_default_buffer(self) -> None:
|
||||||
b = self.history.default_buffer
|
b = self.history.default_buffer
|
||||||
|
|
||||||
b.set_document(self.get_new_document(b.cursor_position), bypass_readonly=True)
|
b.set_document(self.get_new_document(b.cursor_position), bypass_readonly=True)
|
||||||
|
|
||||||
|
|
||||||
def _toggle_help(history):
|
def _toggle_help(history: "PythonHistory") -> None:
|
||||||
"Display/hide help."
|
"Display/hide help."
|
||||||
help_buffer_control = history.history_layout.help_buffer_control
|
help_buffer_control = history.history_layout.help_buffer_control
|
||||||
|
|
||||||
|
@ -429,7 +459,7 @@ def _toggle_help(history):
|
||||||
history.app.layout.current_control = help_buffer_control
|
history.app.layout.current_control = help_buffer_control
|
||||||
|
|
||||||
|
|
||||||
def _select_other_window(history):
|
def _select_other_window(history: "PythonHistory") -> None:
|
||||||
"Toggle focus between left/right window."
|
"Toggle focus between left/right window."
|
||||||
current_buffer = history.app.current_buffer
|
current_buffer = history.app.current_buffer
|
||||||
layout = history.history_layout.layout
|
layout = history.history_layout.layout
|
||||||
|
@ -441,7 +471,11 @@ def _select_other_window(history):
|
||||||
layout.current_control = history.history_layout.history_buffer_control
|
layout.current_control = history.history_layout.history_buffer_control
|
||||||
|
|
||||||
|
|
||||||
def create_key_bindings(history, python_input, history_mapping):
|
def create_key_bindings(
|
||||||
|
history: "PythonHistory",
|
||||||
|
python_input: "PythonInput",
|
||||||
|
history_mapping: HistoryMapping,
|
||||||
|
) -> KeyBindings:
|
||||||
"""
|
"""
|
||||||
Key bindings.
|
Key bindings.
|
||||||
"""
|
"""
|
||||||
|
@ -449,7 +483,7 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
handle = bindings.add
|
handle = bindings.add
|
||||||
|
|
||||||
@handle(" ", filter=has_focus(history.history_buffer))
|
@handle(" ", filter=has_focus(history.history_buffer))
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Space: select/deselect line from history pane.
|
Space: select/deselect line from history pane.
|
||||||
"""
|
"""
|
||||||
|
@ -486,7 +520,7 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle(" ", filter=has_focus(DEFAULT_BUFFER))
|
@handle(" ", filter=has_focus(DEFAULT_BUFFER))
|
||||||
@handle("delete", filter=has_focus(DEFAULT_BUFFER))
|
@handle("delete", filter=has_focus(DEFAULT_BUFFER))
|
||||||
@handle("c-h", filter=has_focus(DEFAULT_BUFFER))
|
@handle("c-h", filter=has_focus(DEFAULT_BUFFER))
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Space: remove line from default pane.
|
Space: remove line from default pane.
|
||||||
"""
|
"""
|
||||||
|
@ -512,17 +546,17 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle("c-x", filter=main_buffer_focussed, eager=True)
|
@handle("c-x", filter=main_buffer_focussed, eager=True)
|
||||||
# Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding.
|
# Eager: ignore the Emacs [Ctrl-X Ctrl-X] binding.
|
||||||
@handle("c-w", filter=main_buffer_focussed)
|
@handle("c-w", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Select other window."
|
"Select other window."
|
||||||
_select_other_window(history)
|
_select_other_window(history)
|
||||||
|
|
||||||
@handle("f4")
|
@handle("f4")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Switch between Emacs/Vi mode."
|
"Switch between Emacs/Vi mode."
|
||||||
python_input.vi_mode = not python_input.vi_mode
|
python_input.vi_mode = not python_input.vi_mode
|
||||||
|
|
||||||
@handle("f1")
|
@handle("f1")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Display/hide help."
|
"Display/hide help."
|
||||||
_toggle_help(history)
|
_toggle_help(history)
|
||||||
|
|
||||||
|
@ -530,7 +564,7 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle("c-c", filter=help_focussed)
|
@handle("c-c", filter=help_focussed)
|
||||||
@handle("c-g", filter=help_focussed)
|
@handle("c-g", filter=help_focussed)
|
||||||
@handle("escape", filter=help_focussed)
|
@handle("escape", filter=help_focussed)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Leave help."
|
"Leave help."
|
||||||
event.app.layout.focus_previous()
|
event.app.layout.focus_previous()
|
||||||
|
|
||||||
|
@ -538,19 +572,19 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
@handle("f3", filter=main_buffer_focussed)
|
@handle("f3", filter=main_buffer_focussed)
|
||||||
@handle("c-c", filter=main_buffer_focussed)
|
@handle("c-c", filter=main_buffer_focussed)
|
||||||
@handle("c-g", filter=main_buffer_focussed)
|
@handle("c-g", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Cancel and go back."
|
"Cancel and go back."
|
||||||
event.app.exit(result=None)
|
event.app.exit(result=None)
|
||||||
|
|
||||||
@handle("enter", filter=main_buffer_focussed)
|
@handle("enter", filter=main_buffer_focussed)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Accept input."
|
"Accept input."
|
||||||
event.app.exit(result=history.default_buffer.text)
|
event.app.exit(result=history.default_buffer.text)
|
||||||
|
|
||||||
enable_system_bindings = Condition(lambda: python_input.enable_system_bindings)
|
enable_system_bindings = Condition(lambda: python_input.enable_system_bindings)
|
||||||
|
|
||||||
@handle("c-z", filter=enable_system_bindings)
|
@handle("c-z", filter=enable_system_bindings)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Suspend to background."
|
"Suspend to background."
|
||||||
event.app.suspend_to_background()
|
event.app.suspend_to_background()
|
||||||
|
|
||||||
|
@ -558,7 +592,9 @@ def create_key_bindings(history, python_input, history_mapping):
|
||||||
|
|
||||||
|
|
||||||
class PythonHistory:
|
class PythonHistory:
|
||||||
def __init__(self, python_input, original_document):
|
def __init__(
|
||||||
|
self, python_input: "PythonInput", original_document: Document
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Create an `Application` for the history screen.
|
Create an `Application` for the history screen.
|
||||||
This has to be run as a sub application of `python_input`.
|
This has to be run as a sub application of `python_input`.
|
||||||
|
@ -577,12 +613,14 @@ class PythonHistory:
|
||||||
+ document.get_start_of_line_position(),
|
+ document.get_start_of_line_position(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def accept_handler(buffer: Buffer) -> bool:
|
||||||
|
get_app().exit(result=self.default_buffer.text)
|
||||||
|
return False
|
||||||
|
|
||||||
self.history_buffer = Buffer(
|
self.history_buffer = Buffer(
|
||||||
document=document,
|
document=document,
|
||||||
on_cursor_position_changed=self._history_buffer_pos_changed,
|
on_cursor_position_changed=self._history_buffer_pos_changed,
|
||||||
accept_handler=(
|
accept_handler=accept_handler,
|
||||||
lambda buff: get_app().exit(result=self.default_buffer.text)
|
|
||||||
),
|
|
||||||
read_only=True,
|
read_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -597,7 +635,7 @@ class PythonHistory:
|
||||||
|
|
||||||
self.history_layout = HistoryLayout(self)
|
self.history_layout = HistoryLayout(self)
|
||||||
|
|
||||||
self.app = Application(
|
self.app: Application[str] = Application(
|
||||||
layout=self.history_layout.layout,
|
layout=self.history_layout.layout,
|
||||||
full_screen=True,
|
full_screen=True,
|
||||||
style=python_input._current_style,
|
style=python_input._current_style,
|
||||||
|
@ -605,7 +643,7 @@ class PythonHistory:
|
||||||
key_bindings=create_key_bindings(self, python_input, history_mapping),
|
key_bindings=create_key_bindings(self, python_input, history_mapping),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _default_buffer_pos_changed(self, _):
|
def _default_buffer_pos_changed(self, _: Buffer) -> None:
|
||||||
"""When the cursor changes in the default buffer. Synchronize with
|
"""When the cursor changes in the default buffer. Synchronize with
|
||||||
history buffer."""
|
history buffer."""
|
||||||
# Only when this buffer has the focus.
|
# Only when this buffer has the focus.
|
||||||
|
@ -629,7 +667,7 @@ class PythonHistory:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _history_buffer_pos_changed(self, _):
|
def _history_buffer_pos_changed(self, _: Buffer) -> None:
|
||||||
"""When the cursor changes in the history buffer. Synchronize."""
|
"""When the cursor changes in the history buffer. Synchronize."""
|
||||||
# Only when this buffer has the focus.
|
# Only when this buffer has the focus.
|
||||||
if self.app.current_buffer == self.history_buffer:
|
if self.app.current_buffer == self.history_buffer:
|
||||||
|
|
|
@ -8,6 +8,7 @@ also the power of for instance all the %-magic functions that IPython has to
|
||||||
offer.
|
offer.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from typing import Iterable
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from IPython import utils as ipy_utils
|
from IPython import utils as ipy_utils
|
||||||
|
@ -15,6 +16,7 @@ from IPython.core.inputsplitter import IPythonInputSplitter
|
||||||
from IPython.terminal.embed import InteractiveShellEmbed as _InteractiveShellEmbed
|
from IPython.terminal.embed import InteractiveShellEmbed as _InteractiveShellEmbed
|
||||||
from IPython.terminal.ipapp import load_default_config
|
from IPython.terminal.ipapp import load_default_config
|
||||||
from prompt_toolkit.completion import (
|
from prompt_toolkit.completion import (
|
||||||
|
CompleteEvent,
|
||||||
Completer,
|
Completer,
|
||||||
Completion,
|
Completion,
|
||||||
PathCompleter,
|
PathCompleter,
|
||||||
|
@ -25,15 +27,17 @@ from prompt_toolkit.contrib.regular_languages.compiler import compile
|
||||||
from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
|
from prompt_toolkit.contrib.regular_languages.completion import GrammarCompleter
|
||||||
from prompt_toolkit.contrib.regular_languages.lexer import GrammarLexer
|
from prompt_toolkit.contrib.regular_languages.lexer import GrammarLexer
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
from prompt_toolkit.formatted_text import PygmentsTokens
|
from prompt_toolkit.formatted_text import AnyFormattedText, PygmentsTokens
|
||||||
from prompt_toolkit.lexers import PygmentsLexer, SimpleLexer
|
from prompt_toolkit.lexers import PygmentsLexer, SimpleLexer
|
||||||
from prompt_toolkit.styles import Style
|
from prompt_toolkit.styles import Style
|
||||||
from pygments.lexers import BashLexer, PythonLexer
|
from pygments.lexers import BashLexer, PythonLexer
|
||||||
|
|
||||||
from ptpython.prompt_style import PromptStyle
|
from ptpython.prompt_style import PromptStyle
|
||||||
|
|
||||||
from .python_input import PythonCompleter, PythonInput, PythonValidator
|
from .completer import PythonCompleter
|
||||||
|
from .python_input import PythonInput
|
||||||
from .style import default_ui_style
|
from .style import default_ui_style
|
||||||
|
from .validator import PythonValidator
|
||||||
|
|
||||||
__all__ = ["embed"]
|
__all__ = ["embed"]
|
||||||
|
|
||||||
|
@ -46,13 +50,13 @@ class IPythonPrompt(PromptStyle):
|
||||||
def __init__(self, prompts):
|
def __init__(self, prompts):
|
||||||
self.prompts = prompts
|
self.prompts = prompts
|
||||||
|
|
||||||
def in_prompt(self):
|
def in_prompt(self) -> AnyFormattedText:
|
||||||
return PygmentsTokens(self.prompts.in_prompt_tokens())
|
return PygmentsTokens(self.prompts.in_prompt_tokens())
|
||||||
|
|
||||||
def in2_prompt(self, width):
|
def in2_prompt(self, width: int) -> AnyFormattedText:
|
||||||
return PygmentsTokens(self.prompts.continuation_prompt_tokens())
|
return PygmentsTokens(self.prompts.continuation_prompt_tokens())
|
||||||
|
|
||||||
def out_prompt(self):
|
def out_prompt(self) -> AnyFormattedText:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,7 +65,7 @@ class IPythonValidator(PythonValidator):
|
||||||
super(IPythonValidator, self).__init__(*args, **kwargs)
|
super(IPythonValidator, self).__init__(*args, **kwargs)
|
||||||
self.isp = IPythonInputSplitter()
|
self.isp = IPythonInputSplitter()
|
||||||
|
|
||||||
def validate(self, document):
|
def validate(self, document: Document) -> None:
|
||||||
document = Document(text=self.isp.transform_cell(document.text))
|
document = Document(text=self.isp.transform_cell(document.text))
|
||||||
super(IPythonValidator, self).validate(document)
|
super(IPythonValidator, self).validate(document)
|
||||||
|
|
||||||
|
@ -142,7 +146,9 @@ class MagicsCompleter(Completer):
|
||||||
def __init__(self, magics_manager):
|
def __init__(self, magics_manager):
|
||||||
self.magics_manager = magics_manager
|
self.magics_manager = magics_manager
|
||||||
|
|
||||||
def get_completions(self, document, complete_event):
|
def get_completions(
|
||||||
|
self, document: Document, complete_event: CompleteEvent
|
||||||
|
) -> Iterable[Completion]:
|
||||||
text = document.text_before_cursor.lstrip()
|
text = document.text_before_cursor.lstrip()
|
||||||
|
|
||||||
for m in sorted(self.magics_manager.magics["line"]):
|
for m in sorted(self.magics_manager.magics["line"]):
|
||||||
|
@ -154,7 +160,9 @@ class AliasCompleter(Completer):
|
||||||
def __init__(self, alias_manager):
|
def __init__(self, alias_manager):
|
||||||
self.alias_manager = alias_manager
|
self.alias_manager = alias_manager
|
||||||
|
|
||||||
def get_completions(self, document, complete_event):
|
def get_completions(
|
||||||
|
self, document: Document, complete_event: CompleteEvent
|
||||||
|
) -> Iterable[Completion]:
|
||||||
text = document.text_before_cursor.lstrip()
|
text = document.text_before_cursor.lstrip()
|
||||||
# aliases = [a for a, _ in self.alias_manager.aliases]
|
# aliases = [a for a, _ in self.alias_manager.aliases]
|
||||||
aliases = self.alias_manager.aliases
|
aliases = self.alias_manager.aliases
|
||||||
|
@ -240,7 +248,7 @@ class InteractiveShellEmbed(_InteractiveShellEmbed):
|
||||||
|
|
||||||
self.python_input = python_input
|
self.python_input = python_input
|
||||||
|
|
||||||
def prompt_for_code(self):
|
def prompt_for_code(self) -> str:
|
||||||
try:
|
try:
|
||||||
return self.python_input.app.run()
|
return self.python_input.app.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@ -269,6 +277,25 @@ def initialize_extensions(shell, extensions):
|
||||||
shell.showtraceback()
|
shell.showtraceback()
|
||||||
|
|
||||||
|
|
||||||
|
def run_exec_lines(shell, exec_lines):
|
||||||
|
"""
|
||||||
|
Partial copy of run_exec_lines code from IPython.core.shellapp .
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
iter(exec_lines)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
for line in exec_lines:
|
||||||
|
try:
|
||||||
|
shell.run_cell(line, store_history=False)
|
||||||
|
except:
|
||||||
|
shell.showtraceback()
|
||||||
|
except:
|
||||||
|
shell.showtraceback()
|
||||||
|
|
||||||
|
|
||||||
def embed(**kwargs):
|
def embed(**kwargs):
|
||||||
"""
|
"""
|
||||||
Copied from `IPython/terminal/embed.py`, but using our `InteractiveShellEmbed` instead.
|
Copied from `IPython/terminal/embed.py`, but using our `InteractiveShellEmbed` instead.
|
||||||
|
@ -282,6 +309,7 @@ def embed(**kwargs):
|
||||||
kwargs["config"] = config
|
kwargs["config"] = config
|
||||||
shell = InteractiveShellEmbed.instance(**kwargs)
|
shell = InteractiveShellEmbed.instance(**kwargs)
|
||||||
initialize_extensions(shell, config["InteractiveShellApp"]["extensions"])
|
initialize_extensions(shell, config["InteractiveShellApp"]["extensions"])
|
||||||
|
run_exec_lines(shell, config["InteractiveShellApp"]["exec_lines"])
|
||||||
run_startup_scripts(shell)
|
run_startup_scripts(shell)
|
||||||
shell(header=header, stack_depth=2, compile_flags=compile_flags)
|
shell(header=header, stack_depth=2, compile_flags=compile_flags)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from prompt_toolkit.application import get_app
|
from prompt_toolkit.application import get_app
|
||||||
|
from prompt_toolkit.buffer import Buffer
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
from prompt_toolkit.enums import DEFAULT_BUFFER
|
from prompt_toolkit.enums import DEFAULT_BUFFER
|
||||||
from prompt_toolkit.filters import (
|
from prompt_toolkit.filters import (
|
||||||
|
@ -11,19 +14,25 @@ from prompt_toolkit.filters import (
|
||||||
)
|
)
|
||||||
from prompt_toolkit.key_binding import KeyBindings
|
from prompt_toolkit.key_binding import KeyBindings
|
||||||
from prompt_toolkit.key_binding.bindings.named_commands import get_by_name
|
from prompt_toolkit.key_binding.bindings.named_commands import get_by_name
|
||||||
|
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
|
||||||
from prompt_toolkit.keys import Keys
|
from prompt_toolkit.keys import Keys
|
||||||
|
|
||||||
from .utils import document_is_multiline_python
|
from .utils import document_is_multiline_python
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .python_input import PythonInput
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"load_python_bindings",
|
"load_python_bindings",
|
||||||
"load_sidebar_bindings",
|
"load_sidebar_bindings",
|
||||||
"load_confirm_exit_bindings",
|
"load_confirm_exit_bindings",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
E = KeyPressEvent
|
||||||
|
|
||||||
|
|
||||||
@Condition
|
@Condition
|
||||||
def tab_should_insert_whitespace():
|
def tab_should_insert_whitespace() -> bool:
|
||||||
"""
|
"""
|
||||||
When the 'tab' key is pressed with only whitespace character before the
|
When the 'tab' key is pressed with only whitespace character before the
|
||||||
cursor, do autocompletion. Otherwise, insert indentation.
|
cursor, do autocompletion. Otherwise, insert indentation.
|
||||||
|
@ -38,7 +47,7 @@ def tab_should_insert_whitespace():
|
||||||
return bool(b.text and (not before_cursor or before_cursor.isspace()))
|
return bool(b.text and (not before_cursor or before_cursor.isspace()))
|
||||||
|
|
||||||
|
|
||||||
def load_python_bindings(python_input):
|
def load_python_bindings(python_input: "PythonInput") -> KeyBindings:
|
||||||
"""
|
"""
|
||||||
Custom key bindings.
|
Custom key bindings.
|
||||||
"""
|
"""
|
||||||
|
@ -48,14 +57,14 @@ def load_python_bindings(python_input):
|
||||||
handle = bindings.add
|
handle = bindings.add
|
||||||
|
|
||||||
@handle("c-l")
|
@handle("c-l")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Clear whole screen and render again -- also when the sidebar is visible.
|
Clear whole screen and render again -- also when the sidebar is visible.
|
||||||
"""
|
"""
|
||||||
event.app.renderer.clear()
|
event.app.renderer.clear()
|
||||||
|
|
||||||
@handle("c-z")
|
@handle("c-z")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Suspend.
|
Suspend.
|
||||||
"""
|
"""
|
||||||
|
@ -67,7 +76,7 @@ def load_python_bindings(python_input):
|
||||||
handle("c-w")(get_by_name("backward-kill-word"))
|
handle("c-w")(get_by_name("backward-kill-word"))
|
||||||
|
|
||||||
@handle("f2")
|
@handle("f2")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Show/hide sidebar.
|
Show/hide sidebar.
|
||||||
"""
|
"""
|
||||||
|
@ -78,21 +87,21 @@ def load_python_bindings(python_input):
|
||||||
event.app.layout.focus_last()
|
event.app.layout.focus_last()
|
||||||
|
|
||||||
@handle("f3")
|
@handle("f3")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Select from the history.
|
Select from the history.
|
||||||
"""
|
"""
|
||||||
python_input.enter_history()
|
python_input.enter_history()
|
||||||
|
|
||||||
@handle("f4")
|
@handle("f4")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Toggle between Vi and Emacs mode.
|
Toggle between Vi and Emacs mode.
|
||||||
"""
|
"""
|
||||||
python_input.vi_mode = not python_input.vi_mode
|
python_input.vi_mode = not python_input.vi_mode
|
||||||
|
|
||||||
@handle("f6")
|
@handle("f6")
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Enable/Disable paste mode.
|
Enable/Disable paste mode.
|
||||||
"""
|
"""
|
||||||
|
@ -101,14 +110,14 @@ def load_python_bindings(python_input):
|
||||||
@handle(
|
@handle(
|
||||||
"tab", filter=~sidebar_visible & ~has_selection & tab_should_insert_whitespace
|
"tab", filter=~sidebar_visible & ~has_selection & tab_should_insert_whitespace
|
||||||
)
|
)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
When tab should insert whitespace, do that instead of completion.
|
When tab should insert whitespace, do that instead of completion.
|
||||||
"""
|
"""
|
||||||
event.app.current_buffer.insert_text(" ")
|
event.app.current_buffer.insert_text(" ")
|
||||||
|
|
||||||
@Condition
|
@Condition
|
||||||
def is_multiline():
|
def is_multiline() -> bool:
|
||||||
return document_is_multiline_python(python_input.default_buffer.document)
|
return document_is_multiline_python(python_input.default_buffer.document)
|
||||||
|
|
||||||
@handle(
|
@handle(
|
||||||
|
@ -120,7 +129,7 @@ def load_python_bindings(python_input):
|
||||||
& ~is_multiline,
|
& ~is_multiline,
|
||||||
)
|
)
|
||||||
@handle(Keys.Escape, Keys.Enter, filter=~sidebar_visible & emacs_mode)
|
@handle(Keys.Escape, Keys.Enter, filter=~sidebar_visible & emacs_mode)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Accept input (for single line input).
|
Accept input (for single line input).
|
||||||
"""
|
"""
|
||||||
|
@ -143,7 +152,7 @@ def load_python_bindings(python_input):
|
||||||
& has_focus(DEFAULT_BUFFER)
|
& has_focus(DEFAULT_BUFFER)
|
||||||
& is_multiline,
|
& is_multiline,
|
||||||
)
|
)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Behaviour of the Enter key.
|
Behaviour of the Enter key.
|
||||||
|
|
||||||
|
@ -153,11 +162,11 @@ def load_python_bindings(python_input):
|
||||||
b = event.current_buffer
|
b = event.current_buffer
|
||||||
empty_lines_required = python_input.accept_input_on_enter or 10000
|
empty_lines_required = python_input.accept_input_on_enter or 10000
|
||||||
|
|
||||||
def at_the_end(b):
|
def at_the_end(b: Buffer) -> bool:
|
||||||
"""we consider the cursor at the end when there is no text after
|
"""we consider the cursor at the end when there is no text after
|
||||||
the cursor, or only whitespace."""
|
the cursor, or only whitespace."""
|
||||||
text = b.document.text_after_cursor
|
text = b.document.text_after_cursor
|
||||||
return text == "" or (text.isspace() and not "\n" in text)
|
return text == "" or (text.isspace() and "\n" not in text)
|
||||||
|
|
||||||
if python_input.paste_mode:
|
if python_input.paste_mode:
|
||||||
# In paste mode, always insert text.
|
# In paste mode, always insert text.
|
||||||
|
@ -187,7 +196,7 @@ def load_python_bindings(python_input):
|
||||||
not get_app().current_buffer.text
|
not get_app().current_buffer.text
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Override Control-D exit, to ask for confirmation.
|
Override Control-D exit, to ask for confirmation.
|
||||||
"""
|
"""
|
||||||
|
@ -202,14 +211,14 @@ def load_python_bindings(python_input):
|
||||||
event.app.exit(exception=EOFError)
|
event.app.exit(exception=EOFError)
|
||||||
|
|
||||||
@handle("c-c", filter=has_focus(python_input.default_buffer))
|
@handle("c-c", filter=has_focus(python_input.default_buffer))
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Abort when Control-C has been pressed."
|
"Abort when Control-C has been pressed."
|
||||||
event.app.exit(exception=KeyboardInterrupt, style="class:aborting")
|
event.app.exit(exception=KeyboardInterrupt, style="class:aborting")
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
|
|
||||||
|
|
||||||
def load_sidebar_bindings(python_input):
|
def load_sidebar_bindings(python_input: "PythonInput") -> KeyBindings:
|
||||||
"""
|
"""
|
||||||
Load bindings for the navigation in the sidebar.
|
Load bindings for the navigation in the sidebar.
|
||||||
"""
|
"""
|
||||||
|
@ -221,7 +230,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("up", filter=sidebar_visible)
|
@handle("up", filter=sidebar_visible)
|
||||||
@handle("c-p", filter=sidebar_visible)
|
@handle("c-p", filter=sidebar_visible)
|
||||||
@handle("k", filter=sidebar_visible)
|
@handle("k", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Go to previous option."
|
"Go to previous option."
|
||||||
python_input.selected_option_index = (
|
python_input.selected_option_index = (
|
||||||
python_input.selected_option_index - 1
|
python_input.selected_option_index - 1
|
||||||
|
@ -230,7 +239,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("down", filter=sidebar_visible)
|
@handle("down", filter=sidebar_visible)
|
||||||
@handle("c-n", filter=sidebar_visible)
|
@handle("c-n", filter=sidebar_visible)
|
||||||
@handle("j", filter=sidebar_visible)
|
@handle("j", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Go to next option."
|
"Go to next option."
|
||||||
python_input.selected_option_index = (
|
python_input.selected_option_index = (
|
||||||
python_input.selected_option_index + 1
|
python_input.selected_option_index + 1
|
||||||
|
@ -239,14 +248,14 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("right", filter=sidebar_visible)
|
@handle("right", filter=sidebar_visible)
|
||||||
@handle("l", filter=sidebar_visible)
|
@handle("l", filter=sidebar_visible)
|
||||||
@handle(" ", filter=sidebar_visible)
|
@handle(" ", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Select next value for current option."
|
"Select next value for current option."
|
||||||
option = python_input.selected_option
|
option = python_input.selected_option
|
||||||
option.activate_next()
|
option.activate_next()
|
||||||
|
|
||||||
@handle("left", filter=sidebar_visible)
|
@handle("left", filter=sidebar_visible)
|
||||||
@handle("h", filter=sidebar_visible)
|
@handle("h", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Select previous value for current option."
|
"Select previous value for current option."
|
||||||
option = python_input.selected_option
|
option = python_input.selected_option
|
||||||
option.activate_previous()
|
option.activate_previous()
|
||||||
|
@ -256,7 +265,7 @@ def load_sidebar_bindings(python_input):
|
||||||
@handle("c-d", filter=sidebar_visible)
|
@handle("c-d", filter=sidebar_visible)
|
||||||
@handle("enter", filter=sidebar_visible)
|
@handle("enter", filter=sidebar_visible)
|
||||||
@handle("escape", filter=sidebar_visible)
|
@handle("escape", filter=sidebar_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"Hide sidebar."
|
"Hide sidebar."
|
||||||
python_input.show_sidebar = False
|
python_input.show_sidebar = False
|
||||||
event.app.layout.focus_last()
|
event.app.layout.focus_last()
|
||||||
|
@ -264,7 +273,7 @@ def load_sidebar_bindings(python_input):
|
||||||
return bindings
|
return bindings
|
||||||
|
|
||||||
|
|
||||||
def load_confirm_exit_bindings(python_input):
|
def load_confirm_exit_bindings(python_input: "PythonInput") -> KeyBindings:
|
||||||
"""
|
"""
|
||||||
Handle yes/no key presses when the exit confirmation is shown.
|
Handle yes/no key presses when the exit confirmation is shown.
|
||||||
"""
|
"""
|
||||||
|
@ -277,14 +286,14 @@ def load_confirm_exit_bindings(python_input):
|
||||||
@handle("Y", filter=confirmation_visible)
|
@handle("Y", filter=confirmation_visible)
|
||||||
@handle("enter", filter=confirmation_visible)
|
@handle("enter", filter=confirmation_visible)
|
||||||
@handle("c-d", filter=confirmation_visible)
|
@handle("c-d", filter=confirmation_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Really quit.
|
Really quit.
|
||||||
"""
|
"""
|
||||||
event.app.exit(exception=EOFError, style="class:exiting")
|
event.app.exit(exception=EOFError, style="class:exiting")
|
||||||
|
|
||||||
@handle(Keys.Any, filter=confirmation_visible)
|
@handle(Keys.Any, filter=confirmation_visible)
|
||||||
def _(event):
|
def _(event: E) -> None:
|
||||||
"""
|
"""
|
||||||
Cancel exit.
|
Cancel exit.
|
||||||
"""
|
"""
|
||||||
|
@ -294,7 +303,7 @@ def load_confirm_exit_bindings(python_input):
|
||||||
return bindings
|
return bindings
|
||||||
|
|
||||||
|
|
||||||
def auto_newline(buffer):
|
def auto_newline(buffer: Buffer) -> None:
|
||||||
r"""
|
r"""
|
||||||
Insert \n at the cursor position. Also add necessary padding.
|
Insert \n at the cursor position. Also add necessary padding.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,7 +5,7 @@ import platform
|
||||||
import sys
|
import sys
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from inspect import _ParameterKind as ParameterKind
|
from inspect import _ParameterKind as ParameterKind
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Any, List, Optional, Type
|
||||||
|
|
||||||
from prompt_toolkit.application import get_app
|
from prompt_toolkit.application import get_app
|
||||||
from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
|
from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
|
||||||
|
@ -15,10 +15,15 @@ from prompt_toolkit.filters import (
|
||||||
is_done,
|
is_done,
|
||||||
renderer_height_is_known,
|
renderer_height_is_known,
|
||||||
)
|
)
|
||||||
from prompt_toolkit.formatted_text import fragment_list_width, to_formatted_text
|
from prompt_toolkit.formatted_text import (
|
||||||
|
AnyFormattedText,
|
||||||
|
fragment_list_width,
|
||||||
|
to_formatted_text,
|
||||||
|
)
|
||||||
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
from prompt_toolkit.formatted_text.base import StyleAndTextTuples
|
||||||
from prompt_toolkit.key_binding.vi_state import InputMode
|
from prompt_toolkit.key_binding.vi_state import InputMode
|
||||||
from prompt_toolkit.layout.containers import (
|
from prompt_toolkit.layout.containers import (
|
||||||
|
AnyContainer,
|
||||||
ConditionalContainer,
|
ConditionalContainer,
|
||||||
Container,
|
Container,
|
||||||
Float,
|
Float,
|
||||||
|
@ -40,9 +45,10 @@ from prompt_toolkit.layout.processors import (
|
||||||
HighlightIncrementalSearchProcessor,
|
HighlightIncrementalSearchProcessor,
|
||||||
HighlightMatchingBracketProcessor,
|
HighlightMatchingBracketProcessor,
|
||||||
HighlightSelectionProcessor,
|
HighlightSelectionProcessor,
|
||||||
|
Processor,
|
||||||
TabsProcessor,
|
TabsProcessor,
|
||||||
)
|
)
|
||||||
from prompt_toolkit.lexers import SimpleLexer
|
from prompt_toolkit.lexers import Lexer, SimpleLexer
|
||||||
from prompt_toolkit.mouse_events import MouseEvent
|
from prompt_toolkit.mouse_events import MouseEvent
|
||||||
from prompt_toolkit.selection import SelectionType
|
from prompt_toolkit.selection import SelectionType
|
||||||
from prompt_toolkit.widgets.toolbars import (
|
from prompt_toolkit.widgets.toolbars import (
|
||||||
|
@ -55,6 +61,7 @@ from prompt_toolkit.widgets.toolbars import (
|
||||||
from pygments.lexers import PythonLexer
|
from pygments.lexers import PythonLexer
|
||||||
|
|
||||||
from .filters import HasSignature, ShowDocstring, ShowSidebar, ShowSignature
|
from .filters import HasSignature, ShowDocstring, ShowSidebar, ShowSignature
|
||||||
|
from .prompt_style import PromptStyle
|
||||||
from .utils import if_mousedown
|
from .utils import if_mousedown
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -98,7 +105,7 @@ def python_sidebar(python_input: "PythonInput") -> Window:
|
||||||
def get_text_fragments() -> StyleAndTextTuples:
|
def get_text_fragments() -> StyleAndTextTuples:
|
||||||
tokens: StyleAndTextTuples = []
|
tokens: StyleAndTextTuples = []
|
||||||
|
|
||||||
def append_category(category: "OptionCategory") -> None:
|
def append_category(category: "OptionCategory[Any]") -> None:
|
||||||
tokens.extend(
|
tokens.extend(
|
||||||
[
|
[
|
||||||
("class:sidebar", " "),
|
("class:sidebar", " "),
|
||||||
|
@ -150,10 +157,10 @@ def python_sidebar(python_input: "PythonInput") -> Window:
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
class Control(FormattedTextControl):
|
class Control(FormattedTextControl):
|
||||||
def move_cursor_down(self):
|
def move_cursor_down(self) -> None:
|
||||||
python_input.selected_option_index += 1
|
python_input.selected_option_index += 1
|
||||||
|
|
||||||
def move_cursor_up(self):
|
def move_cursor_up(self) -> None:
|
||||||
python_input.selected_option_index -= 1
|
python_input.selected_option_index -= 1
|
||||||
|
|
||||||
return Window(
|
return Window(
|
||||||
|
@ -165,12 +172,12 @@ def python_sidebar(python_input: "PythonInput") -> Window:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def python_sidebar_navigation(python_input):
|
def python_sidebar_navigation(python_input: "PythonInput") -> Window:
|
||||||
"""
|
"""
|
||||||
Create the `Layout` showing the navigation information for the sidebar.
|
Create the `Layout` showing the navigation information for the sidebar.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_text_fragments():
|
def get_text_fragments() -> StyleAndTextTuples:
|
||||||
# Show navigation info.
|
# Show navigation info.
|
||||||
return [
|
return [
|
||||||
("class:sidebar", " "),
|
("class:sidebar", " "),
|
||||||
|
@ -191,13 +198,13 @@ def python_sidebar_navigation(python_input):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def python_sidebar_help(python_input):
|
def python_sidebar_help(python_input: "PythonInput") -> Container:
|
||||||
"""
|
"""
|
||||||
Create the `Layout` for the help text for the current item in the sidebar.
|
Create the `Layout` for the help text for the current item in the sidebar.
|
||||||
"""
|
"""
|
||||||
token = "class:sidebar.helptext"
|
token = "class:sidebar.helptext"
|
||||||
|
|
||||||
def get_current_description():
|
def get_current_description() -> str:
|
||||||
"""
|
"""
|
||||||
Return the description of the selected option.
|
Return the description of the selected option.
|
||||||
"""
|
"""
|
||||||
|
@ -209,7 +216,7 @@ def python_sidebar_help(python_input):
|
||||||
i += 1
|
i += 1
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_help_text():
|
def get_help_text() -> StyleAndTextTuples:
|
||||||
return [(token, get_current_description())]
|
return [(token, get_current_description())]
|
||||||
|
|
||||||
return ConditionalContainer(
|
return ConditionalContainer(
|
||||||
|
@ -225,7 +232,7 @@ def python_sidebar_help(python_input):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def signature_toolbar(python_input):
|
def signature_toolbar(python_input: "PythonInput") -> Container:
|
||||||
"""
|
"""
|
||||||
Return the `Layout` for the signature.
|
Return the `Layout` for the signature.
|
||||||
"""
|
"""
|
||||||
|
@ -311,21 +318,23 @@ class PythonPromptMargin(PromptMargin):
|
||||||
It shows something like "In [1]:".
|
It shows something like "In [1]:".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, python_input) -> None:
|
def __init__(self, python_input: "PythonInput") -> None:
|
||||||
self.python_input = python_input
|
self.python_input = python_input
|
||||||
|
|
||||||
def get_prompt_style():
|
def get_prompt_style() -> PromptStyle:
|
||||||
return python_input.all_prompt_styles[python_input.prompt_style]
|
return python_input.all_prompt_styles[python_input.prompt_style]
|
||||||
|
|
||||||
def get_prompt() -> StyleAndTextTuples:
|
def get_prompt() -> StyleAndTextTuples:
|
||||||
return to_formatted_text(get_prompt_style().in_prompt())
|
return to_formatted_text(get_prompt_style().in_prompt())
|
||||||
|
|
||||||
def get_continuation(width, line_number, is_soft_wrap):
|
def get_continuation(
|
||||||
|
width: int, line_number: int, is_soft_wrap: bool
|
||||||
|
) -> StyleAndTextTuples:
|
||||||
if python_input.show_line_numbers and not is_soft_wrap:
|
if python_input.show_line_numbers and not is_soft_wrap:
|
||||||
text = ("%i " % (line_number + 1)).rjust(width)
|
text = ("%i " % (line_number + 1)).rjust(width)
|
||||||
return [("class:line-number", text)]
|
return [("class:line-number", text)]
|
||||||
else:
|
else:
|
||||||
return get_prompt_style().in2_prompt(width)
|
return to_formatted_text(get_prompt_style().in2_prompt(width))
|
||||||
|
|
||||||
super().__init__(get_prompt, get_continuation)
|
super().__init__(get_prompt, get_continuation)
|
||||||
|
|
||||||
|
@ -510,7 +519,7 @@ def show_sidebar_button_info(python_input: "PythonInput") -> Container:
|
||||||
|
|
||||||
|
|
||||||
def create_exit_confirmation(
|
def create_exit_confirmation(
|
||||||
python_input: "PythonInput", style="class:exit-confirmation"
|
python_input: "PythonInput", style: str = "class:exit-confirmation"
|
||||||
) -> Container:
|
) -> Container:
|
||||||
"""
|
"""
|
||||||
Create `Layout` for the exit message.
|
Create `Layout` for the exit message.
|
||||||
|
@ -567,22 +576,22 @@ class PtPythonLayout:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
python_input: "PythonInput",
|
python_input: "PythonInput",
|
||||||
lexer=PythonLexer,
|
lexer: Lexer,
|
||||||
extra_body=None,
|
extra_body: Optional[AnyContainer] = None,
|
||||||
extra_toolbars=None,
|
extra_toolbars: Optional[List[AnyContainer]] = None,
|
||||||
extra_buffer_processors=None,
|
extra_buffer_processors: Optional[List[Processor]] = None,
|
||||||
input_buffer_height: Optional[AnyDimension] = None,
|
input_buffer_height: Optional[AnyDimension] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
D = Dimension
|
D = Dimension
|
||||||
extra_body = [extra_body] if extra_body else []
|
extra_body_list: List[AnyContainer] = [extra_body] if extra_body else []
|
||||||
extra_toolbars = extra_toolbars or []
|
extra_toolbars = extra_toolbars or []
|
||||||
extra_buffer_processors = extra_buffer_processors or []
|
|
||||||
input_buffer_height = input_buffer_height or D(min=6)
|
input_buffer_height = input_buffer_height or D(min=6)
|
||||||
|
|
||||||
search_toolbar = SearchToolbar(python_input.search_buffer)
|
search_toolbar = SearchToolbar(python_input.search_buffer)
|
||||||
|
|
||||||
def create_python_input_window():
|
def create_python_input_window() -> Window:
|
||||||
def menu_position():
|
def menu_position() -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
When there is no autocompletion menu to be shown, and we have a
|
When there is no autocompletion menu to be shown, and we have a
|
||||||
signature, set the pop-up position at `bracket_start`.
|
signature, set the pop-up position at `bracket_start`.
|
||||||
|
@ -593,6 +602,7 @@ class PtPythonLayout:
|
||||||
row, col = python_input.signatures[0].bracket_start
|
row, col = python_input.signatures[0].bracket_start
|
||||||
index = b.document.translate_row_col_to_index(row - 1, col)
|
index = b.document.translate_row_col_to_index(row - 1, col)
|
||||||
return index
|
return index
|
||||||
|
return None
|
||||||
|
|
||||||
return Window(
|
return Window(
|
||||||
BufferControl(
|
BufferControl(
|
||||||
|
@ -622,7 +632,7 @@ class PtPythonLayout:
|
||||||
processor=AppendAutoSuggestion(), filter=~is_done
|
processor=AppendAutoSuggestion(), filter=~is_done
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
+ extra_buffer_processors,
|
+ (extra_buffer_processors or []),
|
||||||
menu_position=menu_position,
|
menu_position=menu_position,
|
||||||
# Make sure that we always see the result of an reverse-i-search:
|
# Make sure that we always see the result of an reverse-i-search:
|
||||||
preview_search=True,
|
preview_search=True,
|
||||||
|
@ -654,7 +664,7 @@ class PtPythonLayout:
|
||||||
[
|
[
|
||||||
FloatContainer(
|
FloatContainer(
|
||||||
content=HSplit(
|
content=HSplit(
|
||||||
[create_python_input_window()] + extra_body
|
[create_python_input_window()] + extra_body_list
|
||||||
),
|
),
|
||||||
floats=[
|
floats=[
|
||||||
Float(
|
Float(
|
||||||
|
|
0
ptpython/py.typed
Normal file
0
ptpython/py.typed
Normal file
|
@ -6,7 +6,18 @@ import __future__
|
||||||
|
|
||||||
from asyncio import get_event_loop
|
from asyncio import get_event_loop
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Generic, List, Optional, TypeVar
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Generic,
|
||||||
|
List,
|
||||||
|
Mapping,
|
||||||
|
Optional,
|
||||||
|
Tuple,
|
||||||
|
TypeVar,
|
||||||
|
)
|
||||||
|
|
||||||
from prompt_toolkit.application import Application, get_app
|
from prompt_toolkit.application import Application, get_app
|
||||||
from prompt_toolkit.auto_suggest import (
|
from prompt_toolkit.auto_suggest import (
|
||||||
|
@ -44,6 +55,7 @@ from prompt_toolkit.key_binding.bindings.open_in_editor import (
|
||||||
load_open_in_editor_bindings,
|
load_open_in_editor_bindings,
|
||||||
)
|
)
|
||||||
from prompt_toolkit.key_binding.vi_state import InputMode
|
from prompt_toolkit.key_binding.vi_state import InputMode
|
||||||
|
from prompt_toolkit.layout.containers import AnyContainer
|
||||||
from prompt_toolkit.lexers import DynamicLexer, Lexer, SimpleLexer
|
from prompt_toolkit.lexers import DynamicLexer, Lexer, SimpleLexer
|
||||||
from prompt_toolkit.output import ColorDepth, Output
|
from prompt_toolkit.output import ColorDepth, Output
|
||||||
from prompt_toolkit.styles import (
|
from prompt_toolkit.styles import (
|
||||||
|
@ -88,8 +100,8 @@ if TYPE_CHECKING:
|
||||||
_T = TypeVar("_T", bound="_SupportsLessThan")
|
_T = TypeVar("_T", bound="_SupportsLessThan")
|
||||||
|
|
||||||
|
|
||||||
class OptionCategory:
|
class OptionCategory(Generic[_T]):
|
||||||
def __init__(self, title: str, options: List["Option"]) -> None:
|
def __init__(self, title: str, options: List["Option[_T]"]) -> None:
|
||||||
self.title = title
|
self.title = title
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
||||||
|
@ -113,7 +125,7 @@ class Option(Generic[_T]):
|
||||||
get_current_value: Callable[[], _T],
|
get_current_value: Callable[[], _T],
|
||||||
# We accept `object` as return type for the select functions, because
|
# We accept `object` as return type for the select functions, because
|
||||||
# often they return an unused boolean. Maybe this can be improved.
|
# often they return an unused boolean. Maybe this can be improved.
|
||||||
get_values: Callable[[], Dict[_T, Callable[[], object]]],
|
get_values: Callable[[], Mapping[_T, Callable[[], object]]],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.title = title
|
self.title = title
|
||||||
self.description = description
|
self.description = description
|
||||||
|
@ -121,7 +133,7 @@ class Option(Generic[_T]):
|
||||||
self.get_values = get_values
|
self.get_values = get_values
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def values(self) -> Dict[_T, Callable[[], object]]:
|
def values(self) -> Mapping[_T, Callable[[], object]]:
|
||||||
return self.get_values()
|
return self.get_values()
|
||||||
|
|
||||||
def activate_next(self, _previous: bool = False) -> None:
|
def activate_next(self, _previous: bool = False) -> None:
|
||||||
|
@ -192,12 +204,12 @@ class PythonInput:
|
||||||
output: Optional[Output] = None,
|
output: Optional[Output] = None,
|
||||||
# For internal use.
|
# For internal use.
|
||||||
extra_key_bindings: Optional[KeyBindings] = None,
|
extra_key_bindings: Optional[KeyBindings] = None,
|
||||||
create_app=True,
|
create_app: bool = True,
|
||||||
_completer: Optional[Completer] = None,
|
_completer: Optional[Completer] = None,
|
||||||
_validator: Optional[Validator] = None,
|
_validator: Optional[Validator] = None,
|
||||||
_lexer: Optional[Lexer] = None,
|
_lexer: Optional[Lexer] = None,
|
||||||
_extra_buffer_processors=None,
|
_extra_buffer_processors=None,
|
||||||
_extra_layout_body=None,
|
_extra_layout_body: Optional[AnyContainer] = None,
|
||||||
_extra_toolbars=None,
|
_extra_toolbars=None,
|
||||||
_input_buffer_height=None,
|
_input_buffer_height=None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -239,7 +251,7 @@ class PythonInput:
|
||||||
self.history = InMemoryHistory()
|
self.history = InMemoryHistory()
|
||||||
|
|
||||||
self._input_buffer_height = _input_buffer_height
|
self._input_buffer_height = _input_buffer_height
|
||||||
self._extra_layout_body = _extra_layout_body or []
|
self._extra_layout_body = _extra_layout_body
|
||||||
self._extra_toolbars = _extra_toolbars or []
|
self._extra_toolbars = _extra_toolbars or []
|
||||||
self._extra_buffer_processors = _extra_buffer_processors or []
|
self._extra_buffer_processors = _extra_buffer_processors or []
|
||||||
|
|
||||||
|
@ -388,7 +400,9 @@ class PythonInput:
|
||||||
# Create an app if requested. If not, the global get_app() is returned
|
# Create an app if requested. If not, the global get_app() is returned
|
||||||
# for self.app via property getter.
|
# for self.app via property getter.
|
||||||
if create_app:
|
if create_app:
|
||||||
self._app: Optional[Application] = self._create_application(input, output)
|
self._app: Optional[Application[str]] = self._create_application(
|
||||||
|
input, output
|
||||||
|
)
|
||||||
# Setting vi_mode will not work unless the prompt_toolkit
|
# Setting vi_mode will not work unless the prompt_toolkit
|
||||||
# application has been created.
|
# application has been created.
|
||||||
if vi_mode:
|
if vi_mode:
|
||||||
|
@ -408,7 +422,7 @@ class PythonInput:
|
||||||
return sum(len(category.options) for category in self.options)
|
return sum(len(category.options) for category in self.options)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def selected_option(self) -> Option:
|
def selected_option(self) -> Option[Any]:
|
||||||
"Return the currently selected option."
|
"Return the currently selected option."
|
||||||
i = 0
|
i = 0
|
||||||
for category in self.options:
|
for category in self.options:
|
||||||
|
@ -514,7 +528,7 @@ class PythonInput:
|
||||||
self.ui_styles[self._current_ui_style_name],
|
self.ui_styles[self._current_ui_style_name],
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_options(self) -> List[OptionCategory]:
|
def _create_options(self) -> List[OptionCategory[Any]]:
|
||||||
"""
|
"""
|
||||||
Create a list of `Option` instances for the options sidebar.
|
Create a list of `Option` instances for the options sidebar.
|
||||||
"""
|
"""
|
||||||
|
@ -530,15 +544,17 @@ class PythonInput:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def simple_option(
|
def simple_option(
|
||||||
title: str, description: str, field_name: str, values: Optional[List] = None
|
title: str,
|
||||||
) -> Option:
|
description: str,
|
||||||
|
field_name: str,
|
||||||
|
values: Tuple[str, str] = ("off", "on"),
|
||||||
|
) -> Option[str]:
|
||||||
"Create Simple on/of option."
|
"Create Simple on/of option."
|
||||||
values = values or ["off", "on"]
|
|
||||||
|
|
||||||
def get_current_value():
|
def get_current_value() -> str:
|
||||||
return values[bool(getattr(self, field_name))]
|
return values[bool(getattr(self, field_name))]
|
||||||
|
|
||||||
def get_values():
|
def get_values() -> Dict[str, Callable[[], bool]]:
|
||||||
return {
|
return {
|
||||||
values[1]: lambda: enable(field_name),
|
values[1]: lambda: enable(field_name),
|
||||||
values[0]: lambda: disable(field_name),
|
values[0]: lambda: disable(field_name),
|
||||||
|
@ -848,7 +864,7 @@ class PythonInput:
|
||||||
|
|
||||||
def _create_application(
|
def _create_application(
|
||||||
self, input: Optional[Input], output: Optional[Output]
|
self, input: Optional[Input], output: Optional[Output]
|
||||||
) -> Application:
|
) -> Application[str]:
|
||||||
"""
|
"""
|
||||||
Create an `Application` instance.
|
Create an `Application` instance.
|
||||||
"""
|
"""
|
||||||
|
@ -926,7 +942,7 @@ class PythonInput:
|
||||||
self.editing_mode = EditingMode.EMACS
|
self.editing_mode = EditingMode.EMACS
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def app(self) -> Application:
|
def app(self) -> Application[str]:
|
||||||
if self._app is None:
|
if self._app is None:
|
||||||
return get_app()
|
return get_app()
|
||||||
return self._app
|
return self._app
|
||||||
|
|
|
@ -44,6 +44,7 @@ from pygments.token import Token
|
||||||
|
|
||||||
from .python_input import PythonInput
|
from .python_input import PythonInput
|
||||||
|
|
||||||
|
PyCF_ALLOW_TOP_LEVEL_AWAIT: int
|
||||||
try:
|
try:
|
||||||
from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT # type: ignore
|
from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT # type: ignore
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -90,7 +91,7 @@ class PythonRepl(PythonInput):
|
||||||
output = self.app.output
|
output = self.app.output
|
||||||
output.write("WARNING | File not found: {}\n\n".format(path))
|
output.write("WARNING | File not found: {}\n\n".format(path))
|
||||||
|
|
||||||
def run_and_show_expression(self, expression):
|
def run_and_show_expression(self, expression: str) -> None:
|
||||||
try:
|
try:
|
||||||
# Eval.
|
# Eval.
|
||||||
try:
|
try:
|
||||||
|
@ -135,7 +136,7 @@ class PythonRepl(PythonInput):
|
||||||
text = self.read()
|
text = self.read()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
return
|
return
|
||||||
except BaseException as e:
|
except BaseException:
|
||||||
# Something went wrong while reading input.
|
# Something went wrong while reading input.
|
||||||
# (E.g., a bug in the completer that propagates. Don't
|
# (E.g., a bug in the completer that propagates. Don't
|
||||||
# crash the REPL.)
|
# crash the REPL.)
|
||||||
|
@ -149,7 +150,7 @@ class PythonRepl(PythonInput):
|
||||||
clear_title()
|
clear_title()
|
||||||
self._remove_from_namespace()
|
self._remove_from_namespace()
|
||||||
|
|
||||||
async def run_and_show_expression_async(self, text):
|
async def run_and_show_expression_async(self, text: str):
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -349,7 +350,7 @@ class PythonRepl(PythonInput):
|
||||||
if not hasattr(black, "Mode"):
|
if not hasattr(black, "Mode"):
|
||||||
raise ImportError
|
raise ImportError
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass # no Black package in your installation
|
pass # no Black package in your installation
|
||||||
else:
|
else:
|
||||||
result_repr = black.format_str(
|
result_repr = black.format_str(
|
||||||
result_repr,
|
result_repr,
|
||||||
|
@ -725,17 +726,17 @@ def embed(
|
||||||
configure(repl)
|
configure(repl)
|
||||||
|
|
||||||
# Start repl.
|
# Start repl.
|
||||||
patch_context: ContextManager = (
|
patch_context: ContextManager[None] = (
|
||||||
patch_stdout_context() if patch_stdout else DummyContext()
|
patch_stdout_context() if patch_stdout else DummyContext()
|
||||||
)
|
)
|
||||||
|
|
||||||
if return_asyncio_coroutine:
|
if return_asyncio_coroutine:
|
||||||
|
|
||||||
async def coroutine():
|
async def coroutine() -> None:
|
||||||
with patch_context:
|
with patch_context:
|
||||||
await repl.run_async()
|
await repl.run_async()
|
||||||
|
|
||||||
return coroutine()
|
return coroutine() # type: ignore
|
||||||
else:
|
else:
|
||||||
with patch_context:
|
with patch_context:
|
||||||
repl.run()
|
repl.run()
|
||||||
|
|
|
@ -8,13 +8,16 @@ can use `eval()` to evaluate the function object.
|
||||||
import inspect
|
import inspect
|
||||||
from inspect import Signature as InspectSignature
|
from inspect import Signature as InspectSignature
|
||||||
from inspect import _ParameterKind as ParameterKind
|
from inspect import _ParameterKind as ParameterKind
|
||||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from prompt_toolkit.document import Document
|
from prompt_toolkit.document import Document
|
||||||
|
|
||||||
from .completer import DictionaryCompleter
|
from .completer import DictionaryCompleter
|
||||||
from .utils import get_jedi_script_from_document
|
from .utils import get_jedi_script_from_document
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import jedi.api.classes
|
||||||
|
|
||||||
__all__ = ["Signature", "get_signatures_using_jedi", "get_signatures_using_eval"]
|
__all__ = ["Signature", "get_signatures_using_jedi", "get_signatures_using_eval"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,7 +123,9 @@ class Signature:
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_jedi_signature(cls, signature) -> "Signature":
|
def from_jedi_signature(
|
||||||
|
cls, signature: "jedi.api.classes.Signature"
|
||||||
|
) -> "Signature":
|
||||||
parameters = []
|
parameters = []
|
||||||
|
|
||||||
for p in signature.params:
|
for p in signature.params:
|
||||||
|
|
|
@ -2,12 +2,31 @@
|
||||||
For internal use only.
|
For internal use only.
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
from typing import Callable, Iterable, Type, TypeVar, cast
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
Optional,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
|
from prompt_toolkit.document import Document
|
||||||
from prompt_toolkit.formatted_text import to_formatted_text
|
from prompt_toolkit.formatted_text import to_formatted_text
|
||||||
from prompt_toolkit.formatted_text.utils import fragment_list_to_text
|
from prompt_toolkit.formatted_text.utils import fragment_list_to_text
|
||||||
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from jedi import Interpreter
|
||||||
|
|
||||||
|
# See: prompt_toolkit/key_binding/key_bindings.py
|
||||||
|
# Annotating these return types as `object` is what works best, because
|
||||||
|
# `NotImplemented` is typed `Any`.
|
||||||
|
NotImplementedOrNone = object
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"has_unclosed_brackets",
|
"has_unclosed_brackets",
|
||||||
"get_jedi_script_from_document",
|
"get_jedi_script_from_document",
|
||||||
|
@ -45,7 +64,9 @@ def has_unclosed_brackets(text: str) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_jedi_script_from_document(document, locals, globals):
|
def get_jedi_script_from_document(
|
||||||
|
document: Document, locals: Dict[str, Any], globals: Dict[str, Any]
|
||||||
|
) -> "Interpreter":
|
||||||
import jedi # We keep this import in-line, to improve start-up time.
|
import jedi # We keep this import in-line, to improve start-up time.
|
||||||
|
|
||||||
# Importing Jedi is 'slow'.
|
# Importing Jedi is 'slow'.
|
||||||
|
@ -78,7 +99,7 @@ def get_jedi_script_from_document(document, locals, globals):
|
||||||
_multiline_string_delims = re.compile("""[']{3}|["]{3}""")
|
_multiline_string_delims = re.compile("""[']{3}|["]{3}""")
|
||||||
|
|
||||||
|
|
||||||
def document_is_multiline_python(document):
|
def document_is_multiline_python(document: Document) -> bool:
|
||||||
"""
|
"""
|
||||||
Determine whether this is a multiline Python document.
|
Determine whether this is a multiline Python document.
|
||||||
"""
|
"""
|
||||||
|
@ -133,7 +154,7 @@ def if_mousedown(handler: _T) -> _T:
|
||||||
by the Window.)
|
by the Window.)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def handle_if_mouse_down(mouse_event: MouseEvent):
|
def handle_if_mouse_down(mouse_event: MouseEvent) -> "NotImplementedOrNone":
|
||||||
if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
|
if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
|
||||||
return handler(mouse_event)
|
return handler(mouse_event)
|
||||||
else:
|
else:
|
||||||
|
@ -142,7 +163,7 @@ def if_mousedown(handler: _T) -> _T:
|
||||||
return cast(_T, handle_if_mouse_down)
|
return cast(_T, handle_if_mouse_down)
|
||||||
|
|
||||||
|
|
||||||
_T_type = TypeVar("_T_type", bound=Type)
|
_T_type = TypeVar("_T_type", bound=type)
|
||||||
|
|
||||||
|
|
||||||
def ptrepr_to_repr(cls: _T_type) -> _T_type:
|
def ptrepr_to_repr(cls: _T_type) -> _T_type:
|
||||||
|
@ -154,7 +175,8 @@ def ptrepr_to_repr(cls: _T_type) -> _T_type:
|
||||||
"@ptrepr_to_repr can only be applied to classes that have a `__pt_repr__` method."
|
"@ptrepr_to_repr can only be applied to classes that have a `__pt_repr__` method."
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self: object) -> str:
|
||||||
|
assert hasattr(cls, "__pt_repr__")
|
||||||
return fragment_list_to_text(to_formatted_text(cls.__pt_repr__(self)))
|
return fragment_list_to_text(to_formatted_text(cls.__pt_repr__(self)))
|
||||||
|
|
||||||
cls.__repr__ = __repr__ # type:ignore
|
cls.__repr__ = __repr__ # type:ignore
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
from prompt_toolkit.document import Document
|
||||||
from prompt_toolkit.validation import ValidationError, Validator
|
from prompt_toolkit.validation import ValidationError, Validator
|
||||||
|
|
||||||
from .utils import unindent_code
|
from .utils import unindent_code
|
||||||
|
@ -13,10 +16,10 @@ class PythonValidator(Validator):
|
||||||
active compiler flags.
|
active compiler flags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, get_compiler_flags=None):
|
def __init__(self, get_compiler_flags: Optional[Callable[[], int]] = None) -> None:
|
||||||
self.get_compiler_flags = get_compiler_flags
|
self.get_compiler_flags = get_compiler_flags
|
||||||
|
|
||||||
def validate(self, document):
|
def validate(self, document: Document) -> None:
|
||||||
"""
|
"""
|
||||||
Check input for Python syntax errors.
|
Check input for Python syntax errors.
|
||||||
"""
|
"""
|
||||||
|
@ -45,7 +48,7 @@ class PythonValidator(Validator):
|
||||||
# fixed in Python 3.)
|
# fixed in Python 3.)
|
||||||
# TODO: This is not correct if indentation was removed.
|
# TODO: This is not correct if indentation was removed.
|
||||||
index = document.translate_row_col_to_index(
|
index = document.translate_row_col_to_index(
|
||||||
e.lineno - 1, (e.offset or 1) - 1
|
(e.lineno or 1) - 1, (e.offset or 1) - 1
|
||||||
)
|
)
|
||||||
raise ValidationError(index, f"Syntax Error: {e}")
|
raise ValidationError(index, f"Syntax Error: {e}")
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
|
|
44
setup.cfg
44
setup.cfg
|
@ -1,7 +1,41 @@
|
||||||
[bdist_wheel]
|
[bdist_wheel]
|
||||||
universal = 1
|
universal=1
|
||||||
|
|
||||||
[egg_info]
|
|
||||||
tag_build =
|
|
||||||
tag_date = 0
|
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
exclude=__init__.py
|
||||||
|
max_line_length=150
|
||||||
|
ignore=
|
||||||
|
E114,
|
||||||
|
E116,
|
||||||
|
E117,
|
||||||
|
E121,
|
||||||
|
E122,
|
||||||
|
E123,
|
||||||
|
E125,
|
||||||
|
E126,
|
||||||
|
E127,
|
||||||
|
E128,
|
||||||
|
E131,
|
||||||
|
E171,
|
||||||
|
E203,
|
||||||
|
E211,
|
||||||
|
E221,
|
||||||
|
E227,
|
||||||
|
E231,
|
||||||
|
E241,
|
||||||
|
E251,
|
||||||
|
E301,
|
||||||
|
E402,
|
||||||
|
E501,
|
||||||
|
E701,
|
||||||
|
E702,
|
||||||
|
E704,
|
||||||
|
E731,
|
||||||
|
E741,
|
||||||
|
F401,
|
||||||
|
F403,
|
||||||
|
F405,
|
||||||
|
F811,
|
||||||
|
W503,
|
||||||
|
W504,
|
||||||
|
E722
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -11,11 +11,12 @@ with open(os.path.join(os.path.dirname(__file__), "README.rst")) as f:
|
||||||
setup(
|
setup(
|
||||||
name="ptpython",
|
name="ptpython",
|
||||||
author="Jonathan Slenders",
|
author="Jonathan Slenders",
|
||||||
version="3.0.20",
|
version="3.0.21",
|
||||||
url="https://github.com/prompt-toolkit/ptpython",
|
url="https://github.com/prompt-toolkit/ptpython",
|
||||||
description="Python REPL build on top of prompt_toolkit",
|
description="Python REPL build on top of prompt_toolkit",
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
packages=find_packages("."),
|
packages=find_packages("."),
|
||||||
|
package_data={"ptpython": ["py.typed"]},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"appdirs",
|
"appdirs",
|
||||||
"importlib_metadata;python_version<'3.8'",
|
"importlib_metadata;python_version<'3.8'",
|
||||||
|
|
Loading…
Add table
Reference in a new issue