diff --git a/README.md b/README.md index fb5200b2..246038f3 100644 --- a/README.md +++ b/README.md @@ -160,17 +160,15 @@ Press [Tab] to autocomplete Auto suggestion is a way to propose some input completions to the user. Usually, the input is compared to the history and when there is another entry starting with the given text, the completion will be shown as gray text behind the current input. Pressing the right arrow → or ctrl-e will insert this suggestion, alt-f willinsert the first word of the suggestion. ```python + from quo.history import MemoryHistory from quo.prompt import Prompt - from quo.completion import AutoSuggestFromHistory - from quo.history import InMemoryHistory - history = InMemoryHistory() - history.append("import os") - history.append('print("hello")') - history.append('print("world")') - history.append("import path") + MemoryHistory.append("import os") + MemoryHistory.append('print("hello")') + MemoryHistory.append('print("world")') + MemoryHistory.append("import path") - session = Prompt(auto_suggest=AutoSuggestFromHistory(), history=history) + session = Prompt(history=MemoryHistory, suggest="history") while True: session.prompt('> ') @@ -240,7 +238,7 @@ A key binding is an association between a physical key on a keyboard and a param def _(event): echo("Hello, World!") - session.prompt(">>") + session.prompt(">> ") ``` Read more on [Key bindings](https://quo.readthedocs.io/en/latest/kb.html) diff --git a/docs/changes.rst b/docs/changes.rst index fa3a7dc8..0b363ad8 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -7,7 +7,18 @@ Changelog .. image:: https://media.giphy.com/media/12oufCB0MyZ1Go/giphy.gif -``Version 2022.4.5`` +``Version 2022.5`` +-------------------- + +Released on 2022-05-01 + +**Added** +^^^^^^^^^^ +- Added :meth:`quo.console.Console.spin` +- Added :param:`column_width` and :param:`headers` to :func:`quo.table.Table` +- Added :param:`suggest` to :class:`quo.prompt.Prompt` + +`Version 2022.4.5`` --------------------- Released on 2022-04-23 diff --git a/docs/console.rst b/docs/console.rst index 59101d09..13919eec 100644 --- a/docs/console.rst +++ b/docs/console.rst @@ -198,5 +198,22 @@ The :meth:`~quo.console.Console.bar` method will draw a horizontal bar with an o - ``color`` - controls if the pager supports ANSI colors or not. +``spin`` +---------- +This creates a context manager that is used to display a spinner on stdout as long as the context has not exited. +*Added on v2022.5* + +.. code:: python + + import time + + from quo.console import Console + + console = Console() + + with console.spin(): + time.sleep(3) + print("Hello, World") + » Check out more examples `here `_ diff --git a/docs/full_screen_apps.rst b/docs/full_screen_apps.rst index 95664d9d..851465d4 100644 --- a/docs/full_screen_apps.rst +++ b/docs/full_screen_apps.rst @@ -97,7 +97,7 @@ Print the layout to the output **Parameters** - ``container`` - AnyContaine - ``bind`` *(bool)* - When True, initiate a :class:`~quo.keys.Bind` instance for the key bindings. - - ``full_screen`` - When True, run the application on the alternate screen buffer. + - ``full_screen`` *(bool)* - When True, run the application on the alternate screen buffer. - ``focused_element`` - element to be focused initially. *(Can be anything the `focus` function accepts.)* - ``mouse_support`` - :class:`~quo.filters.Filter` or boolean. When True, enable mouse support. - ``style`` - A style string. diff --git a/docs/images/old-simple-progress-bar.png b/docs/images/oldpics/old-simple-progress-bar.png similarity index 100% rename from docs/images/old-simple-progress-bar.png rename to docs/images/oldpics/old-simple-progress-bar.png diff --git a/docs/images/oldmessagebox.png b/docs/images/oldpics/oldmessagebox.png similarity index 100% rename from docs/images/oldmessagebox.png rename to docs/images/oldpics/oldmessagebox.png diff --git a/docs/images/oldpromptbox.png b/docs/images/oldpics/oldpromptbox.png similarity index 100% rename from docs/images/oldpromptbox.png rename to docs/images/oldpics/oldpromptbox.png diff --git a/docs/images/oldpics/two_tasks.png b/docs/images/oldpics/two_tasks.png new file mode 100755 index 00000000..0bb3f751 Binary files /dev/null and b/docs/images/oldpics/two_tasks.png differ diff --git a/docs/images/two_tasks.png b/docs/images/two_tasks.png old mode 100755 new mode 100644 index 0bb3f751..0f8b7b2a Binary files a/docs/images/two_tasks.png and b/docs/images/two_tasks.png differ diff --git a/docs/prompt.rst b/docs/prompt.rst index e99df0b5..5f450c23 100644 --- a/docs/prompt.rst +++ b/docs/prompt.rst @@ -463,6 +463,8 @@ Auto suggestion is a way to propose some input completions to the user like the Usually, the input is compared to the history and when there is another entry starting with the given text, the completion will be shown as gray text behind the current input. Pressing the right arrow :kbd:`→` or :kbd:`ctrl-e` will insert this suggestion, :kbd:`alt-f` will insert the first word of the suggestion. +*Added :param:`suggest` on v2022.5* + .. note:: When suggestions are based on the history, don't forget to share one :class:`~quo.history.History` object between consecutive prompt calls. Using a :class:`~quo.prompt.Prompt` @@ -472,23 +474,20 @@ Example: .. code:: python from quo.prompt import Prompt - from quo.completion import AutoSuggestFromHistory - from quo.history import InMemoryHistory - - history = InMemoryHistory() - history.append("import os") - history.append('print("hello")') - history.append('print("world")') - history.append("import path") + from quo.history import MemoryHistory - session = Prompt(auto_suggest=AutoSuggestFromHistory(), history=history) + MemoryHistory.append("import os") + MemoryHistory.append('print("hello")') + MemoryHistory.append('print("world")') + MemoryHistory.append("import path") + session = Prompt(history=MemoryHistory, suggest="history") while True: text = session.prompt('> ') print(f"You said: {text}") .. image:: ./images/auto-suggestion.png -A suggestion does not have to come from the history. Any implementation of the :class:`~quo.completion.AutoSuggest` abstract base class can be passed as an argument. +A suggestion does not have to come from the history. Any implementation of the :class:`~quo.completion.AutoSuggest` abstract base class can be passed as a string i.e `history`, `dynamic` or `conditional` Autocompletion ^^^^^^^^^^^^^^^ diff --git a/docs/table.rst b/docs/table.rst index 78e948bb..c27a3803 100644 --- a/docs/table.rst +++ b/docs/table.rst @@ -8,6 +8,8 @@ Table **Parameters** - ``data`` - The first required argument. Can be a list-of-lists *(or another iterable of iterables)*, a list of named tuples, a dictionary of iterables, an iterable of dictionaries, a two-dimensional NumPy array, NumPy record array, or a Pandas' dataframe. + - ``align`` - :class:`.WindowAlign` value or callable that return an :class:`.WindowAlign` value. alignment of content. i.e ``left``, ``centre`` or ``right``. ``centre`` is the default value. + - ``style`` - A style string. - ``theme`` - **plain** - Separates columns with a double space. - **simple** - like Pandoc simple_tables. @@ -20,7 +22,6 @@ Table - **mediawiki** - Produces a table markup used in Wikipedia and on other MediaWiki-based sites. - **rst** - Like a simple table format from reStructuredText. - - ``style`` - A style string. Changed on *v2022.4.3* @@ -38,3 +39,33 @@ Changed on *v2022.4.3* Table(data) .. image:: https://raw.githubusercontent.com/scalabli/quo/master/docs/images/table.png + +``Table headers`` +------------------ + +To print nice column headers, supply the ``headers`` argument. + +- `headers` can be an explicit list of column headers. +- if `headers="firstrow"`, then the first row of data is used +- if `headers="keys"`, then dictionary keys or column indices are used otherwise a headerless table is produced. + +.. code:: python + + data = [ + ["sex","age"] + ["Alice","F",24] + ["Bob","M",19] + ] + + Table(data, headers="firstrow") + +``Column Widths and Line Wrapping`` +-------------------------------------- +:func:`Table` will, by default, set the width of each column to the length of the longest element in that column. However, in situations where fields are expected to reasonably be too long to look good as a single line, :param:`:param:`column_width` can help automate word wrapping long fields. + +.. code:: python + + data = [ + [1, 'John Smith', 'This is a rather long description that might look better if it is wrapped a bit'] + ] + Table(data, headers=("Issue Id", "Author", "Description"), column_width=[None, None, 30]) diff --git a/docs/utils.rst b/docs/utils.rst index 95653b56..3fa8f657 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -53,14 +53,14 @@ is instead a pipe. .. code:: python - from quo import echo, getchar + from quo import getchar gc = getchar() if gc == 'y': - echo('We will go on') + print('We will go on') elif gc == 'n': - echo('Abort!') + print('Abort!') Note that this reads raw input, which means that things like arrow keys diff --git a/examples/console/spin.py b/examples/console/spin.py new file mode 100644 index 00000000..e860f226 --- /dev/null +++ b/examples/console/spin.py @@ -0,0 +1,8 @@ +from quo.console import Console + +console = Console() + +with console.spin(): + import time + time.sleep(2) + print("Hello, world") diff --git a/examples/dialogs/inputbox.py b/examples/dialogs/inputbox.py index 52c34ab1..d992988e 100755 --- a/examples/dialogs/inputbox.py +++ b/examples/dialogs/inputbox.py @@ -7,9 +7,7 @@ result = InputBox( title="PromptBox shenanigans", - text="What Country are you from?:", - multiline=True, - bg=False) + text="What Country are you from?:",) print(f"Result = {result}") diff --git a/examples/dialogs/multiline-inputbox.py b/examples/dialogs/multiline-inputbox.py new file mode 100755 index 00000000..52c34ab1 --- /dev/null +++ b/examples/dialogs/multiline-inputbox.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +""" +Example of an input box dialog. +""" +from quo import print +from quo.dialog import InputBox + +result = InputBox( + title="PromptBox shenanigans", + text="What Country are you from?:", + multiline=True, + bg=False) + +print(f"Result = {result}") + diff --git a/examples/prompts/auto-suggestion.py b/examples/prompts/auto-suggestion.py index f5aad503..0a9a8bd6 100755 --- a/examples/prompts/auto-suggestion.py +++ b/examples/prompts/auto-suggestion.py @@ -8,18 +8,15 @@ suggestion. """ -from quo import echo -from quo.completion import AutoSuggestFromHistory -from quo.history import InMemoryHistory +from quo.history import MemoryHistory from quo.prompt import Prompt def main(): # Create some history first. (Easy for testing.) - history = InMemoryHistory() - history.append("import os") - history.append('print("hello")') - history.append('print("world")') - history.append("import path") + MemoryHistory.append("import os") + MemoryHistory.append('print("hello")') + MemoryHistory.append('print("world")') + MemoryHistory.append("import path") # Print help. print("This CLI has fish-style auto-suggestion enable.") @@ -29,10 +26,11 @@ def main(): print() session = Prompt( - history=history, - auto_suggest=AutoSuggestFromHistory(), - enable_history_search=True, - ) + history=MemoryHistory, + suggest="history" + ) + # auto_suggest=AutoSuggestFromHistory() + #) while True: try: @@ -42,7 +40,7 @@ def main(): else: break - echo(f"You said: {text}") + print(f"You said: {text}") if __name__ == "__main__": diff --git a/examples/table/colored-table.py b/examples/table/colored-table.py new file mode 100644 index 00000000..847d43d1 --- /dev/null +++ b/examples/table/colored-table.py @@ -0,0 +1,10 @@ +from quo.table import Table + +data = [ + ["Name", "Gender", "Age"], + ["Alice", "F", 24], + ["Bob", "M", 19], + ["Dave", "M", 24] + ] + +Table(data, style="green") diff --git a/examples/table/headers.py b/examples/table/headers.py new file mode 100644 index 00000000..da37b800 --- /dev/null +++ b/examples/table/headers.py @@ -0,0 +1,8 @@ +from quo.table import Table + +data = [ + [1, 'John Smith', 'This is a rather long description that might look better if it is wrapped a bit'] + ] + + +Table(data, headers=("Issue Id", "Author", "Description"), column_width=[None, None, 30]) diff --git a/examples/table/right-aligned.py b/examples/table/right-aligned.py new file mode 100644 index 00000000..1c707353 --- /dev/null +++ b/examples/table/right-aligned.py @@ -0,0 +1,10 @@ +from quo.table import Table + +data = [ + ["Name", "Gender", "Age"], + ["Alice", "F", 24], + ["Bob", "M", 19], + ["Dave", "M", 24] + ] + +Table(data, align="right") diff --git a/examples/table/table.py b/examples/table/table.py index d0be4e46..418ba94d 100644 --- a/examples/table/table.py +++ b/examples/table/table.py @@ -7,4 +7,4 @@ ["Dave", "M", 24] ] -Table(data, headers="secondrow") +Table(data) diff --git a/src/quo/__init__.py b/src/quo/__init__.py index 61bcfaa1..cbec6faa 100644 --- a/src/quo/__init__.py +++ b/src/quo/__init__.py @@ -112,4 +112,4 @@ def print(*values, style=None, sep=" ", end="\n"): from quo.shortcuts.utils import container -__version__ = "2022.4.6" +__version__ = "2022.5" diff --git a/src/quo/buffer.py b/src/quo/buffer.py index 28c3067f..54e1ff34 100644 --- a/src/quo/buffer.py +++ b/src/quo/buffer.py @@ -31,7 +31,7 @@ from quo.console.current import get_app from quo.console.run_in_terminal import run_in_terminal from quo.completion.auto_suggest import AutoSuggest, Suggestion -from quo.cache import FastDictCache +from quo.cache.core import FastDictCache from quo.clipboard import Data from quo.completion.core import ( CompleteEvent, diff --git a/src/quo/cache/__init__.py b/src/quo/cache/__init__.py index e973e21d..49bc3e34 100644 --- a/src/quo/cache/__init__.py +++ b/src/quo/cache/__init__.py @@ -1,2 +1,2 @@ -from .core import SimpleCache, FastDictCache, memoized -from ._lru_cache import LRUCache +#from .core import SimpleCache, FastDictCache, memoized +#from ._lru_cache import LRUCache diff --git a/src/quo/completion/__init__.py b/src/quo/completion/__init__.py index fd54fb0c..695cfcaf 100644 --- a/src/quo/completion/__init__.py +++ b/src/quo/completion/__init__.py @@ -3,7 +3,7 @@ # CompleteStyle, Completer, Completion, -# ConditionalCompleter, + ConditionalCompleter, # DummyCompleter, DynamicCompleter, ThreadedCompleter, diff --git a/src/quo/completion/auto_suggest.py b/src/quo/completion/auto_suggest.py index b7c1dd64..4a5c302d 100644 --- a/src/quo/completion/auto_suggest.py +++ b/src/quo/completion/auto_suggest.py @@ -147,7 +147,7 @@ def get_suggestion( class ConditionalAutoSuggest(AutoSuggest): """ - Auto suggest that can be turned on and of according to a certain condition. + Auto suggest that can be turned on and off according to a certain condition. """ def __init__(self, auto_suggest: AutoSuggest, filter: Union[bool, Filter]) -> None: diff --git a/src/quo/console/console.py b/src/quo/console/console.py index 6f35e49e..f6300529 100644 --- a/src/quo/console/console.py +++ b/src/quo/console/console.py @@ -44,7 +44,7 @@ from quo.buffer import Buffer from quo.i_o.termui import echo from quo import errors -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from quo.clipboard import Clipboard, InMemoryClipboard from quo.enums import EditingMode from quo.eventloop import ( @@ -911,6 +911,9 @@ def bar( return container( Window(FormattedTextControl(message), height=1, style=style, align=align) ) + def spin(self) -> "Console": + from quo.spin import Spinner + return Spinner() def run( diff --git a/src/quo/dialog.py b/src/quo/dialog.py index 5062ff49..2dc99872 100644 --- a/src/quo/dialog.py +++ b/src/quo/dialog.py @@ -119,9 +119,6 @@ def button_handler(v: _T) -> None: return _create_app(dialog, style) -from quo.widget.design import ok - - def _PromptBox( title: AnyFormattedText = "", text: AnyFormattedText = "", diff --git a/src/quo/filters/app.py b/src/quo/filters/app.py index 7b11cc0b..a51ef165 100644 --- a/src/quo/filters/app.py +++ b/src/quo/filters/app.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, cast from quo.console.current import get_app -from quo.cache import memoized +from quo.cache.core import memoized from quo.enums import EditingMode from .core import Condition diff --git a/src/quo/history.py b/src/quo/history.py index 11f4a47a..a8d6b3ad 100644 --- a/src/quo/history.py +++ b/src/quo/history.py @@ -301,3 +301,9 @@ def write(t: str) -> None: write("\n# %s\n" % datetime.datetime.now()) for line in string.split("\n"): write("+%s\n" % line) + + + + + +MemoryHistory = InMemoryHistory() diff --git a/src/quo/keys/key_binding/key_bindings.py b/src/quo/keys/key_binding/key_bindings.py index 7778587e..83ae6898 100644 --- a/src/quo/keys/key_binding/key_bindings.py +++ b/src/quo/keys/key_binding/key_bindings.py @@ -49,7 +49,7 @@ def my_key_binding(event): # cast, ) -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from quo.errors import UsageError from quo.filters import FilterOrBool, Never, to_filter from quo.keys.list import KEY_ALIASES, Keys diff --git a/src/quo/keys/key_binding/key_processor.py b/src/quo/keys/key_binding/key_processor.py index 0aa0de0a..078a352b 100644 --- a/src/quo/keys/key_binding/key_processor.py +++ b/src/quo/keys/key_binding/key_processor.py @@ -14,7 +14,7 @@ from quo.console.current import get_app from quo.enums import EditingMode from quo.filters.app import vi_navigation_mode -from quo.keys import Keys +from quo.keys.list import Keys from quo.utils.utils import Event from quo.keys.key_binding.key_bindings import Binding, KeyBindingsBase diff --git a/src/quo/layout/containers.py b/src/quo/layout/containers.py index 596ca81e..98d1ae6d 100644 --- a/src/quo/layout/containers.py +++ b/src/quo/layout/containers.py @@ -19,7 +19,7 @@ ) from quo.console.current import get_app -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from quo.filters import ( FilterOrBool, emacs_insert_mode, diff --git a/src/quo/layout/controls.py b/src/quo/layout/controls.py index f84a2809..2e663a79 100644 --- a/src/quo/layout/controls.py +++ b/src/quo/layout/controls.py @@ -17,7 +17,7 @@ from quo.console.current import get_app from quo.buffer import Buffer -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from quo.document import Document from quo.filters import FilterOrBool, to_filter from quo.text.core import ( diff --git a/src/quo/layout/dummy.py b/src/quo/layout/dummy.py index a18d2242..5a587f33 100644 --- a/src/quo/layout/dummy.py +++ b/src/quo/layout/dummy.py @@ -1,7 +1,7 @@ """ Dummy layout. Used when somebody creates a `Console Application` without specifying a `Layout`. """ -from quo.text import Text +from quo.text.html import Text from quo.keys import Bind from .containers import Window diff --git a/src/quo/layout/processors.py b/src/quo/layout/processors.py index ed66e16b..c36cc48e 100644 --- a/src/quo/layout/processors.py +++ b/src/quo/layout/processors.py @@ -20,7 +20,7 @@ ) from quo.console.current import get_app -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from quo.document import Document from quo.filters import FilterOrBool, to_filter, vi_insert_multiple_mode from quo.text.core import ( @@ -321,9 +321,13 @@ class PasswordProcessor(Processor): def __init__( self, - char: str = "\u2735" + char: str = None #u2735" ) -> None: - self.char = char + from quo.accordance import WIN + if WIN: + self.char = "*" + else: + self.char = "\u2735" #char def apply_transformation(self, ti: TransformationInput) -> Transformation: fragments: StyleAndTextTuples = cast( diff --git a/src/quo/layout/screen.py b/src/quo/layout/screen.py index b6d4ed32..f2288eaf 100644 --- a/src/quo/layout/screen.py +++ b/src/quo/layout/screen.py @@ -1,7 +1,7 @@ from collections import defaultdict from typing import TYPE_CHECKING, Callable, DefaultDict, Dict, List, NamedTuple, Optional, Tuple -from quo.cache import FastDictCache +from quo.cache.core import FastDictCache from quo.utils.utils import get_width if TYPE_CHECKING: diff --git a/src/quo/prompt.py b/src/quo/prompt.py index c8093c00..c9264bb4 100644 --- a/src/quo/prompt.py +++ b/src/quo/prompt.py @@ -297,6 +297,7 @@ def prompt_func(text): break if value == value2: return result + from quo.i_o.termui import echo echo(f"ERROR:", nl=False, fg="black", bg="red") @@ -382,6 +383,7 @@ class Prompt(Generic[_T]): :class:`~quo.filters.Filter`. Pressing 'v' in Vi mode or C-X C-E in emacs mode will open an external editor. :param history: :class:`~quo.history.History` instance. + :param suggest: `str` An instance of :class:`quo.completion.AutoSuggestFromHistory or the others. :param clipboard: :class:`~quo.clipboard.Clipboard` instance. (e.g. :class:`~quo.clipboard.InMemoryClipboard`) :param rprompt: Text or formatted text to be displayed on the right side. @@ -449,6 +451,10 @@ class Prompt(Generic[_T]): "tempfile", ) + from quo.completion.auto_suggest import AutoSuggestFromHistory + + from_history = AutoSuggestFromHistory() + from quo.keys import bind as _bind from quo.completion.core import CompleteStyle @@ -503,6 +509,7 @@ def __init__( tempfile_suffix: Optional[Union[str, Callable[[], str]]] = ".txt", tempfile: Optional[Union[str, Callable[[], str]]] = None, refresh_interval: float = 0, + suggest: str = None, input: Optional[Input] = None, output: Optional[Output] = None, ) -> None: @@ -521,6 +528,7 @@ def __init__( history = history or InMemoryHistory() clipboard = clipboard or InMemoryClipboard() + # Ensure backwards-compatibility, when `vi_mode` is passed. if vi_mode: editing_mode = EditingMode.VI @@ -591,6 +599,15 @@ def _continuation(width, line_number, wrap_count): return "." * width self.prompt_continuation = _continuation + # An instance of the auto sugfestuon classes + if suggest == "conditional": + from quo.completion.auto_suggest import ConditionalAutoSuggest + self.auto_suggest = ConditionalAutoSuggest() + if suggest == "dynamic": + self.auto_suggest = DynamicAutoSuggest() + elif suggest == "history": + from quo.completion.auto_suggest import AutoSuggestFromHistory + self.auto_suggest = AutoSuggestFromHistory() def _dyncond(self, attr_name: str) -> Condition: """ diff --git a/src/quo/style/defaults.py b/src/quo/style/defaults.py index d37b2555..f535b0ef 100644 --- a/src/quo/style/defaults.py +++ b/src/quo/style/defaults.py @@ -1,7 +1,7 @@ """ The default styling. """ -from quo.cache import memoized +from quo.cache.core import memoized from .core import ANSI_COLOR_NAMES, BaseStyle from .webcolors import NAMED_COLORS diff --git a/src/quo/style/style.py b/src/quo/style/style.py index 03a1cf10..aae94c29 100644 --- a/src/quo/style/style.py +++ b/src/quo/style/style.py @@ -7,7 +7,7 @@ from enum import Enum from typing import Dict, Hashable, List, Set, Tuple, TypeVar -from quo.cache import SimpleCache +from quo.cache.core import SimpleCache from .core import ( ANSI_COLOR_NAMES, diff --git a/src/quo/style/transformation.py b/src/quo/style/transformation.py index 1f6a49ba..9fd14e1e 100644 --- a/src/quo/style/transformation.py +++ b/src/quo/style/transformation.py @@ -13,7 +13,7 @@ from colorsys import hls_to_rgb, rgb_to_hls from typing import Callable, Hashable, Optional, Sequence, Tuple, Union -from quo.cache import memoized +from quo.cache.core import memoized from quo.filters import FilterOrBool, to_filter from quo.utils.utils import AnyFloat, to_float, to_str diff --git a/src/quo/table.py b/src/quo/table.py index 315a12e1..c9972519 100644 --- a/src/quo/table.py +++ b/src/quo/table.py @@ -7,7 +7,6 @@ import re import sys import textwrap -from typing import Optional from quo.expediency.vitals import inscribe as echo from collections import namedtuple @@ -1269,7 +1268,8 @@ def _Table( with the plain-text format of R and Pandas' dataframes. >>> echo(tabular([["sex","age"],["Alice","F",24],["Bob","M",19]], - ... headers="firstrow")) + ... headers="firstrow")) + >>> Tabl sex age ----- ----- ----- Alice F 24 @@ -1678,7 +1678,14 @@ def _Table( return _format_table(theme, headers, rows, minwidths, aligns, is_multiline) -def Table(data=None, align="center", style=None, theme="fancy_grid"): +def Table( + data=None, + align="center", + headers=(), + column_width=None, + style=None, + theme="fancy_grid" + ): """Format a fixed width table for pretty printing. The first required argument (`data`) can be a @@ -1688,7 +1695,18 @@ def Table(data=None, align="center", style=None, theme="fancy_grid"): from quo.layout.controls import FormattedTextControl from quo.shortcuts.utils import container - content = Window(FormattedTextControl(_Table(data, theme=theme), style=style), align=align) + content = Window( + FormattedTextControl( + _Table( + data, + headers=headers, + theme=theme, + maxcolwidths=column_width + ), + style=style + ), + align=align + ) return container(content) def _expand_numparse(disable_numparse, column_count): @@ -2049,105 +2067,5 @@ def _wrap_chunks(self, chunks): return lines -def _main(): - """\ - Usage: tabular [options] [FILE ...] - - Pretty-print tabular data - - FILE a filename of the file with tabular data; - if "-" or missing, read data from stdin. - - Options: - - -h, --help show this message - -1, --header use the first row of data as a table header - -o FILE, --output FILE print table to FILE (default: stdout) - -s REGEXP, --sep REGEXP use a custom column separator (default: whitespace) - -F FPFMT, --float FPFMT floating point number format (default: g) - -f FMT, --format FMT set output table format; supported formats: - plain, simple, grid, fancy_grid, pipe, orgtbl, - rst, mediawiki, html, latex, latex_raw, - latex_booktabs, latex_longtable, tsv - (default: simple) - """ - import getopt - import sys - import textwrap - - usage = textwrap.dedent(_main.__doc__) - try: - opts, args = getopt.getopt( - sys.argv[1:], - "h1o:s:F:A:f:", - ["help", "header", "output", "sep=", "float=", "align=", "format="], - ) - except getopt.GetoptError as e: - echo(e) - echo(usage) - sys.exit(2) - headers = [] - floatfmt = _DEFAULT_FLOATFMT - colalign = None - theme = "simple" - sep = r"\s+" - outfile = "-" - for opt, value in opts: - if opt in ["-1", "--header"]: - headers = "firstrow" - elif opt in ["-o", "--output"]: - outfile = value - elif opt in ["-F", "--float"]: - floatfmt = value - elif opt in ["-C", "--colalign"]: - colalign = value.split() - elif opt in ["-f", "--format"]: - if value not in tabular_formats: - echo("%s is not a supported table format" % value) - echo(usage) - sys.exit(3) - theme = value - elif opt in ["-s", "--sep"]: - sep = value - elif opt in ["-h", "--help"]: - echo(usage) - sys.exit(0) - files = [sys.stdin] if not args else args - with (sys.stdout if outfile == "-" else open(outfile, "w")) as out: - for f in files: - if f == "-": - f = sys.stdin - if _is_file(f): - _pprint_file( - f, - headers=headers, - theme=theme, - sep=sep, - floatfmt=floatfmt, - file=out, - colalign=colalign, - ) - else: - with open(f) as fobj: - _pprint_file( - fobj, - headers=headers, - theme=theme, - sep=sep, - floatfmt=floatfmt, - file=out, - colalign=colalign, - ) - - -def _pprint_file(fobject, headers, theme, sep, floatfmt, file, colalign): - rows = fobject.readlines() - table = [re.split(sep, r.rstrip()) for r in rows if r.strip()] - echo( - tabular(table, headers, theme, floatfmt=floatfmt, colalign=colalign), - file=file, - ) -if __name__ == "__main__": - _main()